refactor(core): remove unused embedded view instructions (#34715)
This commit performs a cleanup and removes unused embedded view instructions and corresponding tests. PR Close #34715
This commit is contained in:

committed by
Misko Hevery

parent
790af88288
commit
edcc8b8acf
@ -8,12 +8,12 @@
|
||||
|
||||
import {ViewEncapsulation, ɵɵdefineInjectable, ɵɵdefineInjector} from '../../src/core';
|
||||
import {createInjector} from '../../src/di/r3_injector';
|
||||
import {AttributeMarker, ComponentFactory, getRenderedText, LifecycleHooksFeature, markDirty, ɵɵadvance, ɵɵdefineComponent, ɵɵdirectiveInject, ɵɵproperty, ɵɵselect, ɵɵtemplate} from '../../src/render3/index';
|
||||
import {tick, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵnextContext, ɵɵtext, ɵɵtextInterpolate} from '../../src/render3/instructions/all';
|
||||
import {ComponentDef, RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {AttributeMarker, markDirty, ɵɵadvance, ɵɵdefineComponent, ɵɵdirectiveInject, ɵɵproperty, ɵɵtemplate} from '../../src/render3/index';
|
||||
import {ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵtext, ɵɵtextInterpolate} from '../../src/render3/instructions/all';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
|
||||
import {NgIf} from './common_with_def';
|
||||
import {ComponentFixture, containerEl, createComponent, MockRendererFactory, renderComponent, renderToHtml, requestAnimationFrame, toHtml} from './render_util';
|
||||
import {ComponentFixture, containerEl, createComponent, MockRendererFactory, renderComponent, requestAnimationFrame, toHtml} from './render_util';
|
||||
|
||||
describe('component', () => {
|
||||
class CounterComponent {
|
||||
@ -219,349 +219,4 @@ it('should not invoke renderer destroy method for embedded views', () => {
|
||||
// we should never see `destroy` method being called
|
||||
// in case child views are created/removed
|
||||
expect(destroySpy.calls.count()).toBe(0);
|
||||
});
|
||||
|
||||
describe('component with a container', () => {
|
||||
function showItems(rf: RenderFlags, ctx: {items: string[]}) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵcontainer(0);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
ɵɵcontainerRefreshStart(0);
|
||||
{
|
||||
for (const item of ctx.items) {
|
||||
const rf0 = ɵɵembeddedViewStart(0, 1, 1);
|
||||
{
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
ɵɵtext(0);
|
||||
}
|
||||
if (rf0 & RenderFlags.Update) {
|
||||
ɵɵselect(0);
|
||||
ɵɵtextInterpolate(item);
|
||||
}
|
||||
}
|
||||
ɵɵembeddedViewEnd();
|
||||
}
|
||||
}
|
||||
ɵɵcontainerRefreshEnd();
|
||||
}
|
||||
}
|
||||
|
||||
class WrapperComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
items!: string[];
|
||||
static ɵfac = () => new WrapperComponent;
|
||||
static ɵcmp = ɵɵdefineComponent({
|
||||
type: WrapperComponent,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
selectors: [['wrapper']],
|
||||
decls: 1,
|
||||
vars: 0,
|
||||
template:
|
||||
function ChildComponentTemplate(rf: RenderFlags, ctx: {items: string[]}) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵcontainer(0);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
ɵɵcontainerRefreshStart(0);
|
||||
{
|
||||
const rf0 = ɵɵembeddedViewStart(0, 1, 0);
|
||||
{ showItems(rf0, {items: ctx.items}); }
|
||||
ɵɵembeddedViewEnd();
|
||||
}
|
||||
ɵɵcontainerRefreshEnd();
|
||||
}
|
||||
},
|
||||
inputs: {items: 'items'}
|
||||
});
|
||||
}
|
||||
|
||||
function template(rf: RenderFlags, ctx: {items: string[]}) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵelement(0, 'wrapper');
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
ɵɵproperty('items', ctx.items);
|
||||
}
|
||||
}
|
||||
|
||||
const defs = [WrapperComponent];
|
||||
|
||||
it('should re-render on input change', () => {
|
||||
const ctx: {items: string[]} = {items: ['a']};
|
||||
expect(renderToHtml(template, ctx, 1, 1, defs)).toEqual('<wrapper>a</wrapper>');
|
||||
|
||||
ctx.items = [...ctx.items, 'b'];
|
||||
expect(renderToHtml(template, ctx, 1, 1, defs)).toEqual('<wrapper>ab</wrapper>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('recursive components', () => {
|
||||
let events: string[];
|
||||
let count: number;
|
||||
|
||||
beforeEach(() => {
|
||||
events = [];
|
||||
count = 0;
|
||||
});
|
||||
|
||||
class TreeNode {
|
||||
constructor(
|
||||
public value: number, public depth: number, public left: TreeNode|null,
|
||||
public right: TreeNode|null) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* {{ data.value }}
|
||||
*
|
||||
* % if (data.left != null) {
|
||||
* <tree-comp [data]="data.left"></tree-comp>
|
||||
* % }
|
||||
* % if (data.right != null) {
|
||||
* <tree-comp [data]="data.right"></tree-comp>
|
||||
* % }
|
||||
*/
|
||||
class TreeComponent {
|
||||
data: TreeNode = _buildTree(0);
|
||||
|
||||
ngDoCheck() {
|
||||
events.push('check' + this.data.value);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
events.push('destroy' + this.data.value);
|
||||
}
|
||||
|
||||
static ɵfac = () => new TreeComponent();
|
||||
static ɵcmp = ɵɵdefineComponent({
|
||||
type: TreeComponent,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
selectors: [['tree-comp']],
|
||||
decls: 3,
|
||||
vars: 1,
|
||||
template:
|
||||
(rf: RenderFlags, ctx: TreeComponent) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵtext(0);
|
||||
ɵɵcontainer(1);
|
||||
ɵɵcontainer(2);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
ɵɵtextInterpolate(ctx.data.value);
|
||||
ɵɵcontainerRefreshStart(1);
|
||||
{
|
||||
if (ctx.data.left != null) {
|
||||
let rf0 = ɵɵembeddedViewStart(0, 1, 1);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
ɵɵelement(0, 'tree-comp');
|
||||
}
|
||||
if (rf0 & RenderFlags.Update) {
|
||||
ɵɵselect(0);
|
||||
ɵɵproperty('data', ctx.data.left);
|
||||
}
|
||||
ɵɵembeddedViewEnd();
|
||||
}
|
||||
}
|
||||
ɵɵcontainerRefreshEnd();
|
||||
ɵɵcontainerRefreshStart(2);
|
||||
{
|
||||
if (ctx.data.right != null) {
|
||||
let rf0 = ɵɵembeddedViewStart(0, 1, 1);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
ɵɵelement(0, 'tree-comp');
|
||||
}
|
||||
if (rf0 & RenderFlags.Update) {
|
||||
ɵɵselect(0);
|
||||
ɵɵproperty('data', ctx.data.right);
|
||||
}
|
||||
ɵɵembeddedViewEnd();
|
||||
}
|
||||
}
|
||||
ɵɵcontainerRefreshEnd();
|
||||
}
|
||||
},
|
||||
inputs: {data: 'data'}
|
||||
});
|
||||
}
|
||||
|
||||
(TreeComponent.ɵcmp as ComponentDef<TreeComponent>).directiveDefs = () => [TreeComponent.ɵcmp];
|
||||
|
||||
/**
|
||||
* {{ data.value }}
|
||||
* <ng-if-tree [data]="data.left" *ngIf="data.left"></ng-if-tree>
|
||||
* <ng-if-tree [data]="data.right" *ngIf="data.right"></ng-if-tree>
|
||||
*/
|
||||
class NgIfTree {
|
||||
data: TreeNode = _buildTree(0);
|
||||
|
||||
ngDoCheck() {
|
||||
events.push('check' + this.data.value);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
events.push('destroy' + this.data.value);
|
||||
}
|
||||
|
||||
static ɵfac = () => new NgIfTree();
|
||||
static ɵcmp = ɵɵdefineComponent({
|
||||
type: NgIfTree,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
selectors: [['ng-if-tree']],
|
||||
decls: 3,
|
||||
vars: 3,
|
||||
consts: [[AttributeMarker.Bindings, 'data', AttributeMarker.Template, 'ngIf']],
|
||||
template:
|
||||
(rf: RenderFlags, ctx: NgIfTree) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵtext(0);
|
||||
ɵɵtemplate(1, IfTemplate, 1, 1, 'ng-if-tree', 0);
|
||||
ɵɵtemplate(2, IfTemplate2, 1, 1, 'ng-if-tree', 0);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
ɵɵtextInterpolate(ctx.data.value);
|
||||
ɵɵadvance(1);
|
||||
ɵɵproperty('ngIf', ctx.data.left);
|
||||
ɵɵadvance(1);
|
||||
ɵɵproperty('ngIf', ctx.data.right);
|
||||
}
|
||||
},
|
||||
inputs: {data: 'data'},
|
||||
});
|
||||
}
|
||||
|
||||
function IfTemplate(rf: RenderFlags, left: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵelementStart(0, 'ng-if-tree');
|
||||
ɵɵelementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
const parent = ɵɵnextContext();
|
||||
ɵɵproperty('data', parent.data.left);
|
||||
}
|
||||
}
|
||||
|
||||
function IfTemplate2(rf: RenderFlags, right: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵelementStart(0, 'ng-if-tree');
|
||||
ɵɵelementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
const parent = ɵɵnextContext();
|
||||
ɵɵproperty('data', parent.data.right);
|
||||
}
|
||||
}
|
||||
|
||||
(NgIfTree.ɵcmp as ComponentDef<NgIfTree>).directiveDefs = () => [NgIfTree.ɵcmp, NgIf.ɵdir];
|
||||
|
||||
function _buildTree(currDepth: number): TreeNode {
|
||||
const children = currDepth < 2 ? _buildTree(currDepth + 1) : null;
|
||||
const children2 = currDepth < 2 ? _buildTree(currDepth + 1) : null;
|
||||
return new TreeNode(count++, currDepth, children, children2);
|
||||
}
|
||||
|
||||
it('should check each component just once', () => {
|
||||
const comp = renderComponent(TreeComponent, {hostFeatures: [LifecycleHooksFeature]});
|
||||
expect(getRenderedText(comp)).toEqual('6201534');
|
||||
expect(events).toEqual(['check6', 'check2', 'check0', 'check1', 'check5', 'check3', 'check4']);
|
||||
|
||||
events = [];
|
||||
tick(comp);
|
||||
expect(events).toEqual(['check6', 'check2', 'check0', 'check1', 'check5', 'check3', 'check4']);
|
||||
});
|
||||
|
||||
// This tests that the view tree is set up properly for recursive components
|
||||
it('should call onDestroys properly', () => {
|
||||
/**
|
||||
* % if (!skipContent) {
|
||||
* <tree-comp></tree-comp>
|
||||
* % }
|
||||
*/
|
||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵcontainer(0);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
ɵɵcontainerRefreshStart(0);
|
||||
if (!ctx.skipContent) {
|
||||
const rf0 = ɵɵembeddedViewStart(0, 1, 0);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
ɵɵelementStart(0, 'tree-comp');
|
||||
ɵɵelementEnd();
|
||||
}
|
||||
ɵɵembeddedViewEnd();
|
||||
}
|
||||
ɵɵcontainerRefreshEnd();
|
||||
}
|
||||
}, 1, 0, [TreeComponent]);
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
expect(getRenderedText(fixture.component)).toEqual('6201534');
|
||||
|
||||
events = [];
|
||||
fixture.component.skipContent = true;
|
||||
fixture.update();
|
||||
expect(events).toEqual(
|
||||
['destroy0', 'destroy1', 'destroy2', 'destroy3', 'destroy4', 'destroy5', 'destroy6']);
|
||||
});
|
||||
|
||||
it('should call onDestroys properly with ngIf', () => {
|
||||
/**
|
||||
* % if (!skipContent) {
|
||||
* <ng-if-tree></ng-if-tree>
|
||||
* % }
|
||||
*/
|
||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵcontainer(0);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
ɵɵcontainerRefreshStart(0);
|
||||
if (!ctx.skipContent) {
|
||||
const rf0 = ɵɵembeddedViewStart(0, 1, 0);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
ɵɵelementStart(0, 'ng-if-tree');
|
||||
ɵɵelementEnd();
|
||||
}
|
||||
ɵɵembeddedViewEnd();
|
||||
}
|
||||
ɵɵcontainerRefreshEnd();
|
||||
}
|
||||
}, 1, 0, [NgIfTree]);
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
expect(getRenderedText(fixture.component)).toEqual('6201534');
|
||||
expect(events).toEqual(['check6', 'check2', 'check0', 'check1', 'check5', 'check3', 'check4']);
|
||||
|
||||
events = [];
|
||||
fixture.component.skipContent = true;
|
||||
fixture.update();
|
||||
expect(events).toEqual(
|
||||
['destroy0', 'destroy1', 'destroy2', 'destroy3', 'destroy4', 'destroy5', 'destroy6']);
|
||||
});
|
||||
|
||||
it('should map inputs minified & unminified names', async () => {
|
||||
class TestInputsComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
minifiedName!: string;
|
||||
static ɵfac = () => new TestInputsComponent();
|
||||
static ɵcmp = ɵɵdefineComponent({
|
||||
type: TestInputsComponent,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
selectors: [['test-inputs']],
|
||||
inputs: {minifiedName: 'unminifiedName'},
|
||||
decls: 0,
|
||||
vars: 0,
|
||||
template: function(rf: RenderFlags, ctx: TestInputsComponent):
|
||||
void {
|
||||
// Template not needed for this test
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const testInputsComponentFactory = new ComponentFactory(TestInputsComponent.ɵcmp);
|
||||
|
||||
expect([
|
||||
{propName: 'minifiedName', templateName: 'unminifiedName'}
|
||||
]).toEqual(testInputsComponentFactory.inputs);
|
||||
});
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user