feat(core): add ability to reflect DOM properties as attributes
By binding the token `DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES` provided by the dom_renderer module to `true` in the root injector (i.e. bootstrap()), all elements whose properties are set by angular will be reflected as attributes with the prefix "ng-reflect-". Fixes #2910
This commit is contained in:
parent
66ec4d1f5c
commit
903ff9047f
@ -64,4 +64,8 @@ export {
|
|||||||
RenderViewRef,
|
RenderViewRef,
|
||||||
RenderProtoViewRef
|
RenderProtoViewRef
|
||||||
} from 'angular2/src/render/api';
|
} from 'angular2/src/render/api';
|
||||||
export {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
export {
|
||||||
|
DomRenderer,
|
||||||
|
DOCUMENT_TOKEN,
|
||||||
|
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
|
||||||
|
} from 'angular2/src/render/dom/dom_renderer';
|
||||||
|
@ -56,7 +56,11 @@ import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils
|
|||||||
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
|
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
|
||||||
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||||
import {Renderer, RenderCompiler} from 'angular2/src/render/api';
|
import {Renderer, RenderCompiler} from 'angular2/src/render/api';
|
||||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
import {
|
||||||
|
DomRenderer,
|
||||||
|
DOCUMENT_TOKEN,
|
||||||
|
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
|
||||||
|
} from 'angular2/src/render/dom/dom_renderer';
|
||||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||||
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
||||||
|
|
||||||
@ -77,6 +81,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
|||||||
return [
|
return [
|
||||||
bind(DOCUMENT_TOKEN)
|
bind(DOCUMENT_TOKEN)
|
||||||
.toValue(DOM.defaultDoc()),
|
.toValue(DOM.defaultDoc()),
|
||||||
|
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),
|
||||||
bind(appComponentTypeToken).toValue(appComponentType),
|
bind(appComponentTypeToken).toValue(appComponentType),
|
||||||
bind(appComponentRefPromiseToken)
|
bind(appComponentRefPromiseToken)
|
||||||
.toFactory(
|
.toFactory(
|
||||||
|
@ -18,19 +18,26 @@ import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './view
|
|||||||
import {DomView, DomViewRef, resolveInternalDomView} from './view/view';
|
import {DomView, DomViewRef, resolveInternalDomView} from './view/view';
|
||||||
import {DomElement} from './view/element';
|
import {DomElement} from './view/element';
|
||||||
import {DomViewContainer} from './view/view_container';
|
import {DomViewContainer} from './view/view_container';
|
||||||
import {NG_BINDING_CLASS_SELECTOR, NG_BINDING_CLASS} from './util';
|
import {NG_BINDING_CLASS_SELECTOR, NG_BINDING_CLASS, camelCaseToDashCase} from './util';
|
||||||
|
|
||||||
import {Renderer, RenderProtoViewRef, RenderViewRef, RenderElementRef} from '../api';
|
import {Renderer, RenderProtoViewRef, RenderViewRef, RenderElementRef} from '../api';
|
||||||
|
|
||||||
export const DOCUMENT_TOKEN = CONST_EXPR(new OpaqueToken('DocumentToken'));
|
export const DOCUMENT_TOKEN = CONST_EXPR(new OpaqueToken('DocumentToken'));
|
||||||
|
export const DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES =
|
||||||
|
CONST_EXPR(new OpaqueToken('DomReflectPropertiesAsAttributes'));
|
||||||
|
const REFLECT_PREFIX = 'ng-reflect-';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DomRenderer extends Renderer {
|
export class DomRenderer extends Renderer {
|
||||||
_document;
|
_document;
|
||||||
|
_reflectPropertiesAsAttributes: boolean;
|
||||||
|
|
||||||
constructor(public _eventManager: EventManager, public _shadowDomStrategy: ShadowDomStrategy,
|
constructor(public _eventManager: EventManager, public _shadowDomStrategy: ShadowDomStrategy,
|
||||||
@Inject(DOCUMENT_TOKEN) document) {
|
@Inject(DOCUMENT_TOKEN) document,
|
||||||
|
@Inject(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES) reflectPropertiesAsAttributes:
|
||||||
|
boolean) {
|
||||||
super();
|
super();
|
||||||
|
this._reflectPropertiesAsAttributes = reflectPropertiesAsAttributes;
|
||||||
this._document = document;
|
this._document = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,6 +193,11 @@ export class DomRenderer extends Renderer {
|
|||||||
setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any): void {
|
setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any): void {
|
||||||
var view = resolveInternalDomView(location.renderView);
|
var view = resolveInternalDomView(location.renderView);
|
||||||
view.setElementProperty(location.boundElementIndex, propertyName, propertyValue);
|
view.setElementProperty(location.boundElementIndex, propertyName, propertyValue);
|
||||||
|
// Reflect the property value as an attribute value with ng-reflect- prefix.
|
||||||
|
if (this._reflectPropertiesAsAttributes) {
|
||||||
|
this.setElementAttribute(location, `${REFLECT_PREFIX}${camelCaseToDashCase(propertyName)}`,
|
||||||
|
propertyValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string):
|
setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string):
|
||||||
|
@ -50,7 +50,11 @@ import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils
|
|||||||
import {ELEMENT_PROBE_CONFIG} from 'angular2/debug';
|
import {ELEMENT_PROBE_CONFIG} from 'angular2/debug';
|
||||||
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||||
import {RenderCompiler, Renderer} from 'angular2/src/render/api';
|
import {RenderCompiler, Renderer} from 'angular2/src/render/api';
|
||||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
import {
|
||||||
|
DomRenderer,
|
||||||
|
DOCUMENT_TOKEN,
|
||||||
|
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
|
||||||
|
} from 'angular2/src/render/dom/dom_renderer';
|
||||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,6 +94,7 @@ function _getAppBindings() {
|
|||||||
.toFactory((doc) => new EmulatedUnscopedShadowDomStrategy(doc.head), [DOCUMENT_TOKEN]),
|
.toFactory((doc) => new EmulatedUnscopedShadowDomStrategy(doc.head), [DOCUMENT_TOKEN]),
|
||||||
DomRenderer,
|
DomRenderer,
|
||||||
DefaultDomCompiler,
|
DefaultDomCompiler,
|
||||||
|
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),
|
||||||
bind(Renderer).toAlias(DomRenderer),
|
bind(Renderer).toAlias(DomRenderer),
|
||||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||||
ProtoViewFactory,
|
ProtoViewFactory,
|
||||||
|
@ -19,6 +19,8 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
|||||||
import {DomTestbed, TestView, elRef} from './dom_testbed';
|
import {DomTestbed, TestView, elRef} from './dom_testbed';
|
||||||
|
|
||||||
import {ViewDefinition, DirectiveMetadata, RenderViewRef} from 'angular2/src/render/api';
|
import {ViewDefinition, DirectiveMetadata, RenderViewRef} from 'angular2/src/render/api';
|
||||||
|
import {DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES} from 'angular2/src/render/dom/dom_renderer';
|
||||||
|
import {bind} from 'angular2/di';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('DomRenderer integration', () => {
|
describe('DomRenderer integration', () => {
|
||||||
@ -126,6 +128,50 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should NOT reflect property values as attributes if flag is NOT set',
|
||||||
|
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||||
|
tb.compileAll([
|
||||||
|
someComponent,
|
||||||
|
new ViewDefinition(
|
||||||
|
{componentId: 'someComponent', template: '<input [title]="y">', directives: []})
|
||||||
|
])
|
||||||
|
.then((protoViewDtos) => {
|
||||||
|
var rootView = tb.createRootView(protoViewDtos[0]);
|
||||||
|
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
|
||||||
|
var el = DOM.childNodes(tb.rootEl)[0];
|
||||||
|
tb.renderer.setElementProperty(elRef(cmpView.viewRef, 0), 'maxLength', '20');
|
||||||
|
expect(DOM.getAttribute(<HTMLInputElement>el, 'ng-reflect-max-length'))
|
||||||
|
.toEqual(null);
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('reflection', () => {
|
||||||
|
beforeEachBindings(() => [bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(true)]);
|
||||||
|
it('should reflect property values as attributes if flag is set',
|
||||||
|
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||||
|
tb.compileAll([
|
||||||
|
someComponent,
|
||||||
|
new ViewDefinition({
|
||||||
|
componentId: 'someComponent',
|
||||||
|
template: '<input [title]="y">',
|
||||||
|
directives: []
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.then((protoViewDtos) => {
|
||||||
|
var rootView = tb.createRootView(protoViewDtos[0]);
|
||||||
|
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
|
||||||
|
var el = DOM.childNodes(tb.rootEl)[0];
|
||||||
|
tb.renderer.setElementProperty(elRef(cmpView.viewRef, 0), 'maxLength', '20');
|
||||||
|
expect(DOM.getAttribute(<HTMLInputElement>el, 'ng-reflect-max-length'))
|
||||||
|
.toEqual('20');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
if (DOM.supportsDOMEvents()) {
|
if (DOM.supportsDOMEvents()) {
|
||||||
it('should call actions on the element independent of the compilation',
|
it('should call actions on the element independent of the compilation',
|
||||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user