X Tutup
Skip to content

Commit d894aa9

Browse files
feat(compiler): introduce schema for elements
Closes #3353
1 parent aae5a4c commit d894aa9

File tree

12 files changed

+141
-49
lines changed

12 files changed

+141
-49
lines changed

modules/angular2/src/core/application_common.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ import {
6161
DefaultDomCompiler,
6262
APP_ID_RANDOM_BINDING
6363
} from 'angular2/src/render/render';
64+
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
65+
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
6466
import {
6567
SharedStylesHost,
6668
DomSharedStylesHost
@@ -113,6 +115,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
113115
bind(Renderer).toAlias(DomRenderer),
114116
APP_ID_RANDOM_BINDING,
115117
DefaultDomCompiler,
118+
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
116119
bind(RenderCompiler).toAlias(DefaultDomCompiler),
117120
DomSharedStylesHost,
118121
bind(SharedStylesHost).toAlias(DomSharedStylesHost),

modules/angular2/src/render/dom/compiler/compiler.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
import {CompilePipeline} from './compile_pipeline';
1818
import {ViewLoader, TemplateAndStyles} from 'angular2/src/render/dom/compiler/view_loader';
1919
import {CompileStepFactory, DefaultStepFactory} from './compile_step_factory';
20+
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
2021
import {Parser} from 'angular2/src/change_detection/change_detection';
2122
import * as pvm from '../view/proto_view_merger';
2223
import {DOCUMENT_TOKEN, APP_ID_TOKEN} from '../dom_tokens';
@@ -30,7 +31,8 @@ import {prependAll} from '../util';
3031
* the CompilePipeline and the CompileSteps.
3132
*/
3233
export class DomCompiler extends RenderCompiler {
33-
constructor(private _stepFactory: CompileStepFactory, private _viewLoader: ViewLoader,
34+
constructor(private _schemaRegistry: ElementSchemaRegistry,
35+
private _stepFactory: CompileStepFactory, private _viewLoader: ViewLoader,
3436
private _sharedStylesHost: SharedStylesHost) {
3537
super();
3638
}
@@ -84,7 +86,8 @@ export class DomCompiler extends RenderCompiler {
8486
this._sharedStylesHost.addStyles(compiledStyles);
8587
}
8688

87-
return PromiseWrapper.resolve(compileElements[0].inheritedProtoView.build());
89+
return PromiseWrapper.resolve(
90+
compileElements[0].inheritedProtoView.build(this._schemaRegistry));
8891
}
8992

9093
_normalizeViewEncapsulationIfThereAreNoStyles(viewDef: ViewDefinition): ViewDefinition {
@@ -105,8 +108,8 @@ export class DomCompiler extends RenderCompiler {
105108

106109
@Injectable()
107110
export class DefaultDomCompiler extends DomCompiler {
108-
constructor(parser: Parser, viewLoader: ViewLoader, sharedStylesHost: SharedStylesHost,
109-
@Inject(APP_ID_TOKEN) appId: any) {
110-
super(new DefaultStepFactory(parser, appId), viewLoader, sharedStylesHost);
111+
constructor(schemaRegistry: ElementSchemaRegistry, parser: Parser, viewLoader: ViewLoader,
112+
sharedStylesHost: SharedStylesHost, @Inject(APP_ID_TOKEN) appId: any) {
113+
super(schemaRegistry, new DefaultStepFactory(parser, appId), viewLoader, sharedStylesHost);
111114
}
112115
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {isPresent} from 'angular2/src/facade/lang';
2+
import {StringMapWrapper} from 'angular2/src/facade/collection';
3+
import {DOM} from 'angular2/src/dom/dom_adapter';
4+
5+
import {ElementSchemaRegistry} from './element_schema_registry';
6+
7+
export class DomElementSchemaRegistry extends ElementSchemaRegistry {
8+
hasProperty(elm: any, propName: string): boolean {
9+
var tagName = DOM.tagName(elm);
10+
if (tagName.indexOf('-') !== -1) {
11+
// can't tell now as we don't know which properties a custom element will get
12+
// once it is instantiated
13+
return true;
14+
} else {
15+
return DOM.hasProperty(elm, propName);
16+
}
17+
}
18+
19+
getMappedPropName(propName: string): string {
20+
var mappedPropName = StringMapWrapper.get(DOM.attrToPropMap, propName);
21+
return isPresent(mappedPropName) ? mappedPropName : propName;
22+
}
23+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export class ElementSchemaRegistry {
2+
hasProperty(elm: any, propName: string): boolean { return true; }
3+
getMappedPropName(propName: string): string { return propName; }
4+
}

modules/angular2/src/render/dom/view/proto_view_builder.ts

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020

2121
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './proto_view';
2222
import {DomElementBinder, Event, HostAction} from './element_binder';
23+
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
2324

2425
import * as api from '../../api';
2526

@@ -68,7 +69,7 @@ export class ProtoViewBuilder {
6869

6970
setHostAttribute(name: string, value: string) { this.hostAttributes.set(name, value); }
7071

71-
build(): api.ProtoViewDto {
72+
build(schemaRegistry: ElementSchemaRegistry): api.ProtoViewDto {
7273
var domElementBinders = [];
7374

7475
var apiElementBinders = [];
@@ -91,12 +92,12 @@ export class ProtoViewBuilder {
9192
directiveIndex: dbb.directiveIndex,
9293
propertyBindings: dbb.propertyBindings,
9394
eventBindings: dbb.eventBindings,
94-
hostPropertyBindings:
95-
buildElementPropertyBindings(ebb.element, isPresent(ebb.componentId),
96-
dbb.hostPropertyBindings, directiveTemplatePropertyNames)
95+
hostPropertyBindings: buildElementPropertyBindings(schemaRegistry, ebb.element, true,
96+
dbb.hostPropertyBindings, new Set())
9797
});
9898
});
99-
var nestedProtoView = isPresent(ebb.nestedProtoView) ? ebb.nestedProtoView.build() : null;
99+
var nestedProtoView =
100+
isPresent(ebb.nestedProtoView) ? ebb.nestedProtoView.build(schemaRegistry) : null;
100101
if (isPresent(nestedProtoView)) {
101102
transitiveNgContentCount += nestedProtoView.transitiveNgContentCount;
102103
}
@@ -113,7 +114,7 @@ export class ProtoViewBuilder {
113114
directives: apiDirectiveBinders,
114115
nestedProtoView: nestedProtoView,
115116
propertyBindings:
116-
buildElementPropertyBindings(ebb.element, isPresent(ebb.componentId),
117+
buildElementPropertyBindings(schemaRegistry, ebb.element, isPresent(ebb.componentId),
117118
ebb.propertyBindings, directiveTemplatePropertyNames),
118119
variableBindings: ebb.variableBindings,
119120
eventBindings: ebb.eventBindings,
@@ -325,14 +326,15 @@ const ATTRIBUTE_PREFIX = 'attr';
325326
const CLASS_PREFIX = 'class';
326327
const STYLE_PREFIX = 'style';
327328

328-
function buildElementPropertyBindings(protoElement: /*element*/ any, isNgComponent: boolean,
329-
bindingsInTemplate: Map<string, ASTWithSource>,
330-
directiveTempaltePropertyNames: Set<string>):
329+
function buildElementPropertyBindings(
330+
schemaRegistry: ElementSchemaRegistry, protoElement: /*element*/ any, isNgComponent: boolean,
331+
bindingsInTemplate: Map<string, ASTWithSource>, directiveTempaltePropertyNames: Set<string>):
331332
List<api.ElementPropertyBinding> {
332333
var propertyBindings = [];
333334
MapWrapper.forEach(bindingsInTemplate, (ast, propertyNameInTemplate) => {
334-
var propertyBinding = createElementPropertyBinding(ast, propertyNameInTemplate);
335-
if (isValidElementPropertyBinding(protoElement, isNgComponent, propertyBinding)) {
335+
var propertyBinding = createElementPropertyBinding(schemaRegistry, ast, propertyNameInTemplate);
336+
if (isValidElementPropertyBinding(schemaRegistry, protoElement, isNgComponent,
337+
propertyBinding)) {
336338
propertyBindings.push(propertyBinding);
337339
} else if (!SetWrapper.has(directiveTempaltePropertyNames, propertyNameInTemplate)) {
338340
throw new BaseException(
@@ -342,29 +344,25 @@ function buildElementPropertyBindings(protoElement: /*element*/ any, isNgCompone
342344
return propertyBindings;
343345
}
344346

345-
function isValidElementPropertyBinding(protoElement: /*element*/ any, isNgComponent: boolean,
347+
function isValidElementPropertyBinding(schemaRegistry: ElementSchemaRegistry,
348+
protoElement: /*element*/ any, isNgComponent: boolean,
346349
binding: api.ElementPropertyBinding): boolean {
347350
if (binding.type === api.PropertyBindingType.PROPERTY) {
348-
var tagName = DOM.tagName(protoElement);
349-
var possibleCustomElement = tagName.indexOf('-') !== -1;
350-
if (possibleCustomElement && !isNgComponent) {
351-
// can't tell now as we don't know which properties a custom element will get
352-
// once it is instantiated
353-
return true;
351+
if (!isNgComponent) {
352+
return schemaRegistry.hasProperty(protoElement, binding.property);
354353
} else {
354+
// TODO(pk): change this logic as soon as we can properly detect custom elements
355355
return DOM.hasProperty(protoElement, binding.property);
356356
}
357357
}
358358
return true;
359359
}
360360

361-
function createElementPropertyBinding(ast: ASTWithSource, propertyNameInTemplate: string):
362-
api.ElementPropertyBinding {
361+
function createElementPropertyBinding(schemaRegistry: ElementSchemaRegistry, ast: ASTWithSource,
362+
propertyNameInTemplate: string): api.ElementPropertyBinding {
363363
var parts = StringWrapper.split(propertyNameInTemplate, PROPERTY_PARTS_SEPARATOR);
364364
if (parts.length === 1) {
365-
var propName = parts[0];
366-
var mappedPropName = StringMapWrapper.get(DOM.attrToPropMap, propName);
367-
propName = isPresent(mappedPropName) ? mappedPropName : propName;
365+
var propName = schemaRegistry.getMappedPropName(parts[0]);
368366
return new api.ElementPropertyBinding(api.PropertyBindingType.PROPERTY, ast, propName);
369367
} else if (parts[0] == ATTRIBUTE_PREFIX) {
370368
return new api.ElementPropertyBinding(api.PropertyBindingType.ATTRIBUTE, ast, parts[1]);

modules/angular2/src/test_lib/test_injector.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ import {
5656
SharedStylesHost,
5757
DomSharedStylesHost
5858
} from 'angular2/src/render/render';
59+
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
60+
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
5961
import {Serializer} from "angular2/src/web-workers/shared/serializer";
6062
import {Log} from './utils';
6163

@@ -98,6 +100,7 @@ function _getAppBindings() {
98100
bind(APP_ID_TOKEN).toValue('a'),
99101
DefaultDomCompiler,
100102
bind(RenderCompiler).toAlias(DefaultDomCompiler),
103+
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
101104
DomSharedStylesHost,
102105
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
103106
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),

modules/angular2/src/transform/template_compiler/generator.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import 'package:angular2/src/render/dom/compiler/compile_pipeline.dart';
1111
import 'package:angular2/src/render/dom/compiler/style_inliner.dart';
1212
import 'package:angular2/src/render/dom/compiler/style_url_resolver.dart';
1313
import 'package:angular2/src/render/dom/compiler/view_loader.dart';
14+
import 'package:angular2/src/render/dom/schema/element_schema_registry.dart';
15+
import 'package:angular2/src/render/dom/schema/dom_element_schema_registry.dart';
1416
import 'package:angular2/src/render/xhr.dart' show XHR;
1517
import 'package:angular2/src/reflection/reflection.dart';
1618
import 'package:angular2/src/services/url_resolver.dart';
@@ -33,7 +35,7 @@ Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
3335
{bool generateRegistrations: true,
3436
bool generateChangeDetectors: true}) async {
3537
var viewDefResults = await createViewDefinitions(reader, entryPoint);
36-
var extractor = new _TemplateExtractor(new XhrImpl(reader, entryPoint));
38+
var extractor = new _TemplateExtractor(new DomElementSchemaRegistry(), new XhrImpl(reader, entryPoint));
3739

3840
var registrations = new reg.Codegen();
3941
var changeDetectorClasses = new change.Codegen();
@@ -83,14 +85,16 @@ Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
8385
class _TemplateExtractor {
8486
final CompileStepFactory _factory;
8587
ViewLoader _loader;
88+
ElementSchemaRegistry _schemaRegistry;
8689

87-
_TemplateExtractor(XHR xhr)
90+
_TemplateExtractor(ElementSchemaRegistry schemaRegistry, XHR xhr)
8891
: _factory = new CompileStepFactory(new ng.Parser(new ng.Lexer())) {
8992
var urlResolver = new UrlResolver();
9093
var styleUrlResolver = new StyleUrlResolver(urlResolver);
9194
var styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
9295

9396
_loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
97+
_schemaRegistry = schemaRegistry;
9498
}
9599

96100
Future<_ExtractResult> extractTemplates(ViewDefinition viewDef) async {
@@ -110,7 +114,7 @@ class _TemplateExtractor {
110114

111115
var compileElements =
112116
pipeline.processElements(DOM.createTemplate(templateAndStyles.template), ViewType.COMPONENT, viewDef);
113-
var protoViewDto = compileElements[0].inheritedProtoView.build();
117+
var protoViewDto = compileElements[0].inheritedProtoView.build(_schemaRegistry);
114118

115119
reflector.reflectionCapabilities = savedReflectionCapabilities;
116120

modules/angular2/test/render/dom/compiler/compiler_common_tests.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
} from 'angular2/src/render/api';
2727
import {CompileStep} from 'angular2/src/render/dom/compiler/compile_step';
2828
import {CompileStepFactory} from 'angular2/src/render/dom/compiler/compile_step_factory';
29+
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
2930
import {ViewLoader, TemplateAndStyles} from 'angular2/src/render/dom/compiler/view_loader';
3031

3132
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
@@ -48,7 +49,8 @@ export function runCompilerCommonTests() {
4849
var tplLoader = new FakeViewLoader(urlData);
4950
mockStepFactory =
5051
new MockStepFactory([new MockStep(processElementClosure, processStyleClosure)]);
51-
return new DomCompiler(mockStepFactory, tplLoader, sharedStylesHost);
52+
return new DomCompiler(new ElementSchemaRegistry(), mockStepFactory, tplLoader,
53+
sharedStylesHost);
5254
}
5355

5456
describe('compile', () => {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import {
2+
beforeEach,
3+
ddescribe,
4+
xdescribe,
5+
describe,
6+
expect,
7+
iit,
8+
inject,
9+
it,
10+
xit,
11+
IS_DARTIUM
12+
} from 'angular2/test_lib';
13+
14+
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
15+
import {DOM} from 'angular2/src/dom/dom_adapter';
16+
17+
export function main() {
18+
// DOMElementSchema can only be used on the JS side where we can safely
19+
// use reflection for DOM elements
20+
if (IS_DARTIUM) return;
21+
22+
var registry;
23+
24+
beforeEach(() => { registry = new DomElementSchemaRegistry(); });
25+
26+
describe('DOMElementSchema', () => {
27+
28+
it('should detect properties on regular elements', () => {
29+
var divEl = DOM.createElement('div');
30+
expect(registry.hasProperty(divEl, 'id')).toBeTruthy();
31+
expect(registry.hasProperty(divEl, 'title')).toBeTruthy();
32+
expect(registry.hasProperty(divEl, 'unknown')).toBeFalsy();
33+
});
34+
35+
it('should return true for custom-like elements', () => {
36+
var customLikeEl = DOM.createElement('custom-like');
37+
expect(registry.hasProperty(customLikeEl, 'unknown')).toBeTruthy();
38+
});
39+
40+
it('should not re-map property names that are not specified in DOM facade',
41+
() => { expect(registry.getMappedPropName('readonly')).toEqual('readOnly'); });
42+
43+
it('should not re-map property names that are not specified in DOM facade', () => {
44+
expect(registry.getMappedPropName('title')).toEqual('title');
45+
expect(registry.getMappedPropName('exotic-unknown')).toEqual('exotic-unknown');
46+
});
47+
});
48+
}

0 commit comments

Comments
 (0)
X Tutup