feat(compiler): introduce schema for elements

Closes #3353
This commit is contained in:
Pawel Kozlowski
2015-07-29 14:01:18 +02:00
parent aae5a4cece
commit d894aa9101
12 changed files with 141 additions and 49 deletions

View File

@ -61,6 +61,8 @@ import {
DefaultDomCompiler,
APP_ID_RANDOM_BINDING
} from 'angular2/src/render/render';
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
import {
SharedStylesHost,
DomSharedStylesHost
@ -113,6 +115,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
bind(Renderer).toAlias(DomRenderer),
APP_ID_RANDOM_BINDING,
DefaultDomCompiler,
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
bind(RenderCompiler).toAlias(DefaultDomCompiler),
DomSharedStylesHost,
bind(SharedStylesHost).toAlias(DomSharedStylesHost),

View File

@ -17,6 +17,7 @@ import {
import {CompilePipeline} from './compile_pipeline';
import {ViewLoader, TemplateAndStyles} from 'angular2/src/render/dom/compiler/view_loader';
import {CompileStepFactory, DefaultStepFactory} from './compile_step_factory';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {Parser} from 'angular2/src/change_detection/change_detection';
import * as pvm from '../view/proto_view_merger';
import {DOCUMENT_TOKEN, APP_ID_TOKEN} from '../dom_tokens';
@ -30,7 +31,8 @@ import {prependAll} from '../util';
* the CompilePipeline and the CompileSteps.
*/
export class DomCompiler extends RenderCompiler {
constructor(private _stepFactory: CompileStepFactory, private _viewLoader: ViewLoader,
constructor(private _schemaRegistry: ElementSchemaRegistry,
private _stepFactory: CompileStepFactory, private _viewLoader: ViewLoader,
private _sharedStylesHost: SharedStylesHost) {
super();
}
@ -84,7 +86,8 @@ export class DomCompiler extends RenderCompiler {
this._sharedStylesHost.addStyles(compiledStyles);
}
return PromiseWrapper.resolve(compileElements[0].inheritedProtoView.build());
return PromiseWrapper.resolve(
compileElements[0].inheritedProtoView.build(this._schemaRegistry));
}
_normalizeViewEncapsulationIfThereAreNoStyles(viewDef: ViewDefinition): ViewDefinition {
@ -105,8 +108,8 @@ export class DomCompiler extends RenderCompiler {
@Injectable()
export class DefaultDomCompiler extends DomCompiler {
constructor(parser: Parser, viewLoader: ViewLoader, sharedStylesHost: SharedStylesHost,
@Inject(APP_ID_TOKEN) appId: any) {
super(new DefaultStepFactory(parser, appId), viewLoader, sharedStylesHost);
constructor(schemaRegistry: ElementSchemaRegistry, parser: Parser, viewLoader: ViewLoader,
sharedStylesHost: SharedStylesHost, @Inject(APP_ID_TOKEN) appId: any) {
super(schemaRegistry, new DefaultStepFactory(parser, appId), viewLoader, sharedStylesHost);
}
}

View File

@ -0,0 +1,23 @@
import {isPresent} from 'angular2/src/facade/lang';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {ElementSchemaRegistry} from './element_schema_registry';
export class DomElementSchemaRegistry extends ElementSchemaRegistry {
hasProperty(elm: any, propName: string): boolean {
var tagName = DOM.tagName(elm);
if (tagName.indexOf('-') !== -1) {
// can't tell now as we don't know which properties a custom element will get
// once it is instantiated
return true;
} else {
return DOM.hasProperty(elm, propName);
}
}
getMappedPropName(propName: string): string {
var mappedPropName = StringMapWrapper.get(DOM.attrToPropMap, propName);
return isPresent(mappedPropName) ? mappedPropName : propName;
}
}

View File

@ -0,0 +1,4 @@
export class ElementSchemaRegistry {
hasProperty(elm: any, propName: string): boolean { return true; }
getMappedPropName(propName: string): string { return propName; }
}

View File

@ -20,6 +20,7 @@ import {
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './proto_view';
import {DomElementBinder, Event, HostAction} from './element_binder';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import * as api from '../../api';
@ -68,7 +69,7 @@ export class ProtoViewBuilder {
setHostAttribute(name: string, value: string) { this.hostAttributes.set(name, value); }
build(): api.ProtoViewDto {
build(schemaRegistry: ElementSchemaRegistry): api.ProtoViewDto {
var domElementBinders = [];
var apiElementBinders = [];
@ -91,12 +92,12 @@ export class ProtoViewBuilder {
directiveIndex: dbb.directiveIndex,
propertyBindings: dbb.propertyBindings,
eventBindings: dbb.eventBindings,
hostPropertyBindings:
buildElementPropertyBindings(ebb.element, isPresent(ebb.componentId),
dbb.hostPropertyBindings, directiveTemplatePropertyNames)
hostPropertyBindings: buildElementPropertyBindings(schemaRegistry, ebb.element, true,
dbb.hostPropertyBindings, new Set())
});
});
var nestedProtoView = isPresent(ebb.nestedProtoView) ? ebb.nestedProtoView.build() : null;
var nestedProtoView =
isPresent(ebb.nestedProtoView) ? ebb.nestedProtoView.build(schemaRegistry) : null;
if (isPresent(nestedProtoView)) {
transitiveNgContentCount += nestedProtoView.transitiveNgContentCount;
}
@ -113,7 +114,7 @@ export class ProtoViewBuilder {
directives: apiDirectiveBinders,
nestedProtoView: nestedProtoView,
propertyBindings:
buildElementPropertyBindings(ebb.element, isPresent(ebb.componentId),
buildElementPropertyBindings(schemaRegistry, ebb.element, isPresent(ebb.componentId),
ebb.propertyBindings, directiveTemplatePropertyNames),
variableBindings: ebb.variableBindings,
eventBindings: ebb.eventBindings,
@ -325,14 +326,15 @@ const ATTRIBUTE_PREFIX = 'attr';
const CLASS_PREFIX = 'class';
const STYLE_PREFIX = 'style';
function buildElementPropertyBindings(protoElement: /*element*/ any, isNgComponent: boolean,
bindingsInTemplate: Map<string, ASTWithSource>,
directiveTempaltePropertyNames: Set<string>):
function buildElementPropertyBindings(
schemaRegistry: ElementSchemaRegistry, protoElement: /*element*/ any, isNgComponent: boolean,
bindingsInTemplate: Map<string, ASTWithSource>, directiveTempaltePropertyNames: Set<string>):
List<api.ElementPropertyBinding> {
var propertyBindings = [];
MapWrapper.forEach(bindingsInTemplate, (ast, propertyNameInTemplate) => {
var propertyBinding = createElementPropertyBinding(ast, propertyNameInTemplate);
if (isValidElementPropertyBinding(protoElement, isNgComponent, propertyBinding)) {
var propertyBinding = createElementPropertyBinding(schemaRegistry, ast, propertyNameInTemplate);
if (isValidElementPropertyBinding(schemaRegistry, protoElement, isNgComponent,
propertyBinding)) {
propertyBindings.push(propertyBinding);
} else if (!SetWrapper.has(directiveTempaltePropertyNames, propertyNameInTemplate)) {
throw new BaseException(
@ -342,29 +344,25 @@ function buildElementPropertyBindings(protoElement: /*element*/ any, isNgCompone
return propertyBindings;
}
function isValidElementPropertyBinding(protoElement: /*element*/ any, isNgComponent: boolean,
function isValidElementPropertyBinding(schemaRegistry: ElementSchemaRegistry,
protoElement: /*element*/ any, isNgComponent: boolean,
binding: api.ElementPropertyBinding): boolean {
if (binding.type === api.PropertyBindingType.PROPERTY) {
var tagName = DOM.tagName(protoElement);
var possibleCustomElement = tagName.indexOf('-') !== -1;
if (possibleCustomElement && !isNgComponent) {
// can't tell now as we don't know which properties a custom element will get
// once it is instantiated
return true;
if (!isNgComponent) {
return schemaRegistry.hasProperty(protoElement, binding.property);
} else {
// TODO(pk): change this logic as soon as we can properly detect custom elements
return DOM.hasProperty(protoElement, binding.property);
}
}
return true;
}
function createElementPropertyBinding(ast: ASTWithSource, propertyNameInTemplate: string):
api.ElementPropertyBinding {
function createElementPropertyBinding(schemaRegistry: ElementSchemaRegistry, ast: ASTWithSource,
propertyNameInTemplate: string): api.ElementPropertyBinding {
var parts = StringWrapper.split(propertyNameInTemplate, PROPERTY_PARTS_SEPARATOR);
if (parts.length === 1) {
var propName = parts[0];
var mappedPropName = StringMapWrapper.get(DOM.attrToPropMap, propName);
propName = isPresent(mappedPropName) ? mappedPropName : propName;
var propName = schemaRegistry.getMappedPropName(parts[0]);
return new api.ElementPropertyBinding(api.PropertyBindingType.PROPERTY, ast, propName);
} else if (parts[0] == ATTRIBUTE_PREFIX) {
return new api.ElementPropertyBinding(api.PropertyBindingType.ATTRIBUTE, ast, parts[1]);

View File

@ -56,6 +56,8 @@ import {
SharedStylesHost,
DomSharedStylesHost
} from 'angular2/src/render/render';
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
import {Serializer} from "angular2/src/web-workers/shared/serializer";
import {Log} from './utils';
@ -98,6 +100,7 @@ function _getAppBindings() {
bind(APP_ID_TOKEN).toValue('a'),
DefaultDomCompiler,
bind(RenderCompiler).toAlias(DefaultDomCompiler),
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
DomSharedStylesHost,
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),

View File

@ -11,6 +11,8 @@ import 'package:angular2/src/render/dom/compiler/compile_pipeline.dart';
import 'package:angular2/src/render/dom/compiler/style_inliner.dart';
import 'package:angular2/src/render/dom/compiler/style_url_resolver.dart';
import 'package:angular2/src/render/dom/compiler/view_loader.dart';
import 'package:angular2/src/render/dom/schema/element_schema_registry.dart';
import 'package:angular2/src/render/dom/schema/dom_element_schema_registry.dart';
import 'package:angular2/src/render/xhr.dart' show XHR;
import 'package:angular2/src/reflection/reflection.dart';
import 'package:angular2/src/services/url_resolver.dart';
@ -33,7 +35,7 @@ Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
{bool generateRegistrations: true,
bool generateChangeDetectors: true}) async {
var viewDefResults = await createViewDefinitions(reader, entryPoint);
var extractor = new _TemplateExtractor(new XhrImpl(reader, entryPoint));
var extractor = new _TemplateExtractor(new DomElementSchemaRegistry(), new XhrImpl(reader, entryPoint));
var registrations = new reg.Codegen();
var changeDetectorClasses = new change.Codegen();
@ -83,14 +85,16 @@ Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
class _TemplateExtractor {
final CompileStepFactory _factory;
ViewLoader _loader;
ElementSchemaRegistry _schemaRegistry;
_TemplateExtractor(XHR xhr)
_TemplateExtractor(ElementSchemaRegistry schemaRegistry, XHR xhr)
: _factory = new CompileStepFactory(new ng.Parser(new ng.Lexer())) {
var urlResolver = new UrlResolver();
var styleUrlResolver = new StyleUrlResolver(urlResolver);
var styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
_loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
_schemaRegistry = schemaRegistry;
}
Future<_ExtractResult> extractTemplates(ViewDefinition viewDef) async {
@ -110,7 +114,7 @@ class _TemplateExtractor {
var compileElements =
pipeline.processElements(DOM.createTemplate(templateAndStyles.template), ViewType.COMPONENT, viewDef);
var protoViewDto = compileElements[0].inheritedProtoView.build();
var protoViewDto = compileElements[0].inheritedProtoView.build(_schemaRegistry);
reflector.reflectionCapabilities = savedReflectionCapabilities;