diff --git a/modules/angular2/src/core/compiler/directive_metadata.ts b/modules/angular2/src/core/compiler/directive_metadata.ts index 5024e8ba04d6..ec344771b5d9 100644 --- a/modules/angular2/src/core/compiler/directive_metadata.ts +++ b/modules/angular2/src/core/compiler/directive_metadata.ts @@ -64,7 +64,7 @@ export class CompileTemplateMetadata { styleUrls?: string[], ngContentSelectors?: string[] } = {}) { - this.encapsulation = encapsulation; + this.encapsulation = isPresent(encapsulation) ? encapsulation : ViewEncapsulation.Emulated; this.template = template; this.templateUrl = templateUrl; this.styles = isPresent(styles) ? styles : []; diff --git a/modules/angular2/src/core/linker/proto_view_factory.ts b/modules/angular2/src/core/linker/proto_view_factory.ts index 1b62d87f1d87..0caa431199ea 100644 --- a/modules/angular2/src/core/linker/proto_view_factory.ts +++ b/modules/angular2/src/core/linker/proto_view_factory.ts @@ -71,7 +71,7 @@ export class ProtoViewFactory { var compiledTemplateData = cmd.template.getData(this._appId); this._renderer.registerComponentTemplate(cmd.templateId, compiledTemplateData.commands, - compiledTemplateData.styles); + compiledTemplateData.styles, cmd.nativeShadow); var boundPipes = this._flattenPipes(view).map(pipe => this._bindPipe(pipe)); nestedProtoView = new AppProtoView(compiledTemplateData.commands, ViewType.COMPONENT, true, diff --git a/modules/angular2/src/core/render/api.ts b/modules/angular2/src/core/render/api.ts index dde8ec21eac3..84c33e1f14d8 100644 --- a/modules/angular2/src/core/render/api.ts +++ b/modules/angular2/src/core/render/api.ts @@ -230,7 +230,8 @@ export class Renderer { * Once a template is registered it can be referenced via {@link RenderBeginComponentCmd} when * {@link #createProtoView creating Render ProtoView}. */ - registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], styles: string[]) {} + registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], styles: string[], + nativeShadow: boolean) {} /** * Creates a {@link RenderProtoViewRef} from an array of {@link RenderTemplateCmd}`s. diff --git a/modules/angular2/src/core/render/dom/dom_renderer.ts b/modules/angular2/src/core/render/dom/dom_renderer.ts index 42b7024a0e93..c26215a8bb1e 100644 --- a/modules/angular2/src/core/render/dom/dom_renderer.ts +++ b/modules/angular2/src/core/render/dom/dom_renderer.ts @@ -35,6 +35,7 @@ import {camelCaseToDashCase} from './util'; @Injectable() export class DomRenderer implements Renderer, NodeFactory { private _componentCmds: Map = new Map(); + private _nativeShadowStyles: Map = new Map(); private _document; /** @@ -46,9 +47,14 @@ export class DomRenderer implements Renderer, NodeFactory { this._document = document; } - registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], styles: string[]) { + registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], styles: string[], + nativeShadow: boolean) { this._componentCmds.set(templateId, commands); - this._domSharedStylesHost.addStyles(styles); + if (nativeShadow) { + this._nativeShadowStyles.set(templateId, styles); + } else { + this._domSharedStylesHost.addStyles(styles); + } } resolveComponentTemplate(templateId: number): RenderTemplateCmd[] { @@ -193,7 +199,14 @@ export class DomRenderer implements Renderer, NodeFactory { DOM.setAttribute(node, attrNameAndValues[attrIdx], attrNameAndValues[attrIdx + 1]); } } - createShadowRoot(host: Node): Node { return DOM.createShadowRoot(host); } + createShadowRoot(host: Node, templateId: number): Node { + var sr = DOM.createShadowRoot(host); + var styles = this._nativeShadowStyles.get(templateId); + for (var i = 0; i < styles.length; i++) { + DOM.appendChild(sr, DOM.createStyleElement(styles[i])); + } + return sr; + } createText(value: string): Node { return DOM.createTextNode(isPresent(value) ? value : ''); } appendChild(parent: Node, child: Node) { DOM.appendChild(parent, child); } on(element: Node, eventName: string, callback: Function) { diff --git a/modules/angular2/src/core/render/view_factory.ts b/modules/angular2/src/core/render/view_factory.ts index da3017e9feec..c59048b6a9d4 100644 --- a/modules/angular2/src/core/render/view_factory.ts +++ b/modules/angular2/src/core/render/view_factory.ts @@ -70,7 +70,7 @@ export interface NodeFactory { createTemplateAnchor(attrNameAndValues: string[]): N; createElement(name: string, attrNameAndValues: string[]): N; mergeElement(existing: N, attrNameAndValues: string[]); - createShadowRoot(host: N): N; + createShadowRoot(host: N, templateId: number): N; createText(value: string): N; appendChild(parent: N, child: N); on(element: N, eventName: string, callback: Function); @@ -125,7 +125,7 @@ class RenderViewBuilder implements RenderCommandVisitor { var el = this._beginElement(cmd); var root = el; if (cmd.nativeShadow) { - root = this.factory.createShadowRoot(el); + root = this.factory.createShadowRoot(el, cmd.templateId); this.nativeShadowRoots.push(root); } this.parentStack.push(new Component(el, root, cmd, this.factory)); diff --git a/modules/angular2/src/web_workers/ui/renderer.ts b/modules/angular2/src/web_workers/ui/renderer.ts index 023e81131f7a..7a149cfa382a 100644 --- a/modules/angular2/src/web_workers/ui/renderer.ts +++ b/modules/angular2/src/web_workers/ui/renderer.ts @@ -31,7 +31,8 @@ export class MessageBasedRenderer { var broker = this._brokerFactory.createMessageBroker(RENDERER_CHANNEL); this._bus.initChannel(EVENT_CHANNEL); - broker.registerMethod("registerComponentTemplate", [PRIMITIVE, WebWorkerTemplateCmd, PRIMITIVE], + broker.registerMethod("registerComponentTemplate", + [PRIMITIVE, WebWorkerTemplateCmd, PRIMITIVE, PRIMITIVE], bind(this._renderer.registerComponentTemplate, this._renderer)); broker.registerMethod("createProtoView", [WebWorkerTemplateCmd, PRIMITIVE], bind(this._createProtoView, this)); diff --git a/modules/angular2/src/web_workers/worker/renderer.ts b/modules/angular2/src/web_workers/worker/renderer.ts index 9defff4368d5..86a050fc423a 100644 --- a/modules/angular2/src/web_workers/worker/renderer.ts +++ b/modules/angular2/src/web_workers/worker/renderer.ts @@ -35,11 +35,13 @@ export class WebWorkerRenderer implements Renderer { this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL); } - registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], styles: string[]) { + registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], styles: string[], + nativeShadow: boolean) { var fnArgs = [ new FnArg(templateId, null), new FnArg(commands, WebWorkerTemplateCmd), - new FnArg(styles, null) + new FnArg(styles, null), + new FnArg(nativeShadow, null) ]; var args = new UiArguments("registerComponentTemplate", fnArgs); this._messageBroker.runOnService(args, null); diff --git a/modules/angular2/test/core/compiler/directive_metadata_spec.ts b/modules/angular2/test/core/compiler/directive_metadata_spec.ts index 0a6efc1dee62..76f9149ec7c3 100644 --- a/modules/angular2/test/core/compiler/directive_metadata_spec.ts +++ b/modules/angular2/test/core/compiler/directive_metadata_spec.ts @@ -76,6 +76,11 @@ export function main() { }); describe('TemplateMetadata', () => { + it('should use ViewEncapsulation.Emulated by default', () => { + expect(new CompileTemplateMetadata({encapsulation: null}).encapsulation) + .toBe(ViewEncapsulation.Emulated); + }); + it('should serialize with full data', () => { expect(CompileTemplateMetadata.fromJson(fullTemplateMeta.toJson())) .toEqual(fullTemplateMeta); diff --git a/modules/angular2/test/core/compiler/template_compiler_spec.ts b/modules/angular2/test/core/compiler/template_compiler_spec.ts index 9d395afc13db..381376eb5dc4 100644 --- a/modules/angular2/test/core/compiler/template_compiler_spec.ts +++ b/modules/angular2/test/core/compiler/template_compiler_spec.ts @@ -26,6 +26,7 @@ import {evalModule} from './eval_module'; import {SourceModule, moduleRef} from 'angular2/src/core/compiler/source_module'; import {XHR} from 'angular2/src/core/compiler/xhr'; import {MockXHR} from 'angular2/src/core/compiler/xhr_mock'; +import {ViewEncapsulation} from 'angular2/src/core/render/api'; import {Locals} from 'angular2/src/core/change_detection/change_detection'; @@ -283,22 +284,29 @@ export function main() { moduleId: THIS_MODULE_ID, exportAs: 'someExportAs' }) -@View({template: '', styles: ['div {color: red}']}) +@View({ + template: '', + styles: ['div {color: red}'], + encapsulation: ViewEncapsulation.None +}) class CompWithBindingsAndStyles { } @Component({selector: 'tree', moduleId: THIS_MODULE_ID}) -@View({template: '', directives: [TreeComp]}) +@View({template: '', directives: [TreeComp], encapsulation: ViewEncapsulation.None}) class TreeComp { } @Component({selector: 'comp-url', moduleId: THIS_MODULE_ID}) -@View({templateUrl: 'compUrl.html'}) +@View({templateUrl: 'compUrl.html', encapsulation: ViewEncapsulation.None}) class CompWithTemplateUrl { } @Component({selector: 'comp-tpl', moduleId: THIS_MODULE_ID}) -@View({template: ''}) +@View({ + template: '', + encapsulation: ViewEncapsulation.None +}) class CompWithEmbeddedTemplate { } diff --git a/modules/angular2/test/core/linker/projection_integration_spec.ts b/modules/angular2/test/core/linker/projection_integration_spec.ts index 9955c589bef7..728d7079f794 100644 --- a/modules/angular2/test/core/linker/projection_integration_spec.ts +++ b/modules/angular2/test/core/linker/projection_integration_spec.ts @@ -403,18 +403,18 @@ export function main() { })); if (DOM.supportsNativeShadowDOM()) { - it('should support native content projection', + it('should support native content projection and isolate styles per component', inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { tcb.overrideView(MainComp, new ViewMetadata({ - template: '' + - '
A
' + - '
', - directives: [SimpleNative] + template: '
A
' + + '
B
', + directives: [SimpleNative1, SimpleNative2] })) .createAsync(MainComp) .then((main) => { - - expect(main.debugElement.nativeElement).toHaveText('SIMPLE(A)'); + var childNodes = DOM.childNodes(main.debugElement.nativeElement); + expect(childNodes[0]).toHaveText('div {color: red}SIMPLE1(A)'); + expect(childNodes[1]).toHaveText('div {color: blue}SIMPLE2(B)'); async.done(); }); })); @@ -459,13 +459,24 @@ class Simple { stringProp: string = ''; } -@Component({selector: 'simple-native'}) +@Component({selector: 'simple-native1'}) +@View({ + template: 'SIMPLE1()', + directives: [], + encapsulation: ViewEncapsulation.Native, + styles: ['div {color: red}'] +}) +class SimpleNative1 { +} + +@Component({selector: 'simple-native2'}) @View({ - template: 'SIMPLE()', + template: 'SIMPLE2()', directives: [], - encapsulation: ViewEncapsulation.Native + encapsulation: ViewEncapsulation.Native, + styles: ['div {color: blue}'] }) -class SimpleNative { +class SimpleNative2 { } @Component({selector: 'empty'}) diff --git a/modules/angular2/test/core/render/view_factory_spec.ts b/modules/angular2/test/core/render/view_factory_spec.ts index 7fbfd0c65801..014bedb95baa 100644 --- a/modules/angular2/test/core/render/view_factory_spec.ts +++ b/modules/angular2/test/core/render/view_factory_spec.ts @@ -483,7 +483,7 @@ class DomNodeFactory implements NodeFactory { DOM.setAttribute(el, attrNameAndValues[attrIdx], attrNameAndValues[attrIdx + 1]); } } - createShadowRoot(host: Node): Node { + createShadowRoot(host: Node, templateId: number): Node { var root = DOM.createElement('shadow-root'); DOM.appendChild(host, root); return root;