refactor(ivy): split up directiveCreate for tree shaking (#22838)
PR Close #22838
This commit is contained in:
parent
1612985e48
commit
e27cfd6236
|
@ -12,8 +12,8 @@ import {Injector} from '../di/injector';
|
|||
import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
|
||||
|
||||
import {assertNotNull} from './assert';
|
||||
import {queueLifecycleHooks} from './hooks';
|
||||
import {CLEAN_PROMISE, _getComponentHostLElementNode, createLView, createTView, directiveCreate, enterView, getDirectiveInstance, getRootView, hostElement, initChangeDetectorIfExisting, locateHostElement, renderComponentOrTemplate} from './instructions';
|
||||
import {queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||
import {CLEAN_PROMISE, _getComponentHostLElementNode, baseDirectiveCreate, createLView, createTView, enterView, getRootView, hostElement, initChangeDetectorIfExisting, locateHostElement, renderComponentOrTemplate} from './instructions';
|
||||
import {ComponentDef, ComponentType} from './interfaces/definition';
|
||||
import {LElementNode} from './interfaces/node';
|
||||
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||
|
@ -22,6 +22,7 @@ import {stringify} from './util';
|
|||
import {createViewRef} from './view_ref';
|
||||
|
||||
|
||||
|
||||
/** Options that control how the component should be bootstrapped. */
|
||||
export interface CreateComponentOptions {
|
||||
/** Which renderer factory to use. */
|
||||
|
@ -135,7 +136,7 @@ export function renderComponent<T>(
|
|||
// Create element node at index 0 in data array
|
||||
elementNode = hostElement(hostNode, componentDef);
|
||||
// Create directive instance with n() and store at index 1 in data array (el is 0)
|
||||
component = rootContext.component = directiveCreate(1, componentDef.factory(), componentDef) as T;
|
||||
component = rootContext.component = baseDirectiveCreate(1, componentDef.factory(), componentDef) as T;
|
||||
initChangeDetectorIfExisting(elementNode.nodeInjector, component);
|
||||
} finally {
|
||||
// We must not use leaveView here because it will set creationMode to false too early,
|
||||
|
@ -164,6 +165,9 @@ export function renderComponent<T>(
|
|||
*/
|
||||
export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): void {
|
||||
const elementNode = _getComponentHostLElementNode(component);
|
||||
|
||||
// Root component is always created at dir index 1, after host element at 0
|
||||
queueInitHooks(1, def.onInit, def.doCheck, elementNode.view.tView);
|
||||
queueLifecycleHooks(elementNode.flags, elementNode.view);
|
||||
}
|
||||
|
||||
|
|
|
@ -1096,6 +1096,34 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
|
|||
export function directiveCreate<T>(
|
||||
index: number, directive: T, directiveDef: DirectiveDef<T>,
|
||||
localNames?: (string | number)[] | null): T {
|
||||
const instance = baseDirectiveCreate(index, directive, directiveDef);
|
||||
|
||||
ngDevMode && assertNotNull(previousOrParentNode.tNode, 'previousOrParentNode.tNode');
|
||||
const tNode: TNode|null = previousOrParentNode.tNode !;
|
||||
|
||||
if (currentView.tView.firstTemplatePass && localNames) {
|
||||
tNode.localNames = tNode.localNames ? tNode.localNames.concat(localNames) : localNames;
|
||||
}
|
||||
|
||||
if (tNode && tNode.attrs) {
|
||||
setInputsFromAttrs<T>(instance, directiveDef !.inputs, tNode);
|
||||
}
|
||||
|
||||
// Init hooks are queued now so ngOnInit is called in host components before
|
||||
// any projected components.
|
||||
queueInitHooks(index, directiveDef.onInit, directiveDef.doCheck, currentView.tView);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* A lighter version of directiveCreate() that is used for the root component
|
||||
*
|
||||
* This version does not contain features that we don't already support at root in
|
||||
* current Angular. Example: local refs and inputs on root component.
|
||||
*/
|
||||
export function baseDirectiveCreate<T>(
|
||||
index: number, directive: T, directiveDef: DirectiveDef<T>): T {
|
||||
let instance;
|
||||
ngDevMode &&
|
||||
assertNull(currentView.bindingStartIndex, 'directives should be created before any bindings');
|
||||
|
@ -1117,11 +1145,6 @@ export function directiveCreate<T>(
|
|||
|
||||
if (index >= tData.length) {
|
||||
tData[index] = directiveDef !;
|
||||
if (localNames) {
|
||||
ngDevMode && assertNotNull(previousOrParentNode.tNode, 'previousOrParentNode.tNode');
|
||||
const tNode = previousOrParentNode !.tNode !;
|
||||
tNode.localNames = tNode.localNames ? tNode.localNames.concat(localNames) : localNames;
|
||||
}
|
||||
}
|
||||
|
||||
const diPublic = directiveDef !.diPublic;
|
||||
|
@ -1135,15 +1158,6 @@ export function directiveCreate<T>(
|
|||
(previousOrParentNode as LElementNode).native, directiveDef !.attributes as string[]);
|
||||
}
|
||||
|
||||
const tNode: TNode|null = previousOrParentNode.tNode !;
|
||||
if (tNode && tNode.attrs) {
|
||||
setInputsFromAttrs<T>(instance, directiveDef !.inputs, tNode);
|
||||
}
|
||||
|
||||
// Init hooks are queued now so ngOnInit is called in host components before
|
||||
// any projected components.
|
||||
queueInitHooks(index, directiveDef.onInit, directiveDef.doCheck, currentView.tView);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
{
|
||||
"name": "appendChild"
|
||||
},
|
||||
{
|
||||
"name": "baseDirectiveCreate"
|
||||
},
|
||||
{
|
||||
"name": "bindingUpdated"
|
||||
},
|
||||
|
@ -74,9 +77,6 @@
|
|||
{
|
||||
"name": "detectChangesInternal"
|
||||
},
|
||||
{
|
||||
"name": "directiveCreate"
|
||||
},
|
||||
{
|
||||
"name": "domRendererFactory3"
|
||||
},
|
||||
|
@ -98,9 +98,6 @@
|
|||
{
|
||||
"name": "findNextRNodeSibling"
|
||||
},
|
||||
{
|
||||
"name": "generateInitialInputs"
|
||||
},
|
||||
{
|
||||
"name": "getDirectiveInstance"
|
||||
},
|
||||
|
@ -146,9 +143,6 @@
|
|||
{
|
||||
"name": "locateHostElement"
|
||||
},
|
||||
{
|
||||
"name": "queueInitHooks"
|
||||
},
|
||||
{
|
||||
"name": "refreshChildComponents"
|
||||
},
|
||||
|
@ -179,9 +173,6 @@
|
|||
{
|
||||
"name": "setHostBindings"
|
||||
},
|
||||
{
|
||||
"name": "setInputsFromAttrs"
|
||||
},
|
||||
{
|
||||
"name": "setUpAttributes"
|
||||
},
|
||||
|
|
|
@ -10,7 +10,7 @@ import {withBody} from '@angular/core/testing';
|
|||
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck} from '../../src/core';
|
||||
import {getRenderedText, whenRendered} from '../../src/render3/component';
|
||||
import {defineComponent, defineDirective, injectChangeDetectorRef} from '../../src/render3/index';
|
||||
import {LifecycleHooksFeature, defineComponent, defineDirective, injectChangeDetectorRef} from '../../src/render3/index';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, detectChanges, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, listener, markDirty, text, textBinding, tick} from '../../src/render3/instructions';
|
||||
|
||||
import {containerEl, renderComponent, requestAnimationFrame} from './render_util';
|
||||
|
@ -39,7 +39,7 @@ describe('change detection', () => {
|
|||
}
|
||||
|
||||
it('should mark a component dirty and schedule change detection', withBody('my-comp', () => {
|
||||
const myComp = renderComponent(MyComponent);
|
||||
const myComp = renderComponent(MyComponent, {hostFeatures: [LifecycleHooksFeature]});
|
||||
expect(getRenderedText(myComp)).toEqual('works');
|
||||
myComp.value = 'updated';
|
||||
markDirty(myComp);
|
||||
|
@ -49,7 +49,7 @@ describe('change detection', () => {
|
|||
}));
|
||||
|
||||
it('should detectChanges on a component', withBody('my-comp', () => {
|
||||
const myComp = renderComponent(MyComponent);
|
||||
const myComp = renderComponent(MyComponent, {hostFeatures: [LifecycleHooksFeature]});
|
||||
expect(getRenderedText(myComp)).toEqual('works');
|
||||
myComp.value = 'updated';
|
||||
detectChanges(myComp);
|
||||
|
@ -58,7 +58,7 @@ describe('change detection', () => {
|
|||
|
||||
it('should detectChanges only once if markDirty is called multiple times',
|
||||
withBody('my-comp', () => {
|
||||
const myComp = renderComponent(MyComponent);
|
||||
const myComp = renderComponent(MyComponent, {hostFeatures: [LifecycleHooksFeature]});
|
||||
expect(getRenderedText(myComp)).toEqual('works');
|
||||
expect(myComp.doCheckCount).toBe(1);
|
||||
myComp.value = 'ignore';
|
||||
|
@ -72,7 +72,7 @@ describe('change detection', () => {
|
|||
}));
|
||||
|
||||
it('should notify whenRendered', withBody('my-comp', async() => {
|
||||
const myComp = renderComponent(MyComponent);
|
||||
const myComp = renderComponent(MyComponent, {hostFeatures: [LifecycleHooksFeature]});
|
||||
await whenRendered(myComp);
|
||||
myComp.value = 'updated';
|
||||
markDirty(myComp);
|
||||
|
@ -347,7 +347,7 @@ describe('change detection', () => {
|
|||
|
||||
it('should check the component view when called by component (even when OnPush && clean)',
|
||||
() => {
|
||||
const comp = renderComponent(MyComp);
|
||||
const comp = renderComponent(MyComp, {hostFeatures: [LifecycleHooksFeature]});
|
||||
expect(getRenderedText(comp)).toEqual('Nancy');
|
||||
|
||||
comp.name = 'Bess'; // as this is not an Input, the component stays clean
|
||||
|
@ -356,7 +356,7 @@ describe('change detection', () => {
|
|||
});
|
||||
|
||||
it('should NOT call component doCheck when called by a component', () => {
|
||||
const comp = renderComponent(MyComp);
|
||||
const comp = renderComponent(MyComp, {hostFeatures: [LifecycleHooksFeature]});
|
||||
expect(comp.doCheckCount).toEqual(1);
|
||||
|
||||
// NOTE: in current Angular, detectChanges does not itself trigger doCheck, but you
|
||||
|
@ -367,7 +367,7 @@ describe('change detection', () => {
|
|||
});
|
||||
|
||||
it('should NOT check the component parent when called by a child component', () => {
|
||||
const parentComp = renderComponent(ParentComp);
|
||||
const parentComp = renderComponent(ParentComp, {hostFeatures: [LifecycleHooksFeature]});
|
||||
expect(getRenderedText(parentComp)).toEqual('1 - Nancy');
|
||||
|
||||
parentComp.doCheckCount = 100;
|
||||
|
@ -378,7 +378,7 @@ describe('change detection', () => {
|
|||
|
||||
it('should check component children when called by component if dirty or check-always',
|
||||
() => {
|
||||
const parentComp = renderComponent(ParentComp);
|
||||
const parentComp = renderComponent(ParentComp, {hostFeatures: [LifecycleHooksFeature]});
|
||||
expect(parentComp.doCheckCount).toEqual(1);
|
||||
|
||||
myComp.name = 'Bess';
|
||||
|
@ -390,7 +390,7 @@ describe('change detection', () => {
|
|||
});
|
||||
|
||||
it('should not group detectChanges calls (call every time)', () => {
|
||||
const parentComp = renderComponent(ParentComp);
|
||||
const parentComp = renderComponent(ParentComp, {hostFeatures: [LifecycleHooksFeature]});
|
||||
expect(myComp.doCheckCount).toEqual(1);
|
||||
|
||||
parentComp.cdr.detectChanges();
|
||||
|
@ -524,7 +524,7 @@ describe('change detection', () => {
|
|||
});
|
||||
}
|
||||
|
||||
const comp = renderComponent(DetectChangesComp);
|
||||
const comp = renderComponent(DetectChangesComp, {hostFeatures: [LifecycleHooksFeature]});
|
||||
expect(getRenderedText(comp)).toEqual('1');
|
||||
});
|
||||
|
||||
|
@ -553,7 +553,7 @@ describe('change detection', () => {
|
|||
});
|
||||
}
|
||||
|
||||
const comp = renderComponent(DetectChangesComp);
|
||||
const comp = renderComponent(DetectChangesComp, {hostFeatures: [LifecycleHooksFeature]});
|
||||
expect(getRenderedText(comp)).toEqual('1');
|
||||
});
|
||||
|
||||
|
@ -940,7 +940,7 @@ describe('change detection', () => {
|
|||
}
|
||||
|
||||
it('should throw if bindings in current view have changed', () => {
|
||||
const comp = renderComponent(NoChangesComp);
|
||||
const comp = renderComponent(NoChangesComp, {hostFeatures: [LifecycleHooksFeature]});
|
||||
|
||||
expect(() => comp.cdr.checkNoChanges()).not.toThrow();
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {defineComponent, defineDirective} from '../../src/render3/index';
|
||||
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, elementAttribute, elementClassNamed, elementEnd, elementProperty, elementStart, elementStyleNamed, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
|
||||
|
||||
import {containerEl, renderToHtml} from './render_util';
|
||||
import {ComponentFixture, containerEl, renderToHtml} from './render_util';
|
||||
|
||||
describe('render3 integration test', () => {
|
||||
|
||||
|
@ -269,6 +269,21 @@ describe('render3 integration test', () => {
|
|||
expect(renderToHtml(Template, {})).toEqual('<todo title="two">two</todo>');
|
||||
});
|
||||
|
||||
it('should support root component with host attribute', () => {
|
||||
class HostAttributeComp {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: HostAttributeComp,
|
||||
tag: 'host-attr-comp',
|
||||
factory: () => new HostAttributeComp(),
|
||||
template: (ctx: HostAttributeComp, cm: boolean) => {},
|
||||
attributes: ['role', 'button']
|
||||
});
|
||||
}
|
||||
|
||||
const fixture = new ComponentFixture(HostAttributeComp);
|
||||
expect(fixture.hostElement.getAttribute('role')).toEqual('button');
|
||||
});
|
||||
|
||||
it('should support component with bindings in template', () => {
|
||||
/** <p> {{ name }} </p>*/
|
||||
class MyComp {
|
||||
|
|
Loading…
Reference in New Issue