feat(core): remove ViewEncapsulation.Native (#38882)

Removes `ViewEncapsulation.Native` which has been deprecated for several major versions.

BREAKING CHANGES:
* `ViewEncapsulation.Native` has been removed. Use `ViewEncapsulation.ShadowDom` instead. Existing
usages will be updated automatically by `ng update`.

PR Close #38882
This commit is contained in:
Kristiyan Kostadinov 2020-10-08 16:59:29 +02:00 committed by atscott
parent 0e733f3689
commit 4a1c12c773
25 changed files with 77 additions and 91 deletions

View File

@ -13,8 +13,8 @@ import { Component, ViewEncapsulation } from '@angular/core';
export class QuestSummaryComponent { } export class QuestSummaryComponent { }
// #enddocregion // #enddocregion
/* /*
// #docregion encapsulation.native // #docregion encapsulation.shadow
// warning: few browsers support shadow DOM encapsulation at this time // warning: few browsers support shadow DOM encapsulation at this time
encapsulation: ViewEncapsulation.Native encapsulation: ViewEncapsulation.ShadowDom
// #enddocregion encapsulation.native // #enddocregion encapsulation.shadow
*/ */

View File

@ -41,7 +41,6 @@ v9 - v12
| `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v11 | | `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v11 |
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v11 | | `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v11 |
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v11 | | `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v11 |
| `@angular/core` | [`ViewEncapsulation.Native`](#core) | <!--v6--> v11 |
| `@angular/core` | [`WrappedValue`](#core) | <!--v10--> v12 | | `@angular/core` | [`WrappedValue`](#core) | <!--v10--> v12 |
| `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | <!--v6--> v11 | | `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | <!--v6--> v11 |
| `@angular/router` | [`preserveQueryParams`](#router) | <!--v7--> v11 | | `@angular/router` | [`preserveQueryParams`](#router) | <!--v7--> v11 |
@ -89,7 +88,6 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i
| [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. | | [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. |
| [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | See [`ReflectiveInjector`](#reflectiveinjector) | | [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | See [`ReflectiveInjector`](#reflectiveinjector) |
| [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none | | [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none |
| [`ViewEncapsulation.Native`](api/core/ViewEncapsulation#Native) | [`ViewEncapsulation.ShadowDom`](api/core/ViewEncapsulation#ShadowDom) | v6 | Use the native encapsulation mechanism of the renderer. See [view.ts](https://github.com/angular/angular/blob/3e992e18ebf51d6036818f26c3d77b52d3ec48eb/packages/core/src/metadata/view.ts#L32).
| [`defineInjectable`](api/core/defineInjectable) | `ɵɵdefineInjectable` | v8 | Used only in generated code. No source code should depend on this API. | | [`defineInjectable`](api/core/defineInjectable) | `ɵɵdefineInjectable` | v8 | Used only in generated code. No source code should depend on this API. |
| [`entryComponents`](api/core/NgModule#entryComponents) | none | v9 | See [`entryComponents`](#entryComponents) | | [`entryComponents`](api/core/NgModule#entryComponents) | none | v9 | See [`entryComponents`](#entryComponents) |
| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) | | [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) |

View File

@ -13,8 +13,6 @@ Choose from the following modes:
to attach a shadow DOM to the component's host element, and then puts the component to attach a shadow DOM to the component's host element, and then puts the component
view inside that shadow DOM. The component's styles are included within the shadow DOM. view inside that shadow DOM. The component's styles are included within the shadow DOM.
* `Native` view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - [learn about the changes](https://hayato.io/2016/shadowdomv1/).
* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing * `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing
(and renaming) the CSS code to effectively scope the CSS to the component's view. (and renaming) the CSS code to effectively scope the CSS to the component's view.
For details, see [Inspecting generated CSS](guide/view-encapsulation#inspect-generated-css) below. For details, see [Inspecting generated CSS](guide/view-encapsulation#inspect-generated-css) below.
@ -26,7 +24,7 @@ Choose from the following modes:
To set the components encapsulation mode, use the `encapsulation` property in the component metadata: To set the components encapsulation mode, use the `encapsulation` property in the component metadata:
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" header="src/app/quest-summary.component.ts"></code-example> <code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.shadow" header="src/app/quest-summary.component.ts"></code-example>
`ShadowDom` view encapsulation only works on browsers that have native support `ShadowDom` view encapsulation only works on browsers that have native support
for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the

View File

@ -21,7 +21,7 @@ const initialDocViewerContent = initialDocViewerElement ? initialDocViewerElemen
selector: 'aio-doc-viewer', selector: 'aio-doc-viewer',
template: '' template: ''
// TODO(robwormald): shadow DOM and emulated don't work here (?!) // TODO(robwormald): shadow DOM and emulated don't work here (?!)
// encapsulation: ViewEncapsulation.Native // encapsulation: ViewEncapsulation.ShadowDom
}) })
export class DocViewerComponent implements OnDestroy { export class DocViewerComponent implements OnDestroy {
// Enable/Disable view transition animations. // Enable/Disable view transition animations.

View File

@ -1032,7 +1032,6 @@ export declare abstract class ViewContainerRef {
export declare enum ViewEncapsulation { export declare enum ViewEncapsulation {
Emulated = 0, Emulated = 0,
Native = 1,
None = 2, None = 2,
ShadowDom = 3 ShadowDom = 3
} }

View File

@ -30,7 +30,7 @@
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime-es2015": 1485, "runtime-es2015": 1485,
"main-es2015": 134891, "main-es2015": 135003,
"polyfills-es2015": 37248 "polyfills-es2015": 37248
} }
} }

View File

@ -107,7 +107,6 @@ export declare enum Style {
*/ */
export declare enum ViewEncapsulation { export declare enum ViewEncapsulation {
Emulated = 'Emulated', Emulated = 'Emulated',
Native = 'Native',
None = 'None', None = 'None',
ShadowDom = 'ShadowDom' ShadowDom = 'ShadowDom'
} }

View File

@ -72,7 +72,7 @@ describe('compiler compliance: styling', () => {
expectEmit(result.source, template, 'Incorrect template'); expectEmit(result.source, template, 'Incorrect template');
}); });
it('should pass in the component metadata styles into the component definition but skip shimming when style encapsulation is set to native', it('should pass in the component metadata styles into the component definition but skip shimming when style encapsulation is set to shadow dom',
() => { () => {
const files = { const files = {
app: { app: {
@ -80,7 +80,7 @@ describe('compiler compliance: styling', () => {
import {Component, NgModule, ViewEncapsulation} from '@angular/core'; import {Component, NgModule, ViewEncapsulation} from '@angular/core';
@Component({ @Component({
encapsulation: ViewEncapsulation.Native, encapsulation: ViewEncapsulation.ShadowDom,
selector: "my-component", selector: "my-component",
styles: ["div.cool { color: blue; }", ":host.nice p { color: gold; }"], styles: ["div.cool { color: blue; }", ":host.nice p { color: gold; }"],
template: "..." template: "..."
@ -98,7 +98,7 @@ describe('compiler compliance: styling', () => {
MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({
styles: ["div.cool { color: blue; }", ":host.nice p { color: gold; }"], styles: ["div.cool { color: blue; }", ":host.nice p { color: gold; }"],
encapsulation: 1 encapsulation: 3
}) })
`; `;
const result = compile(files, angularFiles); const result = compile(files, angularFiles);

View File

@ -77,7 +77,7 @@ export type ɵɵPipeDefWithMeta<PipeT, NameT> = any;
export enum ViewEncapsulation { export enum ViewEncapsulation {
Emulated = 0, Emulated = 0,
Native = 1, // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
None = 2, None = 2,
ShadowDom = 3 ShadowDom = 3
} }

View File

@ -173,7 +173,7 @@ export interface R3FactoryDefMetadataFacade {
export enum ViewEncapsulation { export enum ViewEncapsulation {
Emulated = 0, Emulated = 0,
Native = 1, // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
None = 2, None = 2,
ShadowDom = 3 ShadowDom = 3
} }

View File

@ -82,7 +82,7 @@ export interface Component extends Directive {
} }
export enum ViewEncapsulation { export enum ViewEncapsulation {
Emulated = 0, Emulated = 0,
Native = 1, // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
None = 2, None = 2,
ShadowDom = 3 ShadowDom = 3
} }

View File

@ -163,8 +163,6 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
/** /**
* An encapsulation policy for the template and CSS styles. One of: * An encapsulation policy for the template and CSS styles. One of:
* - `ViewEncapsulation.Native`: Use shadow roots. This works only if natively available on the
* platform (note that this is marked the as the "deprecated shadow DOM" as of Angular v6.1.
* - `ViewEncapsulation.Emulated`: Use shimmed CSS that emulates the native behavior. * - `ViewEncapsulation.Emulated`: Use shimmed CSS that emulates the native behavior.
* - `ViewEncapsulation.None`: Use global CSS without any encapsulation. * - `ViewEncapsulation.None`: Use global CSS without any encapsulation.
* - `ViewEncapsulation.ShadowDom`: Use the latest ShadowDOM API to natively encapsulate styles * - `ViewEncapsulation.ShadowDom`: Use the latest ShadowDOM API to natively encapsulate styles

View File

@ -226,7 +226,7 @@ function normalizeTemplate(normalizer: DirectiveNormalizer, o: {
describe('normalizeLoadedTemplate', () => { describe('normalizeLoadedTemplate', () => {
it('should store the viewEncapsulation in the result', it('should store the viewEncapsulation in the result',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const viewEncapsulation = ViewEncapsulation.Native; const viewEncapsulation = ViewEncapsulation.ShadowDom;
const template = <CompileTemplateMetadata>normalizeTemplate(normalizer, { const template = <CompileTemplateMetadata>normalizeTemplate(normalizer, {
encapsulation: viewEncapsulation, encapsulation: viewEncapsulation,
template: '', template: '',

View File

@ -173,7 +173,7 @@ export interface R3FactoryDefMetadataFacade {
export enum ViewEncapsulation { export enum ViewEncapsulation {
Emulated = 0, Emulated = 0,
Native = 1, // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
None = 2, None = 2,
ShadowDom = 3 ShadowDom = 3
} }

View File

@ -518,7 +518,6 @@ export interface Component extends Directive {
/** /**
* An encapsulation policy for the template and CSS styles. One of: * An encapsulation policy for the template and CSS styles. One of:
* - `ViewEncapsulation.Native`: Deprecated. Use `ViewEncapsulation.ShadowDom` instead.
* - `ViewEncapsulation.Emulated`: Use shimmed CSS that * - `ViewEncapsulation.Emulated`: Use shimmed CSS that
* emulates the native behavior. * emulates the native behavior.
* - `ViewEncapsulation.None`: Use global CSS without any * - `ViewEncapsulation.None`: Use global CSS without any

View File

@ -28,15 +28,9 @@ export enum ViewEncapsulation {
* This is the default option. * This is the default option.
*/ */
Emulated = 0, Emulated = 0,
/**
* @deprecated v6.1.0 - use {ViewEncapsulation.ShadowDom} instead. // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
* Use the native encapsulation mechanism of the renderer.
*
* For the DOM this means using the deprecated [Shadow DOM
* v0](https://w3c.github.io/webcomponents/spec/shadow/) and
* creating a ShadowRoot for Component's Host Element.
*/
Native = 1,
/** /**
* Don't provide any template or style encapsulation. * Don't provide any template or style encapsulation.
*/ */

View File

@ -517,8 +517,8 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme
// Since the projection would then move it to its final destination. Note that we can't // Since the projection would then move it to its final destination. Note that we can't
// make this assumption when using the Shadow DOM, because the native projection placeholders // make this assumption when using the Shadow DOM, because the native projection placeholders
// (<content> or <slot>) have to be in place as elements are being inserted. // (<content> or <slot>) have to be in place as elements are being inserted.
if (encapsulation !== ViewEncapsulation.ShadowDom && if (encapsulation === ViewEncapsulation.None ||
encapsulation !== ViewEncapsulation.Native) { encapsulation === ViewEncapsulation.Emulated) {
return null; return null;
} }
} }

View File

@ -230,7 +230,10 @@ export function getParentRenderElement(view: ViewData, renderHost: any, def: Nod
if ((renderParent.flags & NodeFlags.TypeElement) === 0 || if ((renderParent.flags & NodeFlags.TypeElement) === 0 ||
(renderParent.flags & NodeFlags.ComponentView) === 0 || (renderParent.flags & NodeFlags.ComponentView) === 0 ||
(renderParent.element!.componentRendererType && (renderParent.element!.componentRendererType &&
renderParent.element!.componentRendererType!.encapsulation === ViewEncapsulation.Native)) { (renderParent.element!.componentRendererType!.encapsulation ===
ViewEncapsulation.ShadowDom ||
// TODO(FW-2290): remove the `encapsulation === 1` fallback logic in v12.
renderParent.element!.componentRendererType!.encapsulation === 1))) {
// only children of non components, or children of components with native encapsulation should // only children of non components, or children of components with native encapsulation should
// be attached. // be attached.
return asElementData(view, def.renderParent!.nodeIndex).renderElement; return asElementData(view, def.renderParent!.nodeIndex).renderElement;

View File

@ -490,13 +490,13 @@ describe('projection', () => {
expect(main.nativeElement).toHaveText('TREE(0:TREE2(1:TREE(2:)))'); expect(main.nativeElement).toHaveText('TREE(0:TREE2(1:TREE(2:)))');
}); });
if (supportsNativeShadowDOM()) { if (supportsShadowDOM()) {
it('should support native content projection and isolate styles per component', () => { it('should support shadow dom content projection and isolate styles per component', () => {
TestBed.configureTestingModule({declarations: [SimpleNative1, SimpleNative2]}); TestBed.configureTestingModule({declarations: [SimpleShadowDom1, SimpleShadowDom2]});
TestBed.overrideComponent(MainComp, { TestBed.overrideComponent(MainComp, {
set: { set: {
template: '<simple-native1><div>A</div></simple-native1>' + template: '<simple-shadow-dom1><div>A</div></simple-shadow-dom1>' +
'<simple-native2><div>B</div></simple-native2>' '<simple-shadow-dom2><div>B</div></simple-shadow-dom2>'
} }
}); });
const main = TestBed.createComponent(MainComp); const main = TestBed.createComponent(MainComp);
@ -857,21 +857,21 @@ class Simple {
} }
@Component({ @Component({
selector: 'simple-native1', selector: 'simple-shadow-dom1',
template: 'SIMPLE1(<content></content>)', template: 'SIMPLE1(<slot></slot>)',
encapsulation: ViewEncapsulation.Native, encapsulation: ViewEncapsulation.ShadowDom,
styles: ['div {color: red}'] styles: ['div {color: red}']
}) })
class SimpleNative1 { class SimpleShadowDom1 {
} }
@Component({ @Component({
selector: 'simple-native2', selector: 'simple-shadow-dom2',
template: 'SIMPLE2(<content></content>)', template: 'SIMPLE2(<slot></slot>)',
encapsulation: ViewEncapsulation.Native, encapsulation: ViewEncapsulation.ShadowDom,
styles: ['div {color: blue}'] styles: ['div {color: blue}']
}) })
class SimpleNative2 { class SimpleShadowDom2 {
} }
@Component({selector: 'empty', template: ''}) @Component({selector: 'empty', template: ''})
@ -1043,6 +1043,6 @@ class CmpA1 {
class CmpA2 { class CmpA2 {
} }
function supportsNativeShadowDOM(): boolean { function supportsShadowDOM(): boolean {
return typeof (<any>document.body).createShadowRoot === 'function'; return typeof (<any>document.body).attachShadow !== 'undefined';
} }

View File

@ -74,6 +74,8 @@ function decoratePreventDefault(eventHandler: Function): Function {
}; };
} }
let hasLoggedNativeEncapsulationWarning = false;
@Injectable() @Injectable()
export class DomRendererFactory2 implements RendererFactory2 { export class DomRendererFactory2 implements RendererFactory2 {
private rendererByCompId = new Map<string, Renderer2>(); private rendererByCompId = new Map<string, Renderer2>();
@ -100,8 +102,16 @@ export class DomRendererFactory2 implements RendererFactory2 {
(<EmulatedEncapsulationDomRenderer2>renderer).applyToHost(element); (<EmulatedEncapsulationDomRenderer2>renderer).applyToHost(element);
return renderer; return renderer;
} }
case ViewEncapsulation.Native: case 1:
case ViewEncapsulation.ShadowDom: case ViewEncapsulation.ShadowDom:
// TODO(FW-2290): remove the `case 1:` fallback logic and the warning in v12.
if ((typeof ngDevMode === 'undefined' || ngDevMode) &&
!hasLoggedNativeEncapsulationWarning && type.encapsulation === 1) {
hasLoggedNativeEncapsulationWarning = true;
console.warn(
'ViewEncapsulation.Native is no longer supported. Falling back to ViewEncapsulation.ShadowDom. The fallback will be removed in v12.');
}
return new ShadowDomRenderer(this.eventManager, this.sharedStylesHost, element, type); return new ShadowDomRenderer(this.eventManager, this.sharedStylesHost, element, type);
default: { default: {
if (!this.rendererByCompId.has(type.id)) { if (!this.rendererByCompId.has(type.id)) {
@ -302,13 +312,9 @@ class ShadowDomRenderer extends DefaultDomRenderer2 {
constructor( constructor(
eventManager: EventManager, private sharedStylesHost: DomSharedStylesHost, eventManager: EventManager, private sharedStylesHost: DomSharedStylesHost,
private hostEl: any, private component: RendererType2) { private hostEl: any, component: RendererType2) {
super(eventManager); super(eventManager);
if (component.encapsulation === ViewEncapsulation.ShadowDom) {
this.shadowRoot = (hostEl as any).attachShadow({mode: 'open'}); this.shadowRoot = (hostEl as any).attachShadow({mode: 'open'});
} else {
this.shadowRoot = (hostEl as any).createShadowRoot();
}
this.sharedStylesHost.addHost(this.shadowRoot); this.sharedStylesHost.addHost(this.shadowRoot);
const styles = flattenStyles(component.id, component.styles, []); const styles = flattenStyles(component.id, component.styles, []);
for (let i = 0; i < styles.length; i++) { for (let i = 0; i < styles.length; i++) {

View File

@ -20,8 +20,8 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ declarations: [
TestCmp, SomeApp, CmpEncapsulationEmulated, CmpEncapsulationNative, CmpEncapsulationNone, TestCmp, SomeApp, CmpEncapsulationEmulated, CmpEncapsulationShadow, CmpEncapsulationNone,
CmpEncapsulationNative CmpEncapsulationShadow
] ]
}); });
renderer = TestBed.createComponent(TestCmp).componentInstance.renderer; renderer = TestBed.createComponent(TestCmp).componentInstance.renderer;
@ -98,16 +98,14 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
}); });
}); });
if (browserDetection.supportsDeprecatedShadowDomV0) { if (browserDetection.supportsShadowDom) {
it('should allow to style components with emulated encapsulation and no encapsulation inside of components with shadow DOM', it('should allow to style components with emulated encapsulation and no encapsulation inside of components with shadow DOM',
() => { () => {
const fixture = TestBed.createComponent(SomeApp); const fixture = TestBed.createComponent(SomeApp);
const cmp = fixture.debugElement.query(By.css('cmp-shadow')).nativeElement;
const shadow = cmp.shadowRoot.querySelector('.shadow');
const cmp = fixture.debugElement.query(By.css('cmp-native')).nativeElement; expect(window.getComputedStyle(shadow).color).toEqual('rgb(255, 0, 0)');
const native = cmp.shadowRoot.querySelector('.native');
expect(window.getComputedStyle(native).color).toEqual('rgb(255, 0, 0)');
const emulated = cmp.shadowRoot.querySelector('.emulated'); const emulated = cmp.shadowRoot.querySelector('.emulated');
expect(window.getComputedStyle(emulated).color).toEqual('rgb(0, 0, 255)'); expect(window.getComputedStyle(emulated).color).toEqual('rgb(0, 0, 255)');
@ -119,15 +117,6 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
}); });
} }
@Component({
selector: 'cmp-native',
template: `<div class="native"></div><cmp-emulated></cmp-emulated><cmp-none></cmp-none>`,
styles: [`.native { color: red; }`],
encapsulation: ViewEncapsulation.Native
})
class CmpEncapsulationNative {
}
@Component({ @Component({
selector: 'cmp-emulated', selector: 'cmp-emulated',
template: `<div class="emulated"></div>`, template: `<div class="emulated"></div>`,
@ -149,7 +138,7 @@ class CmpEncapsulationNone {
@Component({ @Component({
selector: 'cmp-shadow', selector: 'cmp-shadow',
template: `<div class="shadow"></div><cmp-emulated></cmp-emulated><cmp-none></cmp-none>`, template: `<div class="shadow"></div><cmp-emulated></cmp-emulated><cmp-none></cmp-none>`,
styles: [`.native { color: red; }`], styles: [`.shadow { color: red; }`],
encapsulation: ViewEncapsulation.ShadowDom encapsulation: ViewEncapsulation.ShadowDom
}) })
class CmpEncapsulationShadow { class CmpEncapsulationShadow {
@ -158,7 +147,7 @@ class CmpEncapsulationShadow {
@Component({ @Component({
selector: 'some-app', selector: 'some-app',
template: ` template: `
<cmp-native></cmp-native> <cmp-shadow></cmp-shadow>
<cmp-emulated></cmp-emulated> <cmp-emulated></cmp-emulated>
<cmp-none></cmp-none> <cmp-none></cmp-none>
`, `,

View File

@ -15,13 +15,11 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
if (browserDetection.supportsShadowDom) { if (browserDetection.supportsShadowDom) {
describe('ShadowDOM Support', () => { describe('ShadowDOM Support', () => {
let testContainer: HTMLDivElement;
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({imports: [TestModule]}); TestBed.configureTestingModule({imports: [TestModule]});
}); });
it('should attach and use a shadowRoot when ViewEncapsulation.Native is set', () => { it('should attach and use a shadowRoot when ViewEncapsulation.ShadowDom is set', () => {
const compEl = TestBed.createComponent(ShadowComponent).nativeElement; const compEl = TestBed.createComponent(ShadowComponent).nativeElement;
expect(compEl.shadowRoot!.textContent).toEqual('Hello World'); expect(compEl.shadowRoot!.textContent).toEqual('Hello World');
}); });

View File

@ -305,8 +305,14 @@ function elementText(n: any): string {
return ''; return '';
} }
if (getDOM().isElementNode(n) && (n as Element).tagName == 'CONTENT') { if (getDOM().isElementNode(n)) {
const tagName = (n as Element).tagName;
if (tagName === 'CONTENT') {
return elementText(Array.prototype.slice.apply((<any>n).getDistributedNodes())); return elementText(Array.prototype.slice.apply((<any>n).getDistributedNodes()));
} else if (tagName === 'SLOT') {
return elementText(Array.prototype.slice.apply((<any>n).assignedNodes()));
}
} }
if (hasShadowRoot(n)) { if (hasShadowRoot(n)) {

View File

@ -32,7 +32,6 @@ export class ServerRendererFactory2 implements RendererFactory2 {
return this.defaultRenderer; return this.defaultRenderer;
} }
switch (type.encapsulation) { switch (type.encapsulation) {
case ViewEncapsulation.Native:
case ViewEncapsulation.Emulated: { case ViewEncapsulation.Emulated: {
let renderer = this.rendererByCompId.get(type.id); let renderer = this.rendererByCompId.get(type.id);
if (!renderer) { if (!renderer) {

View File

@ -272,19 +272,19 @@ class ImageExampleModule {
@Component({ @Component({
selector: 'app', selector: 'app',
template: 'Native works', template: 'Shadow DOM works',
encapsulation: ViewEncapsulation.Native, encapsulation: ViewEncapsulation.ShadowDom,
styles: [':host { color: red; }'] styles: [':host { color: red; }']
}) })
class NativeEncapsulationApp { class ShadowDomEncapsulationApp {
} }
@NgModule({ @NgModule({
declarations: [NativeEncapsulationApp], declarations: [ShadowDomEncapsulationApp],
imports: [BrowserModule.withServerTransition({appId: 'test'}), ServerModule], imports: [BrowserModule.withServerTransition({appId: 'test'}), ServerModule],
bootstrap: [NativeEncapsulationApp] bootstrap: [ShadowDomEncapsulationApp]
}) })
class NativeExampleModule { class ShadowDomExampleModule {
} }
@Component({selector: 'my-child', template: 'Works!'}) @Component({selector: 'my-child', template: 'Works!'})
@ -666,8 +666,8 @@ describe('platform-server integration', () => {
}); });
})); }));
it('should handle ViewEncapsulation.Native', waitForAsync(() => { it('should handle ViewEncapsulation.ShadowDom', waitForAsync(() => {
renderModule(NativeExampleModule, {document: doc}).then(output => { renderModule(ShadowDomExampleModule, {document: doc}).then(output => {
expect(output).not.toBe(''); expect(output).not.toBe('');
expect(output).toContain('color: red'); expect(output).toContain('color: red');
called = true; called = true;