feat(view): add support for components that use shadow dom emulation

This commit is contained in:
vsavkin 2014-12-23 10:45:20 -08:00
parent df4ac0dd33
commit da9d041f90
27 changed files with 188 additions and 89 deletions

View File

@ -1,7 +1,7 @@
import {DOM, document} from 'facade/dom'; import {DOM, document} from 'facade/dom';
import {isBlank, Type} from 'facade/lang'; import {isBlank, Type} from 'facade/lang';
import {MapWrapper} from 'facade/collection'; import {MapWrapper} from 'facade/collection';
import {AnnotatedType} from 'core/compiler/annotated_type'; import {DirectiveMetadata} from 'core/compiler/directive_metadata';
import {Parser} from 'change_detection/parser/parser'; import {Parser} from 'change_detection/parser/parser';
import {Lexer} from 'change_detection/parser/lexer'; import {Lexer} from 'change_detection/parser/lexer';
@ -81,7 +81,7 @@ export function main() {
var reader = new DirectiveMetadataReader(); var reader = new DirectiveMetadataReader();
var cache = new CompilerCache(); var cache = new CompilerCache();
var compiler = new Compiler(null, reader, new Parser(new Lexer()), cache); var compiler = new Compiler(null, reader, new Parser(new Lexer()), cache);
var annotatedComponent = reader.annotatedType(BenchmarkComponent); var annotatedComponent = reader.read(BenchmarkComponent);
var templateNoBindings = loadTemplate('templateNoBindings', COUNT); var templateNoBindings = loadTemplate('templateNoBindings', COUNT);
var templateWithBindings = loadTemplate('templateWithBindings', COUNT); var templateWithBindings = loadTemplate('templateWithBindings', COUNT);

View File

@ -1,6 +1,7 @@
import {ABSTRACT, CONST, normalizeBlank} from 'facade/lang'; import {ABSTRACT, CONST, normalizeBlank} from 'facade/lang';
import {List} from 'facade/collection'; import {List} from 'facade/collection';
import {TemplateConfig} from './template_config'; import {TemplateConfig} from './template_config';
import {ShadowDomStrategy} from '../compiler/shadow_dom';
@ABSTRACT() @ABSTRACT()
@ -35,6 +36,7 @@ export class Component extends Directive {
lightDomServices:any; //List; lightDomServices:any; //List;
shadowDomServices:any; //List; shadowDomServices:any; //List;
componentServices:any; //List; componentServices:any; //List;
shadowDom:any; //ShadowDomStrategy;
@CONST() @CONST()
constructor({ constructor({
@ -44,7 +46,8 @@ export class Component extends Directive {
lightDomServices, lightDomServices,
shadowDomServices, shadowDomServices,
componentServices, componentServices,
implementsTypes implementsTypes,
shadowDom
}:{ }:{
selector:String, selector:String,
bind:Object, bind:Object,
@ -52,7 +55,8 @@ export class Component extends Directive {
lightDomServices:List, lightDomServices:List,
shadowDomServices:List, shadowDomServices:List,
componentServices:List, componentServices:List,
implementsTypes:List implementsTypes:List,
shadowDom:ShadowDomStrategy
}={}) }={})
{ {
super({ super({
@ -65,6 +69,7 @@ export class Component extends Directive {
this.lightDomServices = lightDomServices; this.lightDomServices = lightDomServices;
this.shadowDomServices = shadowDomServices; this.shadowDomServices = shadowDomServices;
this.componentServices = componentServices; this.componentServices = componentServices;
this.shadowDom = shadowDom;
} }
} }

View File

@ -10,7 +10,7 @@ import {ChangeDetector} from 'change_detection/change_detector';
import {RecordRange} from 'change_detection/record_range'; import {RecordRange} from 'change_detection/record_range';
import {TemplateLoader} from './compiler/template_loader'; import {TemplateLoader} from './compiler/template_loader';
import {DirectiveMetadataReader} from './compiler/directive_metadata_reader'; import {DirectiveMetadataReader} from './compiler/directive_metadata_reader';
import {AnnotatedType} from './compiler/annotated_type'; import {DirectiveMetadata} from './compiler/directive_metadata';
import {List, ListWrapper} from 'facade/collection'; import {List, ListWrapper} from 'facade/collection';
import {PromiseWrapper} from 'facade/async'; import {PromiseWrapper} from 'facade/async';
import {VmTurnZone} from 'core/zone/vm_turn_zone'; import {VmTurnZone} from 'core/zone/vm_turn_zone';
@ -36,7 +36,7 @@ export function documentDependentBindings(appComponentType) {
// TODO(rado): inspect annotation here and warn if there are bindings, // TODO(rado): inspect annotation here and warn if there are bindings,
// lightDomServices, and other component annotations that are skipped // lightDomServices, and other component annotations that are skipped
// for bootstrapping components. // for bootstrapping components.
return reader.annotatedType(appComponentType); return reader.read(appComponentType);
}, [DirectiveMetadataReader]), }, [DirectiveMetadataReader]),
bind(appElementToken).toFactory((appComponentAnnotatedType, appDocument) => { bind(appElementToken).toFactory((appComponentAnnotatedType, appDocument) => {

View File

@ -11,7 +11,7 @@ import {CompilePipeline} from './pipeline/compile_pipeline';
import {CompileElement} from './pipeline/compile_element'; import {CompileElement} from './pipeline/compile_element';
import {createDefaultSteps} from './pipeline/default_steps'; import {createDefaultSteps} from './pipeline/default_steps';
import {TemplateLoader} from './template_loader'; import {TemplateLoader} from './template_loader';
import {AnnotatedType} from './annotated_type'; import {DirectiveMetadata} from './directive_metadata';
import {Component} from '../annotations/annotations'; import {Component} from '../annotations/annotations';
/** /**
@ -59,12 +59,12 @@ export class Compiler {
this._compilerCache = cache; this._compilerCache = cache;
} }
createSteps(component:AnnotatedType):List<CompileStep> { createSteps(component:DirectiveMetadata):List<CompileStep> {
var annotation: Component = component.annotation; var annotation: Component = component.annotation;
var directives = annotation.template.directives; var directives = annotation.template.directives;
var annotatedDirectives = ListWrapper.create(); var annotatedDirectives = ListWrapper.create();
for (var i=0; i<directives.length; i++) { for (var i=0; i<directives.length; i++) {
ListWrapper.push(annotatedDirectives, this._reader.annotatedType(directives[i])); ListWrapper.push(annotatedDirectives, this._reader.read(directives[i]));
} }
return createDefaultSteps(this._parser, component, annotatedDirectives); return createDefaultSteps(this._parser, component, annotatedDirectives);
} }
@ -75,12 +75,12 @@ export class Compiler {
// transitively via the _templateLoader and store them in templateCache // transitively via the _templateLoader and store them in templateCache
return PromiseWrapper.resolve(this.compileAllLoaded( return PromiseWrapper.resolve(this.compileAllLoaded(
templateCache, this._reader.annotatedType(component), templateRoot) templateCache, this._reader.read(component), templateRoot)
); );
} }
// public so that we can compile in sync in performance tests. // public so that we can compile in sync in performance tests.
compileAllLoaded(templateCache, component:AnnotatedType, templateRoot:Element = null):ProtoView { compileAllLoaded(templateCache, component:DirectiveMetadata, templateRoot:Element = null):ProtoView {
var rootProtoView = this._compilerCache.get(component.type); var rootProtoView = this._compilerCache.get(component.type);
if (isPresent(rootProtoView)) { if (isPresent(rootProtoView)) {
return rootProtoView; return rootProtoView;

View File

@ -1,14 +1,18 @@
import {Type, FIELD} from 'facade/lang'; import {Type, FIELD} from 'facade/lang';
import {Directive} from '../annotations/annotations' import {Directive} from '../annotations/annotations'
import {ShadowDomStrategy} from './shadow_dom';
/** /**
* Combination of a type with the Directive annotation * Combination of a type with the Directive annotation
*/ */
export class AnnotatedType { export class DirectiveMetadata {
type:Type; type:Type;
annotation:Directive; annotation:Directive;
constructor(type:Type, annotation:Directive) { shadowDomStrategy:ShadowDomStrategy;
constructor(type:Type, annotation:Directive, shadowDomStrategy:ShadowDomStrategy) {
this.annotation = annotation; this.annotation = annotation;
this.type = type; this.type = type;
this.shadowDomStrategy = shadowDomStrategy;
} }
} }

View File

@ -1,27 +1,29 @@
import {Type, isPresent, BaseException, stringify} from 'facade/lang'; import {Type, isPresent, BaseException, stringify} from 'facade/lang';
import {Directive} from '../annotations/annotations'; import {Directive, Component} from '../annotations/annotations';
import {AnnotatedType} from './annotated_type'; import {DirectiveMetadata} from './directive_metadata';
import {reflector} from 'reflection/reflection'; import {reflector} from 'reflection/reflection';
import {ShadowDom, ShadowDomStrategy, ShadowDomNative} from './shadow_dom';
/**
* Interface representing a way of extracting [Directive] annotations from
* [Type]. This interface has three native implementations:
*
* 1) JavaScript native implementation
* 2) Dart reflective implementation
* 3) Dart transformer generated implementation
*/
export class DirectiveMetadataReader { export class DirectiveMetadataReader {
annotatedType(type:Type):AnnotatedType { read(type:Type):DirectiveMetadata {
var annotations = reflector.annotations(type); var annotations = reflector.annotations(type);
if (isPresent(annotations)) { if (isPresent(annotations)) {
for (var i=0; i<annotations.length; i++) { for (var i=0; i<annotations.length; i++) {
var annotation = annotations[i]; var annotation = annotations[i];
if (annotation instanceof Component) {
return new DirectiveMetadata(type, annotation, this.parseShadowDomStrategy(annotation));
}
if (annotation instanceof Directive) { if (annotation instanceof Directive) {
return new AnnotatedType(type, annotation); return new DirectiveMetadata(type, annotation, null);
} }
} }
} }
throw new BaseException(`No Directive annotation found on ${stringify(type)}`); throw new BaseException(`No Directive annotation found on ${stringify(type)}`);
} }
parseShadowDomStrategy(annotation:Component):ShadowDomStrategy{
return isPresent(annotation.shadowDom) ? annotation.shadowDom : ShadowDomNative;
}
} }

View File

@ -1,20 +1,20 @@
import {ProtoElementInjector} from './element_injector'; import {ProtoElementInjector} from './element_injector';
import {FIELD} from 'facade/lang'; import {FIELD} from 'facade/lang';
import {MapWrapper} from 'facade/collection'; import {MapWrapper} from 'facade/collection';
import {AnnotatedType} from './annotated_type'; import {DirectiveMetadata} from './directive_metadata';
import {List, Map} from 'facade/collection'; import {List, Map} from 'facade/collection';
import {ProtoView} from './view'; import {ProtoView} from './view';
export class ElementBinder { export class ElementBinder {
protoElementInjector:ProtoElementInjector; protoElementInjector:ProtoElementInjector;
componentDirective:AnnotatedType; componentDirective:DirectiveMetadata;
templateDirective:AnnotatedType; templateDirective:DirectiveMetadata;
textNodeIndices:List<int>; textNodeIndices:List<int>;
hasElementPropertyBindings:boolean; hasElementPropertyBindings:boolean;
nestedProtoView: ProtoView; nestedProtoView: ProtoView;
events:Map; events:Map;
constructor( constructor(
protoElementInjector: ProtoElementInjector, componentDirective:AnnotatedType, templateDirective:AnnotatedType) { protoElementInjector: ProtoElementInjector, componentDirective:DirectiveMetadata, templateDirective:DirectiveMetadata) {
this.protoElementInjector = protoElementInjector; this.protoElementInjector = protoElementInjector;
this.componentDirective = componentDirective; this.componentDirective = componentDirective;
this.templateDirective = templateDirective; this.templateDirective = templateDirective;

View File

@ -1,7 +1,7 @@
import {List, Map, ListWrapper, MapWrapper} from 'facade/collection'; import {List, Map, ListWrapper, MapWrapper} from 'facade/collection';
import {Element, DOM} from 'facade/dom'; import {Element, DOM} from 'facade/dom';
import {int, isBlank, isPresent} from 'facade/lang'; import {int, isBlank, isPresent} from 'facade/lang';
import {AnnotatedType} from '../annotated_type'; import {DirectiveMetadata} from '../directive_metadata';
import {Decorator} from '../../annotations/annotations'; import {Decorator} from '../../annotations/annotations';
import {Component} from '../../annotations/annotations'; import {Component} from '../../annotations/annotations';
import {Template} from '../../annotations/annotations'; import {Template} from '../../annotations/annotations';
@ -24,9 +24,9 @@ export class CompileElement {
propertyBindings:Map; propertyBindings:Map;
eventBindings:Map; eventBindings:Map;
variableBindings:Map; variableBindings:Map;
decoratorDirectives:List<AnnotatedType>; decoratorDirectives:List<DirectiveMetadata>;
templateDirective:AnnotatedType; templateDirective:DirectiveMetadata;
componentDirective:AnnotatedType; componentDirective:DirectiveMetadata;
isViewRoot:boolean; isViewRoot:boolean;
hasBindings:boolean; hasBindings:boolean;
inheritedProtoView:ProtoView; inheritedProtoView:ProtoView;
@ -110,7 +110,7 @@ export class CompileElement {
MapWrapper.set(this.eventBindings, eventName, expression); MapWrapper.set(this.eventBindings, eventName, expression);
} }
addDirective(directive:AnnotatedType) { addDirective(directive:DirectiveMetadata) {
var annotation = directive.annotation; var annotation = directive.annotation;
if (annotation instanceof Decorator) { if (annotation instanceof Decorator) {
if (isBlank(this.decoratorDirectives)) { if (isBlank(this.decoratorDirectives)) {

View File

@ -4,7 +4,7 @@ import {Element, Node, DOM} from 'facade/dom';
import {CompileElement} from './compile_element'; import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control'; import {CompileControl} from './compile_control';
import {CompileStep} from './compile_step'; import {CompileStep} from './compile_step';
import {AnnotatedType} from '../annotated_type'; import {DirectiveMetadata} from '../directive_metadata';
/** /**
* CompilePipeline for executing CompileSteps recursively for * CompilePipeline for executing CompileSteps recursively for

View File

@ -1,6 +1,6 @@
import {CompileElement} from './compile_element'; import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control'; import {CompileControl} from './compile_control';
import {AnnotatedType} from '../annotated_type'; import {DirectiveMetadata} from '../directive_metadata';
/** /**
* One part of the compile process. * One part of the compile process.

View File

@ -9,7 +9,7 @@ import {ElementBindingMarker} from './element_binding_marker';
import {ProtoViewBuilder} from './proto_view_builder'; import {ProtoViewBuilder} from './proto_view_builder';
import {ProtoElementInjectorBuilder} from './proto_element_injector_builder'; import {ProtoElementInjectorBuilder} from './proto_element_injector_builder';
import {ElementBinderBuilder} from './element_binder_builder'; import {ElementBinderBuilder} from './element_binder_builder';
import {AnnotatedType} from 'core/compiler/annotated_type'; import {DirectiveMetadata} from 'core/compiler/directive_metadata';
import {stringify} from 'facade/lang'; import {stringify} from 'facade/lang';
/** /**
@ -17,8 +17,8 @@ import {stringify} from 'facade/lang';
* Takes in an HTMLElement and produces the ProtoViews, * Takes in an HTMLElement and produces the ProtoViews,
* ProtoElementInjectors and ElementBinders in the end. * ProtoElementInjectors and ElementBinders in the end.
*/ */
export function createDefaultSteps(parser:Parser, compiledComponent: AnnotatedType, export function createDefaultSteps(parser:Parser, compiledComponent: DirectiveMetadata,
directives: List<AnnotatedType>) { directives: List<DirectiveMetadata>) {
var compilationUnit = stringify(compiledComponent.type); var compilationUnit = stringify(compiledComponent.type);
return [ return [

View File

@ -4,7 +4,7 @@ import {TemplateElement} from 'facade/dom';
import {SelectorMatcher} from '../selector'; import {SelectorMatcher} from '../selector';
import {CssSelector} from '../selector'; import {CssSelector} from '../selector';
import {AnnotatedType} from '../annotated_type'; import {DirectiveMetadata} from '../directive_metadata';
import {Template} from '../../annotations/annotations'; import {Template} from '../../annotations/annotations';
import {Component} from '../../annotations/annotations'; import {Component} from '../../annotations/annotations';
import {CompileStep} from './compile_step'; import {CompileStep} from './compile_step';
@ -28,13 +28,13 @@ import {CompileControl} from './compile_control';
*/ */
export class DirectiveParser extends CompileStep { export class DirectiveParser extends CompileStep {
_selectorMatcher:SelectorMatcher; _selectorMatcher:SelectorMatcher;
constructor(directives:List<AnnotatedType>) { constructor(directives:List<DirectiveMetadata>) {
this._selectorMatcher = new SelectorMatcher(); this._selectorMatcher = new SelectorMatcher();
for (var i=0; i<directives.length; i++) { for (var i=0; i<directives.length; i++) {
var annotatedType = directives[i]; var directiveMetadata = directives[i];
this._selectorMatcher.addSelectable( this._selectorMatcher.addSelectable(
CssSelector.parse(annotatedType.annotation.selector), CssSelector.parse(directiveMetadata.annotation.selector),
annotatedType directiveMetadata
); );
} }
} }

View File

@ -8,7 +8,7 @@ import {Parser} from 'change_detection/parser/parser';
import {ProtoRecordRange} from 'change_detection/record_range'; import {ProtoRecordRange} from 'change_detection/record_range';
import {Component, Directive} from '../../annotations/annotations'; import {Component, Directive} from '../../annotations/annotations';
import {AnnotatedType} from '../annotated_type'; import {DirectiveMetadata} from '../directive_metadata';
import {ProtoView, ElementPropertyMemento, DirectivePropertyMemento} from '../view'; import {ProtoView, ElementPropertyMemento, DirectivePropertyMemento} from '../view';
import {ProtoElementInjector} from '../element_injector'; import {ProtoElementInjector} from '../element_injector';
import {ElementBinder} from '../element_binder'; import {ElementBinder} from '../element_binder';

View File

@ -0,0 +1,9 @@
library angular.core.compiler.shadow_dom;
//TODO: merge this file with shadow_dom.es6 when the traspiler support creating const globals
import './shadow_dom_strategy.dart';
export './shadow_dom_strategy.dart';
const ShadowDomEmulated = const EmulatedShadowDomStrategy();
const ShadowDomNative = const NativeShadowDomStrategy();

View File

@ -0,0 +1,5 @@
import {EmulatedShadowDomStrategy, NativeShadowDomStrategy} from './shadow_dom_strategy';
export * from './shadow_dom_strategy';
export var ShadowDomEmulated = new EmulatedShadowDomStrategy();
export var ShadowDomNative = new NativeShadowDomStrategy();

View File

@ -0,0 +1,30 @@
import {CONST} from 'facade/lang';
import {DOM} from 'facade/dom';
import {Element} from 'facade/dom';
import {View} from './view';
export class ShadowDomStrategy {
@CONST() constructor() {}
attachTemplate(el:Element, view:View){}
}
export class EmulatedShadowDomStrategy extends ShadowDomStrategy {
@CONST() constructor() {}
attachTemplate(el:Element, view:View){
DOM.clearNodes(el);
moveViewNodesIntoParent(el, view);
}
}
export class NativeShadowDomStrategy extends ShadowDomStrategy {
@CONST() constructor() {}
attachTemplate(el:Element, view:View){
moveViewNodesIntoParent(el.createShadowRoot(), view);
}
}
function moveViewNodesIntoParent(parent, view) {
for (var i = 0; i < view.nodes.length; ++i) {
DOM.appendChild(parent, view.nodes[i]);
}
}

View File

@ -6,7 +6,7 @@ import {AST} from 'change_detection/parser/ast';
import {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector'; import {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector';
import {ElementBinder} from './element_binder'; import {ElementBinder} from './element_binder';
import {AnnotatedType} from './annotated_type'; import {DirectiveMetadata} from './directive_metadata';
import {SetterFn} from 'reflection/types'; import {SetterFn} from 'reflection/types';
import {FIELD, IMPLEMENTS, int, isPresent, isBlank, BaseException} from 'facade/lang'; import {FIELD, IMPLEMENTS, int, isPresent, isBlank, BaseException} from 'facade/lang';
import {Injector} from 'di/di'; import {Injector} from 'di/di';
@ -350,7 +350,8 @@ export class ProtoView {
if (isPresent(binder.componentDirective)) { if (isPresent(binder.componentDirective)) {
var childView = binder.nestedProtoView.instantiate(elementInjector); var childView = binder.nestedProtoView.instantiate(elementInjector);
view.recordRange.addRange(childView.recordRange); view.recordRange.addRange(childView.recordRange);
ViewPort.moveViewNodesIntoParent(element.createShadowRoot(), childView);
binder.componentDirective.shadowDomStrategy.attachTemplate(element, childView);
ListWrapper.push(componentChildViews, childView); ListWrapper.push(componentChildViews, childView);
} }
} }
@ -367,7 +368,7 @@ export class ProtoView {
} }
bindElement(protoElementInjector:ProtoElementInjector, bindElement(protoElementInjector:ProtoElementInjector,
componentDirective:AnnotatedType = null, templateDirective:AnnotatedType = null):ElementBinder { componentDirective:DirectiveMetadata = null, templateDirective:DirectiveMetadata = null):ElementBinder {
var elBinder = new ElementBinder(protoElementInjector, componentDirective, templateDirective); var elBinder = new ElementBinder(protoElementInjector, componentDirective, templateDirective);
ListWrapper.push(this.elementBinders, elBinder); ListWrapper.push(this.elementBinders, elBinder);
return elBinder; return elBinder;
@ -434,7 +435,7 @@ export class ProtoView {
// and the component template is already compiled into protoView. // and the component template is already compiled into protoView.
// Used for bootstrapping. // Used for bootstrapping.
static createRootProtoView(protoView: ProtoView, static createRootProtoView(protoView: ProtoView,
insertionElement, rootComponentAnnotatedType: AnnotatedType): ProtoView { insertionElement, rootComponentAnnotatedType: DirectiveMetadata): ProtoView {
DOM.addClass(insertionElement, 'ng-binding'); DOM.addClass(insertionElement, 'ng-binding');
var rootProtoView = new ProtoView(insertionElement, new ProtoRecordRange()); var rootProtoView = new ProtoView(insertionElement, new ProtoRecordRange());
rootProtoView.instantiateInPlace = true; rootProtoView.instantiateInPlace = true;

View File

@ -105,12 +105,6 @@ export class ViewPort {
} }
} }
static moveViewNodesIntoParent(parent, view) {
for (var i = 0; i < view.nodes.length; ++i) {
DOM.appendChild(parent, view.nodes[i]);
}
}
static moveViewNodesAfterSibling(sibling, view) { static moveViewNodesAfterSibling(sibling, view) {
for (var i = view.nodes.length - 1; i >= 0; --i) { for (var i = view.nodes.length - 1; i >= 0; --i) {
DOM.insertAfter(sibling, view.nodes[i]); DOM.insertAfter(sibling, view.nodes[i]);

View File

@ -66,7 +66,7 @@ export function main() {
current.inheritedProtoView = new ProtoView(current.element, null); current.inheritedProtoView = new ProtoView(current.element, null);
current.inheritedElementBinder = current.inheritedProtoView.bindElement(null); current.inheritedElementBinder = current.inheritedProtoView.bindElement(null);
if (current.element === mainEl) { if (current.element === mainEl) {
current.componentDirective = reader.annotatedType(NestedComponent); current.componentDirective = reader.read(NestedComponent);
} }
}); });
compiler.compile(MainComponent, mainEl).then( (protoView) => { compiler.compile(MainComponent, mainEl).then( (protoView) => {
@ -97,7 +97,7 @@ export function main() {
var compiler = createCompiler( (parent, current, control) => { var compiler = createCompiler( (parent, current, control) => {
current.inheritedProtoView = new ProtoView(current.element, null); current.inheritedProtoView = new ProtoView(current.element, null);
current.inheritedElementBinder = current.inheritedProtoView.bindElement(null); current.inheritedElementBinder = current.inheritedProtoView.bindElement(null);
current.componentDirective = reader.annotatedType(RecursiveComponent); current.componentDirective = reader.read(RecursiveComponent);
}); });
compiler.compile(RecursiveComponent, null).then( (protoView) => { compiler.compile(RecursiveComponent, null).then( (protoView) => {
expect(protoView.elementBinders[0].nestedProtoView).toBe(protoView); expect(protoView.elementBinders[0].nestedProtoView).toBe(protoView);

View File

@ -1,7 +1,8 @@
import {ddescribe, describe, it, iit, expect, beforeEach} from 'test_lib/test_lib'; import {ddescribe, describe, it, iit, expect, beforeEach} from 'test_lib/test_lib';
import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader'; import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader';
import {Decorator} from 'core/annotations/annotations'; import {Decorator, Component} from 'core/annotations/annotations';
import {AnnotatedType} from 'core/compiler/annotated_type'; import {DirectiveMetadata} from 'core/compiler/directive_metadata';
import {ShadowDomEmulated, ShadowDomNative} from 'core/compiler/shadow_dom';
@Decorator({ @Decorator({
selector: 'someSelector' selector: 'someSelector'
@ -9,28 +10,50 @@ import {AnnotatedType} from 'core/compiler/annotated_type';
class SomeDirective { class SomeDirective {
} }
@Component({
selector: 'someSelector'
})
class ComponentWithoutExplicitShadowDomStrategy {}
@Component({
selector: 'someSelector',
shadowDom: ShadowDomEmulated
})
class ComponentWithExplicitShadowDomStrategy {}
class SomeDirectiveWithoutAnnotation { class SomeDirectiveWithoutAnnotation {
} }
export function main() { export function main() {
describe("DirectiveMetadataReader", () => { describe("DirectiveMetadataReader", () => {
var rader; var reader;
beforeEach( () => { beforeEach( () => {
rader = new DirectiveMetadataReader(); reader = new DirectiveMetadataReader();
}); });
it('should read out the annotation', () => { it('should read out the annotation', () => {
var annoatedDirective = rader.annotatedType(SomeDirective); var directiveMetadata = reader.read(SomeDirective);
expect(annoatedDirective).toEqual( expect(directiveMetadata).toEqual(
new AnnotatedType(SomeDirective, new Decorator({selector: 'someSelector'}))); new DirectiveMetadata(SomeDirective, new Decorator({selector: 'someSelector'}), null));
}); });
it('should throw if not matching annotation is found', () => { it('should throw if not matching annotation is found', () => {
expect(() => { expect(() => {
rader.annotatedType(SomeDirectiveWithoutAnnotation); reader.read(SomeDirectiveWithoutAnnotation);
}).toThrowError('No Directive annotation found on SomeDirectiveWithoutAnnotation'); }).toThrowError('No Directive annotation found on SomeDirectiveWithoutAnnotation');
}); });
describe("shadow dom strategy", () => {
it('should return the provided shadow dom strategy when it is present', () => {
var directiveMetadata = reader.read(ComponentWithExplicitShadowDomStrategy);
expect(directiveMetadata.shadowDomStrategy).toEqual(ShadowDomEmulated);
});
it('should return Native otherwise', () => {
var directiveMetadata = reader.read(ComponentWithoutExplicitShadowDomStrategy);
expect(directiveMetadata.shadowDomStrategy).toEqual(ShadowDomNative);
});
});
}); });
} }

View File

@ -28,7 +28,7 @@ export function main() {
var parser = new Parser(new Lexer()); var parser = new Parser(new Lexer());
var annotatedDirectives = ListWrapper.create(); var annotatedDirectives = ListWrapper.create();
for (var i=0; i<directives.length; i++) { for (var i=0; i<directives.length; i++) {
ListWrapper.push(annotatedDirectives, reader.annotatedType(directives[i])); ListWrapper.push(annotatedDirectives, reader.read(directives[i]));
} }
return new CompilePipeline([new MockStep((parent, current, control) => { return new CompilePipeline([new MockStep((parent, current, control) => {
@ -53,7 +53,7 @@ export function main() {
describe('component directives', () => { describe('component directives', () => {
it('should detect them in attributes', () => { it('should detect them in attributes', () => {
var results = createPipeline().process(createElement('<div some-comp></div>')); var results = createPipeline().process(createElement('<div some-comp></div>'));
expect(results[0].componentDirective).toEqual(reader.annotatedType(SomeComponent)); expect(results[0].componentDirective).toEqual(reader.read(SomeComponent));
}); });
it('should detect them in property bindings', () => { it('should detect them in property bindings', () => {
@ -61,7 +61,7 @@ export function main() {
'some-comp': 'someExpr' 'some-comp': 'someExpr'
}}); }});
var results = pipeline.process(createElement('<div></div>')); var results = pipeline.process(createElement('<div></div>'));
expect(results[0].componentDirective).toEqual(reader.annotatedType(SomeComponent)); expect(results[0].componentDirective).toEqual(reader.read(SomeComponent));
}); });
it('should detect them in variable bindings', () => { it('should detect them in variable bindings', () => {
@ -69,7 +69,7 @@ export function main() {
'some-comp': 'someExpr' 'some-comp': 'someExpr'
}}); }});
var results = pipeline.process(createElement('<div></div>')); var results = pipeline.process(createElement('<div></div>'));
expect(results[0].componentDirective).toEqual(reader.annotatedType(SomeComponent)); expect(results[0].componentDirective).toEqual(reader.read(SomeComponent));
}); });
it('should not allow multiple component directives on the same element', () => { it('should not allow multiple component directives on the same element', () => {
@ -92,7 +92,7 @@ export function main() {
describe('template directives', () => { describe('template directives', () => {
it('should detect them in attributes', () => { it('should detect them in attributes', () => {
var results = createPipeline().process(createElement('<template some-templ></template>')); var results = createPipeline().process(createElement('<template some-templ></template>'));
expect(results[0].templateDirective).toEqual(reader.annotatedType(SomeTemplate)); expect(results[0].templateDirective).toEqual(reader.read(SomeTemplate));
}); });
it('should detect them in property bindings', () => { it('should detect them in property bindings', () => {
@ -100,7 +100,7 @@ export function main() {
'some-templ': 'someExpr' 'some-templ': 'someExpr'
}}); }});
var results = pipeline.process(createElement('<template></template>')); var results = pipeline.process(createElement('<template></template>'));
expect(results[0].templateDirective).toEqual(reader.annotatedType(SomeTemplate)); expect(results[0].templateDirective).toEqual(reader.read(SomeTemplate));
}); });
it('should detect them in variable bindings', () => { it('should detect them in variable bindings', () => {
@ -108,7 +108,7 @@ export function main() {
'some-templ': 'someExpr' 'some-templ': 'someExpr'
}}); }});
var results = pipeline.process(createElement('<template></template>')); var results = pipeline.process(createElement('<template></template>'));
expect(results[0].templateDirective).toEqual(reader.annotatedType(SomeTemplate)); expect(results[0].templateDirective).toEqual(reader.read(SomeTemplate));
}); });
it('should not allow multiple template directives on the same element', () => { it('should not allow multiple template directives on the same element', () => {
@ -131,7 +131,7 @@ export function main() {
describe('decorator directives', () => { describe('decorator directives', () => {
it('should detect them in attributes', () => { it('should detect them in attributes', () => {
var results = createPipeline().process(createElement('<div some-decor></div>')); var results = createPipeline().process(createElement('<div some-decor></div>'));
expect(results[0].decoratorDirectives).toEqual([reader.annotatedType(SomeDecorator)]); expect(results[0].decoratorDirectives).toEqual([reader.read(SomeDecorator)]);
}); });
it('should detect them in property bindings', () => { it('should detect them in property bindings', () => {
@ -139,7 +139,7 @@ export function main() {
'some-decor': 'someExpr' 'some-decor': 'someExpr'
}}); }});
var results = pipeline.process(createElement('<div></div>')); var results = pipeline.process(createElement('<div></div>'));
expect(results[0].decoratorDirectives).toEqual([reader.annotatedType(SomeDecorator)]); expect(results[0].decoratorDirectives).toEqual([reader.read(SomeDecorator)]);
}); });
it('should detect them in variable bindings', () => { it('should detect them in variable bindings', () => {
@ -147,7 +147,7 @@ export function main() {
'some-decor': 'someExpr' 'some-decor': 'someExpr'
}}); }});
var results = pipeline.process(createElement('<div></div>')); var results = pipeline.process(createElement('<div></div>'));
expect(results[0].decoratorDirectives).toEqual([reader.annotatedType(SomeDecorator)]); expect(results[0].decoratorDirectives).toEqual([reader.read(SomeDecorator)]);
}); });
it('should not allow decorator directives on <template> elements', () => { it('should not allow decorator directives on <template> elements', () => {

View File

@ -59,7 +59,7 @@ export function main() {
if (isPresent(current.element.getAttribute('directives'))) { if (isPresent(current.element.getAttribute('directives'))) {
hasBinding = true; hasBinding = true;
for (var i=0; i<directives.length; i++) { for (var i=0; i<directives.length; i++) {
current.addDirective(reflector.annotatedType(directives[i])); current.addDirective(reflector.read(directives[i]));
} }
} }
if (hasBinding) { if (hasBinding) {

View File

@ -32,7 +32,7 @@ export function main() {
} }
if (isPresent(directives)) { if (isPresent(directives)) {
for (var i=0; i<directives.length; i++) { for (var i=0; i<directives.length; i++) {
current.addDirective(reader.annotatedType(directives[i])); current.addDirective(reader.read(directives[i]));
} }
} }
}), new ElementBindingMarker() }), new ElementBindingMarker()

View File

@ -32,7 +32,7 @@ export function main() {
} }
if (isPresent(current.element.getAttribute('directives'))) { if (isPresent(current.element.getAttribute('directives'))) {
for (var i=0; i<directives.length; i++) { for (var i=0; i<directives.length; i++) {
current.addDirective(reader.annotatedType(directives[i])); current.addDirective(reader.read(directives[i]));
} }
} }
current.inheritedProtoView = protoView; current.inheritedProtoView = protoView;

View File

@ -1,6 +1,7 @@
import {describe, xit, it, expect, beforeEach, ddescribe, iit} from 'test_lib/test_lib'; import {describe, xit, it, expect, beforeEach, ddescribe, iit} from 'test_lib/test_lib';
import {ProtoView, ElementPropertyMemento, DirectivePropertyMemento} from 'core/compiler/view'; import {ProtoView, ElementPropertyMemento, DirectivePropertyMemento} from 'core/compiler/view';
import {ProtoElementInjector, ElementInjector} from 'core/compiler/element_injector'; import {ProtoElementInjector, ElementInjector} from 'core/compiler/element_injector';
import {ShadowDomEmulated} from 'core/compiler/shadow_dom';
import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader'; import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader';
import {Component, Decorator, Template} from 'core/annotations/annotations'; import {Component, Decorator, Template} from 'core/annotations/annotations';
import {OnChange} from 'core/core'; import {OnChange} from 'core/core';
@ -29,8 +30,8 @@ export function main() {
beforeEach(() => { beforeEach(() => {
parser = new Parser(new Lexer()); parser = new Parser(new Lexer());
someComponentDirective = new DirectiveMetadataReader().annotatedType(SomeComponent); someComponentDirective = new DirectiveMetadataReader().read(SomeComponent);
someTemplateDirective = new DirectiveMetadataReader().annotatedType(SomeTemplate); someTemplateDirective = new DirectiveMetadataReader().read(SomeTemplate);
}); });
describe('instatiated from protoView', () => { describe('instatiated from protoView', () => {
@ -258,15 +259,6 @@ export function main() {
return view; return view;
} }
it('should create shadow dom', () => {
var subpv = new ProtoView(createElement('<span>hello shadow dom</span>'), new ProtoRecordRange());
var pv = createComponentWithSubPV(subpv);
var view = createNestedView(pv);
expect(view.nodes[0].shadowRoot.childNodes[0].childNodes[0].nodeValue).toEqual('hello shadow dom');
});
it('should expose component services to the component', () => { it('should expose component services to the component', () => {
var subpv = new ProtoView(createElement('<span></span>'), new ProtoRecordRange()); var subpv = new ProtoView(createElement('<span></span>'), new ProtoRecordRange());
var pv = createComponentWithSubPV(subpv); var pv = createComponentWithSubPV(subpv);
@ -316,6 +308,28 @@ export function main() {
view.componentChildViews.forEach( view.componentChildViews.forEach(
(view) => expectViewHasNoDirectiveInstances(view)); (view) => expectViewHasNoDirectiveInstances(view));
}); });
it('should create shadow dom', () => {
var subpv = new ProtoView(createElement('<span>hello shadow dom</span>'), new ProtoRecordRange());
var pv = createComponentWithSubPV(subpv);
var view = createNestedView(pv);
expect(view.nodes[0].shadowRoot.childNodes[0].childNodes[0].nodeValue).toEqual('hello shadow dom');
});
it('should use the provided shadow DOM strategy', () => {
var subpv = new ProtoView(createElement('<span>hello shadow dom</span>'), new ProtoRecordRange());
var pv = new ProtoView(createElement('<cmp class="ng-binding"></cmp>'), new ProtoRecordRange());
var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeComponentWithEmulatedShadowDom], true));
binder.componentDirective = new DirectiveMetadataReader().read(SomeComponentWithEmulatedShadowDom);
binder.nestedProtoView = subpv;
var view = createNestedView(pv);
expect(view.nodes[0].childNodes[0].childNodes[0].nodeValue).toEqual('hello shadow dom');
});
}); });
describe('with template views', () => { describe('with template views', () => {
@ -485,6 +499,12 @@ class SomeComponent {
} }
} }
@Component({
shadowDom: ShadowDomEmulated
})
class SomeComponentWithEmulatedShadowDom {
}
@Decorator({ @Decorator({
selector: '[dec]' selector: '[dec]'
}) })

View File

@ -47,6 +47,9 @@ class DOM {
static List<Node> childNodes(el) { static List<Node> childNodes(el) {
return el.childNodes; return el.childNodes;
} }
static clearNodes(el) {
el.nodes = [];
}
static appendChild(el, node) { static appendChild(el, node) {
el.append(node); el.append(node);
} }

View File

@ -40,6 +40,9 @@ export class DOM {
static childNodes(el):NodeList { static childNodes(el):NodeList {
return el.childNodes; return el.childNodes;
} }
static clearNodes(el) {
el.innerHTML = "";
}
static appendChild(el, node) { static appendChild(el, node) {
el.appendChild(node); el.appendChild(node);
} }