feat(compiler): implement style encapsulation for new view engine (#14518)
Included refactoring: - splits the `RendererV2` into a `RendererFactoryV2` and a `RendererV2` - makes the `DebugRendererV2` a private class in `@angular/core` - remove `setBindingDebugInfo` from `RendererV2`, but rename `RendererV2.setText` to `RendererV2.setValue` and allow it on comments and text nodes. Part of #14013
This commit is contained in:
@ -9,8 +9,8 @@
|
||||
import {USE_VIEW_ENGINE} from '@angular/compiler/src/config';
|
||||
import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
|
||||
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings';
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RENDERER_V2_DIRECT, RenderComponentType, Renderer, RendererV2, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
|
||||
import {DebugDomRenderer, DebugDomRendererV2} from '@angular/core/src/debug/debug_renderer';
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RendererFactoryV2, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
|
||||
import {DebugDomRenderer} from '@angular/core/src/debug/debug_renderer';
|
||||
import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
@ -57,6 +57,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
renderLog = TestBed.get(RenderLog);
|
||||
directiveLog = TestBed.get(DirectiveLog);
|
||||
elSchema.existingProperties['someProp'] = true;
|
||||
patchLoggingRendererV2(TestBed.get(RendererFactoryV2), renderLog);
|
||||
}
|
||||
|
||||
function queryDirs(el: DebugElement, dirType: Type<any>): any {
|
||||
@ -123,11 +124,6 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
RenderLog,
|
||||
DirectiveLog,
|
||||
{provide: RootRenderer, useClass: LoggingRootRenderer},
|
||||
{
|
||||
provide: RendererV2,
|
||||
useFactory: (r: RendererV2, log: RenderLog) => new LoggingRendererV2(r, log),
|
||||
deps: [[new Inject(RENDERER_V2_DIRECT)], [RenderLog]],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
@ -1244,28 +1240,26 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
expect(renderLog.loggedValues).toEqual(['Tom']);
|
||||
});
|
||||
|
||||
// TODO(tbosch): ViewQueries don't work yet with the view engine...
|
||||
viewEngine ||
|
||||
it('should recurse into nested view containers even if there are no bindings in the component view',
|
||||
() => {
|
||||
@Component({template: '<template #vc>{{name}}</template>'})
|
||||
class Comp {
|
||||
name = 'Tom';
|
||||
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
|
||||
@ViewChild(TemplateRef) template: TemplateRef<any>;
|
||||
}
|
||||
it('should recurse into nested view containers even if there are no bindings in the component view',
|
||||
() => {
|
||||
@Component({template: '<template #vc>{{name}}</template>'})
|
||||
class Comp {
|
||||
name = 'Tom';
|
||||
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
|
||||
@ViewChild(TemplateRef) template: TemplateRef<any>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Comp]});
|
||||
initHelpers();
|
||||
TestBed.configureTestingModule({declarations: [Comp]});
|
||||
initHelpers();
|
||||
|
||||
const ctx = TestBed.createComponent(Comp);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual([]);
|
||||
const ctx = TestBed.createComponent(Comp);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual([]);
|
||||
|
||||
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual(['Tom']);
|
||||
});
|
||||
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual(['Tom']);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1315,18 +1309,32 @@ class DirectiveLogEntry {
|
||||
constructor(public directiveName: string, public method: string) {}
|
||||
}
|
||||
|
||||
class LoggingRendererV2 extends DebugDomRendererV2 {
|
||||
constructor(private delegate: RendererV2, private log: RenderLog) { super(delegate); }
|
||||
|
||||
setProperty(el: any, name: string, value: any): void {
|
||||
this.log.setElementProperty(el, name, value);
|
||||
super.setProperty(el, name, value);
|
||||
}
|
||||
|
||||
setText(node: any, value: string): void {
|
||||
this.log.setText(node, value);
|
||||
super.setText(node, value);
|
||||
function patchLoggingRendererV2(rendererFactory: RendererFactoryV2, log: RenderLog) {
|
||||
if ((<any>rendererFactory).__patchedForLogging) {
|
||||
return;
|
||||
}
|
||||
(<any>rendererFactory).__patchedForLogging = true;
|
||||
const origCreateRenderer = rendererFactory.createRenderer;
|
||||
rendererFactory.createRenderer = function() {
|
||||
const renderer = origCreateRenderer.apply(this, arguments);
|
||||
if ((<any>renderer).__patchedForLogging) {
|
||||
return renderer;
|
||||
}
|
||||
(<any>renderer).__patchedForLogging = true;
|
||||
const origSetProperty = renderer.setProperty;
|
||||
const origSetValue = renderer.setValue;
|
||||
renderer.setProperty = function(el: any, name: string, value: any): void {
|
||||
log.setElementProperty(el, name, value);
|
||||
origSetProperty.call(this, el, name, value);
|
||||
};
|
||||
renderer.setValue = function(node: any, value: string): void {
|
||||
if (getDOM().isTextNode(node)) {
|
||||
log.setText(node, value);
|
||||
}
|
||||
origSetValue.call(this, node, value);
|
||||
};
|
||||
return renderer;
|
||||
};
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
|
@ -1515,7 +1515,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
const fixture = TestBed.createComponent(ParentCmp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('ng-reflect-test$="hello"');
|
||||
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('ng-reflect-test_="hello"');
|
||||
});
|
||||
|
||||
it('should reflect property values on template comments', () => {
|
||||
|
@ -378,7 +378,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
expect(main.nativeElement).toHaveText('TREE(0:TREE2(1:TREE(2:)))');
|
||||
});
|
||||
|
||||
if (!viewEngine && getDOM().supportsNativeShadowDOM()) {
|
||||
if (getDOM().supportsNativeShadowDOM()) {
|
||||
it('should support native content projection and isolate styles per component', () => {
|
||||
TestBed.configureTestingModule({declarations: [SimpleNative1, SimpleNative2]});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
@ -396,7 +396,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
});
|
||||
}
|
||||
|
||||
if (!viewEngine && getDOM().supportsDOMEvents()) {
|
||||
if (getDOM().supportsDOMEvents()) {
|
||||
it('should support non emulated styles', () => {
|
||||
TestBed.configureTestingModule({declarations: [OtherComp]});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
|
Reference in New Issue
Block a user