refactor(ivy): merge directives into LViewData (#26316)
PR Close #26316
This commit is contained in:
parent
b0879046b7
commit
7ea5161d4d
|
@ -588,7 +588,7 @@ describe('compiler compliance', () => {
|
|||
selectors: [["", "hostBindingDir", ""]],
|
||||
factory: function HostBindingDir_Factory(t) { return new (t || HostBindingDir)(); },
|
||||
hostBindings: function HostBindingDir_HostBindings(dirIndex, elIndex) {
|
||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵloadDirective(dirIndex).dirId));
|
||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵload(dirIndex).dirId));
|
||||
},
|
||||
hostVars: 1,
|
||||
features: [$r3$.ɵPublicFeature]
|
||||
|
@ -632,7 +632,7 @@ describe('compiler compliance', () => {
|
|||
selectors: [["host-binding-comp"]],
|
||||
factory: function HostBindingComp_Factory(t) { return new (t || HostBindingComp)(); },
|
||||
hostBindings: function HostBindingComp_HostBindings(dirIndex, elIndex) {
|
||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵpureFunction1(1, $ff$, $r3$.ɵloadDirective(dirIndex).id)));
|
||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵpureFunction1(1, $ff$, $r3$.ɵload(dirIndex).id)));
|
||||
},
|
||||
hostVars: 3,
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
|
@ -1343,7 +1343,7 @@ describe('compiler compliance', () => {
|
|||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false));
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
const instance = $r3$.ɵloadDirective(dirIndex);
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
var $tmp$;
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && ($instance$.someDir = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && ($instance$.someDirList = $tmp$));
|
||||
|
@ -1403,7 +1403,7 @@ describe('compiler compliance', () => {
|
|||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, ["myRef1", "myRef2", "myRef3"], false));
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
const instance = $r3$.ɵloadDirective(dirIndex);
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
var $tmp$;
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && (instance.myRefs = $tmp$));
|
||||
|
@ -1452,7 +1452,7 @@ describe('compiler compliance', () => {
|
|||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, ["myRef1", "myRef2", "myRef3"], false, ElementRef));
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
const instance = $r3$.ɵloadDirective(dirIndex);
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
var $tmp$;
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && (instance.myRefs = $tmp$));
|
||||
|
|
|
@ -463,17 +463,14 @@ describe('ngtsc behavioral tests', () => {
|
|||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents)
|
||||
.toContain(
|
||||
`i0.ɵelementProperty(elIndex, "attr.hello", i0.ɵbind(i0.ɵloadDirective(dirIndex).foo));`);
|
||||
.toContain(`i0.ɵelementProperty(elIndex, "attr.hello", i0.ɵbind(i0.ɵload(dirIndex).foo));`);
|
||||
expect(jsContents)
|
||||
.toContain(`i0.ɵelementProperty(elIndex, "prop", i0.ɵbind(i0.ɵload(dirIndex).bar));`);
|
||||
expect(jsContents)
|
||||
.toContain(
|
||||
`i0.ɵelementProperty(elIndex, "prop", i0.ɵbind(i0.ɵloadDirective(dirIndex).bar));`);
|
||||
expect(jsContents)
|
||||
.toContain(
|
||||
'i0.ɵelementProperty(elIndex, "class.someclass", i0.ɵbind(i0.ɵloadDirective(dirIndex).someClass))');
|
||||
expect(jsContents).toContain('i0.ɵloadDirective(dirIndex).onClick($event)');
|
||||
expect(jsContents)
|
||||
.toContain('i0.ɵloadDirective(dirIndex).onChange(i0.ɵloadDirective(dirIndex).arg)');
|
||||
'i0.ɵelementProperty(elIndex, "class.someclass", i0.ɵbind(i0.ɵload(dirIndex).someClass))');
|
||||
expect(jsContents).toContain('i0.ɵload(dirIndex).onClick($event)');
|
||||
expect(jsContents).toContain('i0.ɵload(dirIndex).onChange(i0.ɵload(dirIndex).arg)');
|
||||
});
|
||||
|
||||
it('should correctly recognize local symbols', () => {
|
||||
|
|
|
@ -96,7 +96,6 @@ export class Identifiers {
|
|||
static pipeBindV: o.ExternalReference = {name: 'ɵpipeBindV', moduleName: CORE};
|
||||
|
||||
static load: o.ExternalReference = {name: 'ɵload', moduleName: CORE};
|
||||
static loadDirective: o.ExternalReference = {name: 'ɵloadDirective', moduleName: CORE};
|
||||
static loadQueryList: o.ExternalReference = {name: 'ɵloadQueryList', moduleName: CORE};
|
||||
|
||||
static pipe: o.ExternalReference = {name: 'ɵpipe', moduleName: CORE};
|
||||
|
|
|
@ -473,10 +473,9 @@ function createContentQueriesRefreshFunction(meta: R3DirectiveMetadata): o.Expre
|
|||
// var $tmp$: any;
|
||||
const temporary = temporaryAllocator(statements, TEMPORARY_NAME);
|
||||
|
||||
// const $instance$ = $r3$.ɵloadDirective(dirIndex);
|
||||
statements.push(
|
||||
directiveInstanceVar.set(o.importExpr(R3.loadDirective).callFn([o.variable('dirIndex')]))
|
||||
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
|
||||
// const $instance$ = $r3$.ɵload(dirIndex);
|
||||
statements.push(directiveInstanceVar.set(o.importExpr(R3.load).callFn([o.variable('dirIndex')]))
|
||||
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
|
||||
|
||||
meta.queries.forEach((query: R3QueryMetadata, idx: number) => {
|
||||
const loadQLArg = o.variable('queryStartIndex');
|
||||
|
@ -580,7 +579,7 @@ function createHostBindingsFunction(
|
|||
|
||||
// Calculate the host property bindings
|
||||
const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
|
||||
const bindingContext = o.importExpr(R3.loadDirective).callFn([o.variable('dirIndex')]);
|
||||
const bindingContext = o.importExpr(R3.load).callFn([o.variable('dirIndex')]);
|
||||
if (bindings) {
|
||||
const valueConverter = new ValueConverter(
|
||||
constantPool,
|
||||
|
|
|
@ -47,7 +47,6 @@ export {
|
|||
embeddedViewStart as ɵembeddedViewStart,
|
||||
query as ɵquery,
|
||||
registerContentQuery as ɵregisterContentQuery,
|
||||
loadDirective as ɵloadDirective,
|
||||
projection as ɵprojection,
|
||||
bind as ɵbind,
|
||||
interpolation1 as ɵinterpolation1,
|
||||
|
|
|
@ -128,8 +128,6 @@ NOTE:
|
|||
|
||||
## `EXPANDO`
|
||||
|
||||
*TODO*: This section is to be implemented.
|
||||
|
||||
`EXPANDO` contains information on data which size is not known at compile time.
|
||||
Examples include:
|
||||
- `Component`/`Directives` since we don't know at compile time which directives will match.
|
||||
|
@ -203,7 +201,7 @@ The `EXPANDO` section needs additional information for information stored in `TV
|
|||
|
||||
| Index | `TView.expandoInstructions` | Meaning
|
||||
| ----: | ---------------------------: | -------
|
||||
| 0 | -10 | Negative numbers signifies pointers to elements. In this case 10 (`<child>`)
|
||||
| 0 | -10 | Negative numbers signify pointers to elements. In this case 10 (`<child>`)
|
||||
| 1 | 2 | Injector size. Number of values to skip to get to Host Bindings.
|
||||
| 2 | Child.ngComponentDef.hostBindings | The function to call. (Only when `hostVars` is not `0`)
|
||||
| 3 | Child.ngComponentDef.hostVars | Number of host bindings to process. (Only when `hostVars` is not `0`)
|
||||
|
@ -215,9 +213,9 @@ The reason for this layout is to make the host binding update efficient using th
|
|||
let currentDirectiveIndex = -1;
|
||||
let currentElementIndex = -1;
|
||||
// This is global state which is used internally by hostBindings to know where the offset is
|
||||
let bindingRootIndex = tView.expandoStart;
|
||||
for(var i = 0; i < tview.expandoInstructions.length; i++) {
|
||||
let instruction = tview.expandoInstructions[i];
|
||||
let bindingRootIndex = tView.expandoStartIndex;
|
||||
for(var i = 0; i < tView.expandoInstructions.length; i++) {
|
||||
let instruction = tView.expandoInstructions[i];
|
||||
if (typeof instruction === 'number') {
|
||||
// Numbers are used to update the indices.
|
||||
if (instruction < 0) {
|
||||
|
|
|
@ -16,11 +16,10 @@ import {assertComponentType, assertDefined} from './assert';
|
|||
import {getLElementFromComponent, readPatchedLViewData} from './context_discovery';
|
||||
import {getComponentDef} from './definition';
|
||||
import {queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||
import {PlayerHandler} from './interfaces/player';
|
||||
|
||||
import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, hostElement, leaveView, locateHostElement, setHostBindings, queueHostBindingForCheck,} from './instructions';
|
||||
import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, hostElement, leaveView, locateHostElement, prefillHostVars, setHostBindings} from './instructions';
|
||||
import {ComponentDef, ComponentType} from './interfaces/definition';
|
||||
import {LElementNode} from './interfaces/node';
|
||||
import {LElementNode, TNodeFlags} from './interfaces/node';
|
||||
import {PlayerHandler} from './interfaces/player';
|
||||
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||
import {CONTEXT, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||
import {getRootView, stringify} from './util';
|
||||
|
@ -151,15 +150,16 @@ export function renderComponent<T>(
|
|||
export function createRootComponent<T>(
|
||||
elementNode: LElementNode, componentDef: ComponentDef<T>, rootView: LViewData,
|
||||
rootContext: RootContext, hostFeatures: HostFeature[] | null): any {
|
||||
// Create directive instance with factory() and store at index 0 in directives array
|
||||
const component = baseDirectiveCreate(0, componentDef.factory() as T, componentDef, elementNode);
|
||||
// Create directive instance with factory() and store at next index in viewData
|
||||
const component =
|
||||
baseDirectiveCreate(rootView.length, componentDef.factory() as T, componentDef, elementNode);
|
||||
|
||||
if (componentDef.hostBindings) queueHostBindingForCheck(0, componentDef.hostVars);
|
||||
rootContext.components.push(component);
|
||||
(elementNode.data as LViewData)[CONTEXT] = component;
|
||||
|
||||
hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef));
|
||||
setHostBindings(rootView[TVIEW].hostBindings);
|
||||
if (rootView[TVIEW].firstTemplatePass) prefillHostVars(componentDef.hostVars);
|
||||
setHostBindings();
|
||||
return component;
|
||||
}
|
||||
|
||||
|
@ -190,11 +190,10 @@ export function createRootContext(
|
|||
*/
|
||||
export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): void {
|
||||
const rootTView = readPatchedLViewData(component) ![TVIEW];
|
||||
const dirIndex = rootTView.data.length - 1;
|
||||
|
||||
// Root component is always created at dir index 0
|
||||
queueInitHooks(0, def.onInit, def.doCheck, rootTView);
|
||||
// Directive starting index 0, directive count 1 -> directive flags: 1
|
||||
queueLifecycleHooks(1, rootTView);
|
||||
queueInitHooks(dirIndex, def.onInit, def.doCheck, rootTView);
|
||||
queueLifecycleHooks(dirIndex << TNodeFlags.DirectiveStartingIndexShift | 1, rootTView);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -139,13 +139,6 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
|||
|
||||
// Create element node at index 0 in data array
|
||||
elementNode = hostElement(componentTag, hostNode, this.componentDef);
|
||||
|
||||
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
|
||||
// executed here?
|
||||
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
|
||||
component = createRootComponent(
|
||||
elementNode, this.componentDef, rootView, rootContext, [LifecycleHooksFeature]);
|
||||
|
||||
tElementNode = getTNode(0) as TElementNode;
|
||||
|
||||
// Transform the arrays of native nodes into a LNode structure that can be consumed by the
|
||||
|
@ -168,6 +161,12 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
|
||||
// executed here?
|
||||
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
|
||||
component = createRootComponent(
|
||||
elementNode, this.componentDef, rootView, rootContext, [LifecycleHooksFeature]);
|
||||
|
||||
// Execute the template in creation mode only, and then turn off the CreationMode flag
|
||||
const componentView = elementNode.data as LViewData;
|
||||
renderEmbeddedTemplate(componentView, componentView[TVIEW], component, RenderFlags.Create);
|
||||
|
|
|
@ -10,7 +10,7 @@ import './ng_dev_mode';
|
|||
import {assertEqual} from './assert';
|
||||
import {LElementNode, TNode, TNodeFlags} from './interfaces/node';
|
||||
import {RElement} from './interfaces/renderer';
|
||||
import {CONTEXT, DIRECTIVES, HEADER_OFFSET, LViewData, TVIEW} from './interfaces/view';
|
||||
import {CONTEXT, HEADER_OFFSET, LViewData, TVIEW} from './interfaces/view';
|
||||
|
||||
/**
|
||||
* This property will be monkey-patched on elements, components and directives
|
||||
|
@ -306,21 +306,17 @@ function findViaDirective(lViewData: LViewData, directiveInstance: {}): number {
|
|||
// element bound to the directive being search lives somewhere
|
||||
// in the view data. We loop through the nodes and check their
|
||||
// list of directives for the instance.
|
||||
const directivesAcrossView = lViewData[DIRECTIVES];
|
||||
let tNode = lViewData[TVIEW].firstChild;
|
||||
if (directivesAcrossView != null) {
|
||||
while (tNode) {
|
||||
const directiveIndexStart = getDirectiveStartIndex(tNode);
|
||||
const directiveIndexEnd = getDirectiveEndIndex(tNode, directiveIndexStart);
|
||||
for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
|
||||
if (directivesAcrossView[i] === directiveInstance) {
|
||||
return tNode.index;
|
||||
}
|
||||
while (tNode) {
|
||||
const directiveIndexStart = getDirectiveStartIndex(tNode);
|
||||
const directiveIndexEnd = getDirectiveEndIndex(tNode, directiveIndexStart);
|
||||
for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
|
||||
if (lViewData[i] === directiveInstance) {
|
||||
return tNode.index;
|
||||
}
|
||||
tNode = traverseNextElement(tNode);
|
||||
}
|
||||
tNode = traverseNextElement(tNode);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -341,24 +337,20 @@ function getLNodeFromViewData(lViewData: LViewData, lElementIndex: number): LEle
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a list of directives extracted from the given view. Does not contain
|
||||
* the component.
|
||||
* Returns a list of directives extracted from the given view based on the
|
||||
* provided list of directive index values.
|
||||
*
|
||||
* @param nodeIndex Index of node to search
|
||||
* @param nodeIndex The node index
|
||||
* @param lViewData The target view data
|
||||
* @param includeComponents Whether or not to include components in returned directives
|
||||
*/
|
||||
export function discoverDirectives(
|
||||
nodeIndex: number, lViewData: LViewData, includeComponents: boolean): any[]|null {
|
||||
const directivesAcrossView = lViewData[DIRECTIVES];
|
||||
if (directivesAcrossView != null) {
|
||||
const tNode = lViewData[TVIEW].data[nodeIndex] as TNode;
|
||||
let directiveStartIndex = getDirectiveStartIndex(tNode);
|
||||
const directiveEndIndex = getDirectiveEndIndex(tNode, directiveStartIndex);
|
||||
if (!includeComponents && tNode.flags & TNodeFlags.isComponent) directiveStartIndex++;
|
||||
return directivesAcrossView.slice(directiveStartIndex, directiveEndIndex);
|
||||
}
|
||||
return null;
|
||||
const tNode = lViewData[TVIEW].data[nodeIndex] as TNode;
|
||||
let directiveStartIndex = getDirectiveStartIndex(tNode);
|
||||
const directiveEndIndex = getDirectiveEndIndex(tNode, directiveStartIndex);
|
||||
if (!includeComponents && tNode.flags & TNodeFlags.isComponent) directiveStartIndex++;
|
||||
return lViewData.slice(directiveStartIndex, directiveEndIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -375,7 +367,7 @@ export function discoverLocalRefs(lViewData: LViewData, lNodeIndex: number): {[k
|
|||
const directiveIndex = tNode.localNames[i + 1] as number;
|
||||
result[localRefName] = directiveIndex === -1 ?
|
||||
getLNodeFromViewData(lViewData, lNodeIndex) !.native :
|
||||
lViewData[DIRECTIVES] ![directiveIndex];
|
||||
lViewData[directiveIndex];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ class Render3DebugContext implements DebugContext {
|
|||
// TODO(vicb): add view providers when supported
|
||||
get providerTokens(): any[] {
|
||||
// TODO(vicb): why/when
|
||||
const directiveDefs = this.view[TVIEW].directives;
|
||||
const directiveDefs = this.view[TVIEW].data;
|
||||
if (this.nodeIndex === null || directiveDefs == null) {
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -20,10 +20,10 @@ import {getComponentDef, getDirectiveDef, getPipeDef} from './definition';
|
|||
import {NG_ELEMENT_ID} from './fields';
|
||||
import {_getViewData, assertPreviousIsParent, getPreviousOrParentTNode, resolveDirective, setEnvironment} from './instructions';
|
||||
import {DirectiveDef} from './interfaces/definition';
|
||||
import {INJECTOR_SIZE, InjectorLocationFlags, PARENT_INJECTOR, TNODE,} from './interfaces/injector';
|
||||
import {InjectorLocationFlags, PARENT_INJECTOR, TNODE,} from './interfaces/injector';
|
||||
import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||
import {isProceduralRenderer} from './interfaces/renderer';
|
||||
import {DECLARATION_VIEW, DIRECTIVES, HOST_NODE, INJECTOR, LViewData, PARENT, RENDERER, TData, TVIEW, TView} from './interfaces/view';
|
||||
import {DECLARATION_VIEW, HOST_NODE, INJECTOR, LViewData, PARENT, RENDERER, TData, TVIEW, TView} from './interfaces/view';
|
||||
import {assertNodeOfPossibleTypes} from './node_assert';
|
||||
|
||||
/**
|
||||
|
@ -104,12 +104,10 @@ export function getOrCreateNodeInjectorForNode(
|
|||
|
||||
const tView = hostView[TVIEW];
|
||||
if (tView.firstTemplatePass) {
|
||||
// TODO(kara): Store node injector with host bindings for that node (see VIEW_DATA.md)
|
||||
tNode.injectorIndex = hostView.length;
|
||||
setUpBloom(tView.data, tNode); // foundation for node bloom
|
||||
setUpBloom(hostView, null); // foundation for cumulative bloom
|
||||
setUpBloom(tView.blueprint, null);
|
||||
tView.hostBindingStartIndex += INJECTOR_SIZE;
|
||||
}
|
||||
|
||||
const parentLoc = getParentInjectorLocation(tNode, hostView);
|
||||
|
@ -244,7 +242,9 @@ export function directiveInject<T>(token: Type<T>| InjectionToken<T>): T;
|
|||
export function directiveInject<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags): T;
|
||||
export function directiveInject<T>(
|
||||
token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
|
||||
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), _getViewData(), token, flags);
|
||||
const hostTNode =
|
||||
getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode;
|
||||
return getOrCreateInjectable<T>(hostTNode, _getViewData(), token, flags);
|
||||
}
|
||||
|
||||
export function injectRenderer2(): Renderer2 {
|
||||
|
@ -320,8 +320,8 @@ function getOrCreateRenderer2(view: LViewData): Renderer2 {
|
|||
* @returns the value from the injector or `null` when not found
|
||||
*/
|
||||
export function getOrCreateInjectable<T>(
|
||||
startInjectorIndex: number, hostView: LViewData, token: Type<T>| InjectionToken<T>,
|
||||
flags: InjectFlags = InjectFlags.Default): T|null {
|
||||
hostTNode: TElementNode | TContainerNode | TElementContainerNode, hostView: LViewData,
|
||||
token: Type<T>| InjectionToken<T>, flags: InjectFlags = InjectFlags.Default): T|null {
|
||||
const bloomHash = bloomHashBitOrFactory(token);
|
||||
// If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
|
||||
// so just call the factory function to create it.
|
||||
|
@ -330,13 +330,24 @@ export function getOrCreateInjectable<T>(
|
|||
// If the token has a bloom hash, then it is a directive that is public to the injection system
|
||||
// (diPublic) otherwise fall back to the module injector.
|
||||
if (bloomHash != null) {
|
||||
const startInjectorIndex = getInjectorIndex(hostTNode, hostView);
|
||||
|
||||
let injectorIndex = startInjectorIndex;
|
||||
let injectorView = hostView;
|
||||
let parentLocation: number = -1;
|
||||
|
||||
if (flags & InjectFlags.SkipSelf) {
|
||||
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
|
||||
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
|
||||
injectorView = getParentInjectorView(parentLocation, injectorView);
|
||||
// If we should skip this injector or if an injector doesn't exist on this node (e.g. all
|
||||
// directives on this node are private), start by searching the parent injector.
|
||||
if (flags & InjectFlags.SkipSelf || injectorIndex === -1) {
|
||||
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(hostTNode, hostView) :
|
||||
injectorView[injectorIndex + PARENT_INJECTOR];
|
||||
|
||||
if (shouldNotSearchParent(flags, parentLocation)) {
|
||||
injectorIndex = -1;
|
||||
} else {
|
||||
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
|
||||
injectorView = getParentInjectorView(parentLocation, injectorView);
|
||||
}
|
||||
}
|
||||
|
||||
while (injectorIndex !== -1) {
|
||||
|
@ -348,9 +359,8 @@ export function getOrCreateInjectable<T>(
|
|||
break;
|
||||
}
|
||||
|
||||
if (flags & InjectFlags.Self ||
|
||||
flags & InjectFlags.Host &&
|
||||
!sameHostView(injectorView[injectorIndex + PARENT_INJECTOR])) {
|
||||
parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
|
||||
if (shouldNotSearchParent(flags, parentLocation)) {
|
||||
injectorIndex = -1;
|
||||
break;
|
||||
}
|
||||
|
@ -359,7 +369,6 @@ export function getOrCreateInjectable<T>(
|
|||
// up to find the specific injector. If the ancestor bloom filter does not have the bit, we
|
||||
// can abort.
|
||||
if (injectorHasToken(bloomHash, injectorIndex, injectorView)) {
|
||||
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
|
||||
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
|
||||
injectorView = getParentInjectorView(parentLocation, injectorView);
|
||||
} else {
|
||||
|
@ -390,7 +399,6 @@ export function getOrCreateInjectable<T>(
|
|||
|
||||
// The def wasn't found anywhere on this node, so it was a false positive.
|
||||
// Traverse up the tree and continue searching.
|
||||
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
|
||||
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
|
||||
injectorView = getParentInjectorView(parentLocation, injectorView);
|
||||
}
|
||||
|
@ -411,7 +419,7 @@ function searchMatchesQueuedForCreation<T>(token: any, hostTView: TView): T|null
|
|||
for (let i = 0; i < matches.length; i += 2) {
|
||||
const def = matches[i] as DirectiveDef<any>;
|
||||
if (def.type === token) {
|
||||
return resolveDirective(def, i + 1, matches, hostTView);
|
||||
return resolveDirective(def, i + 1, matches);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -427,14 +435,14 @@ function searchDirectivesOnInjector<T>(
|
|||
if (count !== 0) {
|
||||
const start = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const end = start + count;
|
||||
const defs = injectorView[TVIEW].directives !;
|
||||
const defs = injectorView[TVIEW].data;
|
||||
|
||||
for (let i = start; i < end; i++) {
|
||||
// Get the definition for the directive at this index and, if it is injectable (diPublic),
|
||||
// and matches the given token, return the directive instance.
|
||||
const directiveDef = defs[i] as DirectiveDef<any>;
|
||||
if (directiveDef.type === token && directiveDef.diPublic) {
|
||||
return injectorView[DIRECTIVES] ![i];
|
||||
return injectorView[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -486,15 +494,10 @@ export function injectorHasToken(
|
|||
return !!(value & mask);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the current injector and its parent are in the same host view.
|
||||
*
|
||||
* This is necessary to support @Host() decorators. If @Host() is set, we should stop searching once
|
||||
* the injector and its parent view don't match because it means we'd cross the view boundary.
|
||||
*/
|
||||
function sameHostView(parentLocation: number): boolean {
|
||||
return !!parentLocation && (parentLocation >> InjectorLocationFlags.ViewOffsetShift) === 0;
|
||||
/** Returns true if flags prevent parent injector from being searched for tokens */
|
||||
function shouldNotSearchParent(flags: InjectFlags, parentLocation: number): boolean|number {
|
||||
return flags & InjectFlags.Self ||
|
||||
(flags & InjectFlags.Host && (parentLocation >> InjectorLocationFlags.ViewOffsetShift) > 0);
|
||||
}
|
||||
|
||||
export class NodeInjector implements Injector {
|
||||
|
@ -512,7 +515,7 @@ export class NodeInjector implements Injector {
|
|||
}
|
||||
|
||||
setEnvironment(this._tNode, this._hostView);
|
||||
return getOrCreateInjectable(this._injectorIndex, this._hostView, token);
|
||||
return getOrCreateInjectable(this._tNode, this._hostView, token);
|
||||
}
|
||||
}
|
||||
export function getFactoryOf<T>(type: Type<any>): ((type?: Type<T>) => T)|null {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {assertEqual} from './assert';
|
||||
import {DirectiveDef} from './interfaces/definition';
|
||||
import {TNodeFlags} from './interfaces/node';
|
||||
import {DIRECTIVES, FLAGS, HookData, LViewData, LViewFlags, TView} from './interfaces/view';
|
||||
import {FLAGS, HookData, LViewData, LViewFlags, TView} from './interfaces/view';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@ import {DIRECTIVES, FLAGS, HookData, LViewData, LViewFlags, TView} from './inter
|
|||
* directive index), then saved in the even indices of the initHooks array. The odd indices
|
||||
* hold the hook functions themselves.
|
||||
*
|
||||
* @param index The index of the directive in LViewData[DIRECTIVES]
|
||||
* @param index The index of the directive in LViewData
|
||||
* @param hooks The static hooks map on the directive def
|
||||
* @param tView The current TView
|
||||
*/
|
||||
|
@ -52,7 +52,7 @@ export function queueLifecycleHooks(flags: number, tView: TView): void {
|
|||
// directiveCreate) so we can preserve the current hook order. Content, view, and destroy
|
||||
// hooks for projected components and directives must be called *before* their hosts.
|
||||
for (let i = start; i < end; i++) {
|
||||
const def: DirectiveDef<any> = tView.directives ![i];
|
||||
const def = tView.data[i] as DirectiveDef<any>;
|
||||
queueContentHooks(def, tView, i);
|
||||
queueViewHooks(def, tView, i);
|
||||
queueDestroyHooks(def, tView, i);
|
||||
|
@ -99,7 +99,7 @@ function queueDestroyHooks(def: DirectiveDef<any>, tView: TView, i: number): voi
|
|||
export function executeInitHooks(
|
||||
currentView: LViewData, tView: TView, creationMode: boolean): void {
|
||||
if (currentView[FLAGS] & LViewFlags.RunInit) {
|
||||
executeHooks(currentView[DIRECTIVES] !, tView.initHooks, tView.checkHooks, creationMode);
|
||||
executeHooks(currentView, tView.initHooks, tView.checkHooks, creationMode);
|
||||
currentView[FLAGS] &= ~LViewFlags.RunInit;
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ export function executeInitHooks(
|
|||
* @param currentView The current view
|
||||
*/
|
||||
export function executeHooks(
|
||||
data: any[], allHooks: HookData | null, checkHooks: HookData | null,
|
||||
data: LViewData, allHooks: HookData | null, checkHooks: HookData | null,
|
||||
creationMode: boolean): void {
|
||||
const hooksToCall = creationMode ? allHooks : checkHooks;
|
||||
if (hooksToCall) {
|
||||
|
@ -125,8 +125,8 @@ export function executeHooks(
|
|||
* @param currentView The current view
|
||||
* @param arr The array in which the hooks are found
|
||||
*/
|
||||
export function callHooks(data: any[], arr: HookData): void {
|
||||
export function callHooks(currentView: any[], arr: HookData): void {
|
||||
for (let i = 0; i < arr.length; i += 2) {
|
||||
(arr[i + 1] as() => void).call(data[arr[i] as number]);
|
||||
(arr[i + 1] as() => void).call(currentView[arr[i] as number]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,6 @@ export {
|
|||
listener,
|
||||
store,
|
||||
load,
|
||||
loadDirective,
|
||||
|
||||
namespaceHTML,
|
||||
namespaceMathML,
|
||||
|
|
|
@ -18,12 +18,13 @@ import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComp
|
|||
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||
import {INJECTOR_SIZE} from './interfaces/injector';
|
||||
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode} from './interfaces/node';
|
||||
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||
import {LQueries} from './interfaces/query';
|
||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
|
||||
import {StylingContext} from './interfaces/styling';
|
||||
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
|
||||
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getHostElementNode, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
|
||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||
|
@ -42,15 +43,6 @@ const _CLEAN_PROMISE = Promise.resolve(null);
|
|||
*/
|
||||
export type SanitizerFn = (value: any) => string;
|
||||
|
||||
/**
|
||||
* TView.data needs to fill the same number of slots as the LViewData header
|
||||
* so the indices of nodes are consistent between LViewData and TView.data.
|
||||
*
|
||||
* It's much faster to keep a blueprint of the pre-filled array and slice it
|
||||
* than it is to create a new array and fill it each time a TView is created.
|
||||
*/
|
||||
const HEADER_FILLER = new Array(HEADER_OFFSET).fill(null);
|
||||
|
||||
/**
|
||||
* Token set in currentMatches while dependencies are being resolved.
|
||||
*
|
||||
|
@ -230,14 +222,6 @@ export function _getViewData(): LViewData {
|
|||
*/
|
||||
let contextViewData: LViewData = null !;
|
||||
|
||||
/**
|
||||
* An array of directive instances in the current view.
|
||||
*
|
||||
* These must be stored separately from LNodes because their presence is
|
||||
* unknown at compile-time and thus space cannot be reserved in data[].
|
||||
*/
|
||||
let directives: any[]|null;
|
||||
|
||||
function getCleanup(view: LViewData): any[] {
|
||||
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
|
||||
return view[CLEANUP] || (view[CLEANUP] = []);
|
||||
|
@ -259,7 +243,7 @@ let firstTemplatePass = true;
|
|||
/**
|
||||
* The root index from which pure function instructions should calculate their binding
|
||||
* indices. In component views, this is TView.bindingStartIndex. In a host binding
|
||||
* context, this is the TView.hostBindingStartIndex + any hostVars before the given dir.
|
||||
* context, this is the TView.expandoStartIndex + any dirs/hostVars before the given dir.
|
||||
*/
|
||||
let bindingRootIndex: number = -1;
|
||||
|
||||
|
@ -268,6 +252,9 @@ export function getBindingRoot() {
|
|||
return bindingRootIndex;
|
||||
}
|
||||
|
||||
// Root component will always have an element index of 0 and an injector size of 1
|
||||
const ROOT_EXPANDO_INSTRUCTIONS = [0, 1];
|
||||
|
||||
const enum BindingDirection {
|
||||
Input,
|
||||
Output,
|
||||
|
@ -288,7 +275,6 @@ const enum BindingDirection {
|
|||
export function enterView(
|
||||
newView: LViewData, hostTNode: TElementNode | TViewNode | null): LViewData {
|
||||
const oldView: LViewData = viewData;
|
||||
directives = newView && newView[DIRECTIVES];
|
||||
tView = newView && newView[TVIEW];
|
||||
|
||||
creationMode = newView && (newView[FLAGS] & LViewFlags.CreationMode) === LViewFlags.CreationMode;
|
||||
|
@ -317,7 +303,7 @@ export function enterView(
|
|||
export function leaveView(newView: LViewData, creationOnly?: boolean): void {
|
||||
if (!creationOnly) {
|
||||
if (!checkNoChangesMode) {
|
||||
executeHooks(directives !, tView.viewHooks, tView.viewCheckHooks, creationMode);
|
||||
executeHooks(viewData, tView.viewHooks, tView.viewCheckHooks, creationMode);
|
||||
}
|
||||
// Views are clean and in update mode after being checked, so these bits are cleared
|
||||
viewData[FLAGS] &= ~(LViewFlags.CreationMode | LViewFlags.Dirty);
|
||||
|
@ -334,7 +320,7 @@ export function leaveView(newView: LViewData, creationOnly?: boolean): void {
|
|||
* Note: view hooks are triggered later when leaving the view.
|
||||
*/
|
||||
function refreshDescendantViews() {
|
||||
setHostBindings(tView.hostBindings);
|
||||
setHostBindings();
|
||||
const parentFirstTemplatePass = firstTemplatePass;
|
||||
|
||||
// This needs to be set before children are processed to support recursive components
|
||||
|
@ -349,7 +335,7 @@ function refreshDescendantViews() {
|
|||
refreshContentQueries(tView);
|
||||
|
||||
if (!checkNoChangesMode) {
|
||||
executeHooks(directives !, tView.contentHooks, tView.contentCheckHooks, creationMode);
|
||||
executeHooks(viewData, tView.contentHooks, tView.contentCheckHooks, creationMode);
|
||||
}
|
||||
|
||||
refreshChildComponents(tView.components, parentFirstTemplatePass);
|
||||
|
@ -357,21 +343,38 @@ function refreshDescendantViews() {
|
|||
|
||||
|
||||
/** Sets the host bindings for the current view. */
|
||||
export function setHostBindings(bindings: number[] | null): void {
|
||||
if (bindings != null) {
|
||||
bindingRootIndex = viewData[BINDING_INDEX] = tView.hostBindingStartIndex;
|
||||
const defs = tView.directives !;
|
||||
for (let i = 0; i < bindings.length; i += 2) {
|
||||
const dirIndex = bindings[i];
|
||||
const def = defs[dirIndex] as DirectiveDef<any>;
|
||||
if (firstTemplatePass) {
|
||||
for (let i = 0; i < def.hostVars; i++) {
|
||||
tView.blueprint.push(NO_CHANGE);
|
||||
viewData.push(NO_CHANGE);
|
||||
export function setHostBindings(): void {
|
||||
if (tView.expandoInstructions) {
|
||||
bindingRootIndex = viewData[BINDING_INDEX] = tView.expandoStartIndex;
|
||||
let currentDirectiveIndex = -1;
|
||||
let currentElementIndex = -1;
|
||||
for (let i = 0; i < tView.expandoInstructions.length; i++) {
|
||||
const instruction = tView.expandoInstructions[i];
|
||||
if (typeof instruction === 'number') {
|
||||
if (instruction <= 0) {
|
||||
// Negative numbers mean that we are starting new EXPANDO block and need to update
|
||||
// the current element and directive index.
|
||||
currentElementIndex = -instruction;
|
||||
if (typeof viewData[bindingRootIndex] === 'number') {
|
||||
// We've hit an injector. It may or may not exist depending on whether
|
||||
// there is a public directive on this node.
|
||||
bindingRootIndex += INJECTOR_SIZE;
|
||||
}
|
||||
currentDirectiveIndex = bindingRootIndex;
|
||||
} else {
|
||||
// This is either the injector size (so the binding root can skip over directives
|
||||
// and get to the first set of host bindings on this node) or the host var count
|
||||
// (to get to the next set of host bindings on this node).
|
||||
bindingRootIndex += instruction;
|
||||
}
|
||||
} else {
|
||||
// If it's not a number, it's a host binding function that needs to be executed.
|
||||
viewData[BINDING_INDEX] = bindingRootIndex;
|
||||
// We must subtract the header offset because the load() instruction
|
||||
// expects a raw, unadjusted index.
|
||||
instruction(currentDirectiveIndex - HEADER_OFFSET, currentElementIndex);
|
||||
currentDirectiveIndex++;
|
||||
}
|
||||
def.hostBindings !(dirIndex, bindings[i + 1]);
|
||||
bindingRootIndex = viewData[BINDING_INDEX] = bindingRootIndex + def.hostVars;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -381,9 +384,10 @@ function refreshContentQueries(tView: TView): void {
|
|||
if (tView.contentQueries != null) {
|
||||
for (let i = 0; i < tView.contentQueries.length; i += 2) {
|
||||
const directiveDefIdx = tView.contentQueries[i];
|
||||
const directiveDef = tView.directives ![directiveDefIdx];
|
||||
const directiveDef = tView.data[directiveDefIdx] as DirectiveDef<any>;
|
||||
|
||||
directiveDef.contentQueriesRefresh !(directiveDefIdx, tView.contentQueries[i + 1]);
|
||||
directiveDef.contentQueriesRefresh !(
|
||||
directiveDefIdx - HEADER_OFFSET, tView.contentQueries[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -401,7 +405,7 @@ function refreshChildComponents(
|
|||
export function executeInitAndContentHooks(): void {
|
||||
if (!checkNoChangesMode) {
|
||||
executeInitHooks(viewData, tView, creationMode);
|
||||
executeHooks(directives !, tView.contentHooks, tView.contentCheckHooks, creationMode);
|
||||
executeHooks(viewData, tView.contentHooks, tView.contentCheckHooks, creationMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -538,7 +542,7 @@ export function createNodeAtIndex(
|
|||
export function adjustBlueprintForNewNode(view: LViewData) {
|
||||
const tView = view[TVIEW];
|
||||
if (tView.firstTemplatePass) {
|
||||
tView.hostBindingStartIndex++;
|
||||
tView.expandoStartIndex++;
|
||||
tView.blueprint.push(null);
|
||||
view.push(null);
|
||||
}
|
||||
|
@ -658,10 +662,14 @@ export function renderEmbeddedTemplate<T>(
|
|||
if (rf & RenderFlags.Update) {
|
||||
refreshDescendantViews();
|
||||
} else {
|
||||
// This must be set to false immediately after the first creation run because in an
|
||||
// ngFor loop, all the views will be created together before update mode runs and turns
|
||||
// off firstTemplatePass. If we don't set it here, instances will perform directive
|
||||
// matching, etc again and again.
|
||||
viewToRender[TVIEW].firstTemplatePass = firstTemplatePass = false;
|
||||
}
|
||||
} finally {
|
||||
// renderEmbeddedTemplate() is called twice in fact, once for creation only and then once for
|
||||
// renderEmbeddedTemplate() is called twice, once for creation only and then once for
|
||||
// update. When for creation only, leaveView() must not trigger view hooks, nor clean flags.
|
||||
const isCreationOnly = (rf & RenderFlags.Create) === RenderFlags.Create;
|
||||
leaveView(oldView !, isCreationOnly);
|
||||
|
@ -702,7 +710,7 @@ export function renderComponentOrTemplate<T>(
|
|||
|
||||
// Element was stored at 0 in data and directive was stored at 0 in directives
|
||||
// in renderComponent()
|
||||
setHostBindings(tView.hostBindings);
|
||||
setHostBindings();
|
||||
componentRefresh(HEADER_OFFSET, false);
|
||||
}
|
||||
} finally {
|
||||
|
@ -905,15 +913,45 @@ function cacheMatchingDirectivesForNode(
|
|||
// Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in tsickle.
|
||||
const exportsMap: ({[key: string]: number} | null) = localRefs ? {'': -1} : null;
|
||||
const matches = tView.currentMatches = findDirectiveMatches(tNode);
|
||||
generateExpandoBlock(tNode, matches);
|
||||
let totalHostVars = 0;
|
||||
if (matches) {
|
||||
for (let i = 0; i < matches.length; i += 2) {
|
||||
const def = matches[i] as DirectiveDef<any>;
|
||||
const valueIndex = i + 1;
|
||||
resolveDirective(def, valueIndex, matches, tView);
|
||||
resolveDirective(def, valueIndex, matches);
|
||||
totalHostVars += def.hostVars;
|
||||
saveNameToExportMap(matches[valueIndex] as number, def, exportsMap);
|
||||
}
|
||||
}
|
||||
if (exportsMap) cacheMatchingLocalNames(tNode, localRefs, exportsMap);
|
||||
prefillHostVars(totalHostVars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new block in TView.expandoInstructions for this node.
|
||||
*
|
||||
* Each expando block starts with the element index (turned negative so we can distinguish
|
||||
* it from the hostVar count) and the directive count. See more in VIEW_DATA.md.
|
||||
*/
|
||||
function generateExpandoBlock(tNode: TNode, matches: CurrentMatchesList | null): void {
|
||||
const directiveCount = matches ? matches.length / 2 : 0;
|
||||
const elementIndex = -(tNode.index - HEADER_OFFSET);
|
||||
(tView.expandoInstructions || (tView.expandoInstructions = [
|
||||
])).push(elementIndex, directiveCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* On the first template pass, we need to reserve space for host binding values
|
||||
* after directives are matched (so all directives are saved, then bindings).
|
||||
* Because we are updating the blueprint, we only need to do this once.
|
||||
*/
|
||||
export function prefillHostVars(totalHostVars: number): void {
|
||||
for (let i = 0; i < totalHostVars; i++) {
|
||||
viewData.push(NO_CHANGE);
|
||||
tView.blueprint.push(NO_CHANGE);
|
||||
tView.data.push(null);
|
||||
}
|
||||
}
|
||||
|
||||
/** Matches the current node against all available selectors. */
|
||||
|
@ -925,17 +963,16 @@ function findDirectiveMatches(tNode: TNode): CurrentMatchesList|null {
|
|||
const def = registry[i];
|
||||
if (isNodeMatchingSelectorList(tNode, def.selectors !)) {
|
||||
matches || (matches = []);
|
||||
if (def.diPublic) def.diPublic(def);
|
||||
|
||||
if ((def as ComponentDef<any>).template) {
|
||||
if (tNode.flags & TNodeFlags.isComponent) throwMultipleComponentError(tNode);
|
||||
addComponentLogic(def as ComponentDef<any>);
|
||||
tNode.flags = TNodeFlags.isComponent;
|
||||
|
||||
// The component is always stored first with directives after.
|
||||
matches.unshift(def, null);
|
||||
} else {
|
||||
matches.push(def, null);
|
||||
}
|
||||
if (def.diPublic) def.diPublic(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -943,12 +980,11 @@ function findDirectiveMatches(tNode: TNode): CurrentMatchesList|null {
|
|||
}
|
||||
|
||||
export function resolveDirective(
|
||||
def: DirectiveDef<any>, valueIndex: number, matches: CurrentMatchesList, tView: TView): any {
|
||||
def: DirectiveDef<any>, valueIndex: number, matches: CurrentMatchesList): any {
|
||||
if (matches[valueIndex] === null) {
|
||||
matches[valueIndex] = CIRCULAR;
|
||||
const instance = def.factory();
|
||||
(tView.directives || (tView.directives = [])).push(def);
|
||||
return directiveCreate(matches[valueIndex] = tView.directives !.length - 1, instance, def);
|
||||
return directiveCreate(matches[valueIndex] = viewData.length, instance, def);
|
||||
} else if (matches[valueIndex] === CIRCULAR) {
|
||||
// If we revisit this directive before it's resolved, we know it's circular
|
||||
throwCyclicDependencyError(def.type);
|
||||
|
@ -965,13 +1001,11 @@ function queueComponentIndexForCheck(): void {
|
|||
|
||||
/** Stores index of directive and host element so it will be queued for binding refresh during CD.
|
||||
*/
|
||||
export function queueHostBindingForCheck(dirIndex: number, hostVars: number): void {
|
||||
// Must subtract the header offset because hostBindings functions are generated with
|
||||
// instructions that expect element indices that are NOT adjusted (e.g. elementProperty).
|
||||
export function queueHostBindingForCheck(
|
||||
dirIndex: number, def: DirectiveDef<any>| ComponentDef<any>): void {
|
||||
ngDevMode &&
|
||||
assertEqual(firstTemplatePass, true, 'Should only be called in first template pass.');
|
||||
(tView.hostBindings || (tView.hostBindings = [
|
||||
])).push(dirIndex, previousOrParentTNode.index - HEADER_OFFSET);
|
||||
tView.expandoInstructions !.push(def.hostBindings !, def.hostVars);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -990,10 +1024,9 @@ function instantiateDirectivesDirectly() {
|
|||
if (count > 0) {
|
||||
const start = previousOrParentTNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const end = start + count;
|
||||
const tDirectives = tView.directives !;
|
||||
|
||||
for (let i = start; i < end; i++) {
|
||||
const def: DirectiveDef<any> = tDirectives[i];
|
||||
const def = tView.data[i] as DirectiveDef<any>| ComponentDef<any>;
|
||||
|
||||
// Component view must be set on node before the factory is created so
|
||||
// ChangeDetectorRefs have a way to store component view on creation.
|
||||
|
@ -1046,7 +1079,7 @@ function saveResolvedLocalsInData(localRefExtractor: LocalRefExtractor): void {
|
|||
let localIndex = previousOrParentTNode.index + 1;
|
||||
for (let i = 0; i < localNames.length; i += 2) {
|
||||
const index = localNames[i + 1] as number;
|
||||
const value = index === -1 ? localRefExtractor(tNode, viewData) : directives ![index];
|
||||
const value = index === -1 ? localRefExtractor(tNode, viewData) : viewData[index];
|
||||
viewData[localIndex++] = value;
|
||||
}
|
||||
}
|
||||
|
@ -1108,8 +1141,9 @@ export function createTView(
|
|||
data: blueprint.slice(), // Fill in to match HEADER_OFFSET in LViewData
|
||||
childIndex: -1, // Children set in addToViewTree(), if any
|
||||
bindingStartIndex: bindingStartIndex,
|
||||
hostBindingStartIndex: initialViewLength,
|
||||
expandoStartIndex: initialViewLength,
|
||||
directives: null,
|
||||
expandoInstructions: null,
|
||||
firstTemplatePass: true,
|
||||
initHooks: null,
|
||||
checkHooks: null,
|
||||
|
@ -1120,7 +1154,6 @@ export function createTView(
|
|||
destroyHooks: null,
|
||||
pipeDestroyHooks: null,
|
||||
cleanup: null,
|
||||
hostBindings: null,
|
||||
contentQueries: null,
|
||||
components: null,
|
||||
directiveRegistry: typeof directives === 'function' ? directives() : directives,
|
||||
|
@ -1224,9 +1257,10 @@ export function hostElement(
|
|||
null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer));
|
||||
|
||||
if (firstTemplatePass) {
|
||||
tNode.flags = TNodeFlags.isComponent;
|
||||
tView.expandoInstructions = ROOT_EXPANDO_INSTRUCTIONS.slice();
|
||||
if (def.diPublic) def.diPublic(def);
|
||||
tView.directives = [def];
|
||||
tNode.flags =
|
||||
viewData.length << TNodeFlags.DirectiveStartingIndexShift | TNodeFlags.isComponent;
|
||||
}
|
||||
return viewData[HEADER_OFFSET];
|
||||
}
|
||||
|
@ -1289,8 +1323,8 @@ export function listener(
|
|||
*/
|
||||
function createOutput(outputs: PropertyAliasValue, listener: Function): void {
|
||||
for (let i = 0; i < outputs.length; i += 2) {
|
||||
ngDevMode && assertDataInRange(outputs[i] as number, directives !);
|
||||
const subscription = directives ![outputs[i] as number][outputs[i + 1]].subscribe(listener);
|
||||
ngDevMode && assertDataInRange(outputs[i] as number, viewData);
|
||||
const subscription = viewData[outputs[i] as number][outputs[i + 1]].subscribe(listener);
|
||||
storeCleanupWithContext(viewData, subscription, subscription.unsubscribe);
|
||||
}
|
||||
}
|
||||
|
@ -1498,8 +1532,8 @@ export function createTNode(
|
|||
*/
|
||||
function setInputsForProperty(inputs: PropertyAliasValue, value: any): void {
|
||||
for (let i = 0; i < inputs.length; i += 2) {
|
||||
ngDevMode && assertDataInRange(inputs[i] as number, directives !);
|
||||
directives ![inputs[i] as number][inputs[i + 1]] = value;
|
||||
ngDevMode && assertDataInRange(inputs[i] as number, viewData);
|
||||
viewData[inputs[i] as number][inputs[i + 1]] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1519,7 +1553,7 @@ function generatePropertyAliases(
|
|||
const start = tNodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const end = start + count;
|
||||
const isInput = direction === BindingDirection.Input;
|
||||
const defs = tView.directives !;
|
||||
const defs = tView.data;
|
||||
|
||||
for (let i = start; i < end; i++) {
|
||||
const directiveDef = defs[i] as DirectiveDef<any>;
|
||||
|
@ -1775,8 +1809,6 @@ export function directiveCreate<T>(
|
|||
// Init hooks are queued now so ngOnInit is called in host components before
|
||||
// any projected components.
|
||||
queueInitHooks(directiveDefIdx, directiveDef.onInit, directiveDef.doCheck, tView);
|
||||
|
||||
if (directiveDef.hostBindings) queueHostBindingForCheck(directiveDefIdx, directiveDef.hostVars);
|
||||
}
|
||||
|
||||
ngDevMode && assertDefined(previousOrParentTNode, 'previousOrParentTNode');
|
||||
|
@ -1810,7 +1842,11 @@ function addComponentLogic<T>(def: ComponentDef<T>): void {
|
|||
(hostNode as{data: LViewData}).data = componentView;
|
||||
(componentView as LViewData)[HOST_NODE] = previousOrParentTNode as TElementNode;
|
||||
|
||||
if (firstTemplatePass) queueComponentIndexForCheck();
|
||||
if (firstTemplatePass) {
|
||||
queueComponentIndexForCheck();
|
||||
previousOrParentTNode.flags =
|
||||
viewData.length << TNodeFlags.DirectiveStartingIndexShift | TNodeFlags.isComponent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1832,14 +1868,11 @@ export function baseDirectiveCreate<T>(
|
|||
attachPatchData(hostNode.native, viewData);
|
||||
}
|
||||
|
||||
if (directives == null) viewData[DIRECTIVES] = directives = [];
|
||||
|
||||
ngDevMode && assertDataNext(index, directives);
|
||||
directives[index] = directive;
|
||||
viewData[index] = directive;
|
||||
|
||||
if (firstTemplatePass) {
|
||||
const flags = previousOrParentTNode.flags;
|
||||
if ((flags & TNodeFlags.DirectiveCountMask) === 0) {
|
||||
if (flags === 0) {
|
||||
// When the first directive is created:
|
||||
// - save the index,
|
||||
// - set the number of directives to 1
|
||||
|
@ -1852,6 +1885,10 @@ export function baseDirectiveCreate<T>(
|
|||
'Reached the max number of directives');
|
||||
previousOrParentTNode.flags++;
|
||||
}
|
||||
|
||||
tView.data.push(directiveDef);
|
||||
tView.blueprint.push(null);
|
||||
if (directiveDef.hostBindings) queueHostBindingForCheck(index, directiveDef);
|
||||
} else {
|
||||
const diPublic = directiveDef !.diPublic;
|
||||
if (diPublic) diPublic(directiveDef !);
|
||||
|
@ -2828,13 +2865,6 @@ function walkUpViews(nestingLevel: number, currentView: LViewData): LViewData {
|
|||
return currentView;
|
||||
}
|
||||
|
||||
/** Retrieves a value from the `directives` array. */
|
||||
export function loadDirective<T>(index: number): T {
|
||||
ngDevMode && assertDefined(directives, 'Directives array should be defined if reading a dir.');
|
||||
ngDevMode && assertDataInRange(index, directives !);
|
||||
return directives ![index];
|
||||
}
|
||||
|
||||
export function loadQueryList<T>(queryListIdx: number): QueryList<T> {
|
||||
ngDevMode && assertDefined(
|
||||
viewData[CONTENT_QUERIES],
|
||||
|
@ -2918,7 +2948,7 @@ export function registerContentQuery<Q>(queryList: QueryList<Q>): void {
|
|||
const savedContentQueriesLength =
|
||||
(viewData[CONTENT_QUERIES] || (viewData[CONTENT_QUERIES] = [])).push(queryList);
|
||||
if (firstTemplatePass) {
|
||||
const currentDirectiveIndex = directives !.length - 1;
|
||||
const currentDirectiveIndex = viewData.length - 1;
|
||||
const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []);
|
||||
const lastSavedDirectiveIndex =
|
||||
tView.contentQueries.length ? tView.contentQueries[tView.contentQueries.length - 2] : -1;
|
||||
|
|
|
@ -145,7 +145,7 @@ export interface DirectiveDef<T> extends BaseDef<T> {
|
|||
hostVars: number;
|
||||
|
||||
/** Refreshes host bindings on the associated directive. */
|
||||
hostBindings: ((directiveIndex: number, elementIndex: number) => void)|null;
|
||||
hostBindings: HostBindingsFunction|null;
|
||||
|
||||
/**
|
||||
* Static attributes to set on host element.
|
||||
|
@ -330,6 +330,8 @@ export type DirectiveTypeList =
|
|||
(DirectiveDef<any>| ComponentDef<any>|
|
||||
Type<any>/* Type as workaround for: Microsoft/TypeScript/issues/4881 */)[];
|
||||
|
||||
export type HostBindingsFunction = (directiveIndex: number, elementIndex: number) => void;
|
||||
|
||||
/**
|
||||
* Type used for PipeDefs on component definition.
|
||||
*
|
||||
|
|
|
@ -12,13 +12,13 @@ import {Sanitizer} from '../../sanitization/security';
|
|||
import {PlayerHandler} from '../interfaces/player';
|
||||
|
||||
import {LContainer} from './container';
|
||||
import {ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDef, PipeDefList} from './definition';
|
||||
import {LElementNode, LViewNode, TElementNode, TNode, TViewNode} from './node';
|
||||
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBindingsFunction, PipeDef, PipeDefList} from './definition';
|
||||
import {TElementNode, TNode, TViewNode} from './node';
|
||||
import {LQueries} from './query';
|
||||
import {Renderer3} from './renderer';
|
||||
|
||||
/** Size of LViewData's header. Necessary to adjust for it when setting slots. */
|
||||
export const HEADER_OFFSET = 17;
|
||||
export const HEADER_OFFSET = 16;
|
||||
|
||||
// Below are constants for LViewData indices to help us look up LViewData members
|
||||
// without having to remember the specific indices.
|
||||
|
@ -30,16 +30,15 @@ export const QUERIES = 3;
|
|||
export const FLAGS = 4;
|
||||
export const HOST_NODE = 5;
|
||||
export const BINDING_INDEX = 6;
|
||||
export const DIRECTIVES = 7;
|
||||
export const CLEANUP = 8;
|
||||
export const CONTEXT = 9;
|
||||
export const INJECTOR = 10;
|
||||
export const RENDERER = 11;
|
||||
export const SANITIZER = 12;
|
||||
export const TAIL = 13;
|
||||
export const CONTAINER_INDEX = 14;
|
||||
export const CONTENT_QUERIES = 15;
|
||||
export const DECLARATION_VIEW = 16;
|
||||
export const CLEANUP = 7;
|
||||
export const CONTEXT = 8;
|
||||
export const INJECTOR = 9;
|
||||
export const RENDERER = 10;
|
||||
export const SANITIZER = 11;
|
||||
export const TAIL = 12;
|
||||
export const CONTAINER_INDEX = 13;
|
||||
export const CONTENT_QUERIES = 14;
|
||||
export const DECLARATION_VIEW = 15;
|
||||
|
||||
// This interface replaces the real LViewData interface if it is an arg or a
|
||||
// return value of a public instruction. This ensures we don't need to expose
|
||||
|
@ -115,15 +114,6 @@ export interface LViewData extends Array<any> {
|
|||
*/
|
||||
[BINDING_INDEX]: number;
|
||||
|
||||
/**
|
||||
* An array of directive instances in the current view.
|
||||
*
|
||||
* These must be stored separately from LNodes because their presence is
|
||||
* unknown at compile-time and thus space cannot be reserved in data[].
|
||||
*/
|
||||
// TODO: flatten into LViewData[]
|
||||
[DIRECTIVES]: any[]|null;
|
||||
|
||||
/**
|
||||
* When a view is destroyed, listeners need to be released and outputs need to be
|
||||
* unsubscribed. This context array stores both listener functions wrapped with
|
||||
|
@ -307,11 +297,16 @@ export interface TView {
|
|||
bindingStartIndex: number;
|
||||
|
||||
/**
|
||||
* The index at which the data array begins to store host bindings for components
|
||||
* or directives in its template. Saving this value ensures that we can set the
|
||||
* binding root and binding index correctly before checking host bindings.
|
||||
* The index where the "expando" section of `LViewData` begins. The expando
|
||||
* section contains injectors, directive instances, and host binding values.
|
||||
* Unlike the "consts" and "vars" sections of `LViewData`, the length of this
|
||||
* section cannot be calculated at compile-time because directives are matched
|
||||
* at runtime to preserve locality.
|
||||
*
|
||||
* We store this start index so we know where to start checking host bindings
|
||||
* in `setHostBindings`.
|
||||
*/
|
||||
hostBindingStartIndex: number;
|
||||
expandoStartIndex: number;
|
||||
|
||||
/**
|
||||
* Index of the host node of the first LView or LContainer beneath this LView in
|
||||
|
@ -357,6 +352,13 @@ export interface TView {
|
|||
*/
|
||||
directives: DirectiveDefList|null;
|
||||
|
||||
/**
|
||||
* Set of instructions used to process host bindings efficiently.
|
||||
*
|
||||
* See VIEW_DATA.md for more information.
|
||||
*/
|
||||
expandoInstructions: (number|HostBindingsFunction)[]|null;
|
||||
|
||||
/**
|
||||
* Full registry of directives and components that may be found in this view.
|
||||
*
|
||||
|
@ -479,18 +481,6 @@ export interface TView {
|
|||
*/
|
||||
components: number[]|null;
|
||||
|
||||
/**
|
||||
* A list of indices for child directives that have host bindings.
|
||||
*
|
||||
* Even indices: Directive indices
|
||||
* Odd indices: Element indices
|
||||
*
|
||||
* Element indices are NOT adjusted for LViewData header offset because
|
||||
* they will be fed into instructions that expect the raw index (e.g. elementProperty)
|
||||
*/
|
||||
hostBindings: number[]|null;
|
||||
|
||||
|
||||
/**
|
||||
* A list of indices for child directives that have content queries.
|
||||
*
|
||||
|
@ -558,7 +548,7 @@ export type HookData = (number | (() => void))[];
|
|||
*
|
||||
* Injector bloom filters are also stored here.
|
||||
*/
|
||||
export type TData = (TNode | PipeDef<any>| number | null)[];
|
||||
export type TData = (TNode | PipeDef<any>| DirectiveDef<any>| ComponentDef<any>| number | null)[];
|
||||
|
||||
/** Type for TView.currentMatches */
|
||||
export type CurrentMatchesList = [DirectiveDef<any>, (string | number | null)];
|
||||
|
|
|
@ -41,7 +41,6 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
|||
'ɵnextContext': r3.nextContext,
|
||||
'ɵcontainerRefreshStart': r3.containerRefreshStart,
|
||||
'ɵcontainerRefreshEnd': r3.containerRefreshEnd,
|
||||
'ɵloadDirective': r3.loadDirective,
|
||||
'ɵloadQueryList': r3.loadQueryList,
|
||||
'ɵnamespaceHTML': r3.namespaceHTML,
|
||||
'ɵnamespaceMathML': r3.namespaceMathML,
|
||||
|
|
|
@ -13,7 +13,7 @@ import {LContainer, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unuse
|
|||
import {LContainerNode, LElementContainerNode, LElementNode, LTextNode, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
||||
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
|
||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
|
||||
import {CLEANUP, CONTAINER_INDEX, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||
import {CLEANUP, CONTAINER_INDEX, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||
import {assertNodeType} from './node_assert';
|
||||
import {getLNode, stringify} from './util';
|
||||
|
||||
|
@ -499,7 +499,7 @@ function executeOnDestroys(view: LViewData): void {
|
|||
const tView = view[TVIEW];
|
||||
let destroyHooks: HookData|null;
|
||||
if (tView != null && (destroyHooks = tView.destroyHooks) != null) {
|
||||
callHooks(view[DIRECTIVES] !, destroyHooks);
|
||||
callHooks(view, destroyHooks);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,17 +11,19 @@ import {bindingUpdated, bindingUpdated2, bindingUpdated4, updateBinding, getBind
|
|||
/**
|
||||
* Bindings for pure functions are stored after regular bindings.
|
||||
*
|
||||
* |--------consts--------|----------------vars----------------|------ hostVars (dir1) ------|
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
* | nodes / refs / pipes | bindings | pure function bindings | host bindings | host slots |
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
* ^ ^
|
||||
* TView.bindingStartIndex TView.hostBindingStartIndex
|
||||
* |------consts------|---------vars---------| |----- hostVars (dir1) ------|
|
||||
* ------------------------------------------------------------------------------------------
|
||||
* | nodes/refs/pipes | bindings | fn slots | injector | dir1 | host bindings | host slots |
|
||||
* ------------------------------------------------------------------------------------------
|
||||
* ^ ^
|
||||
* TView.bindingStartIndex TView.expandoStartIndex
|
||||
*
|
||||
* Pure function instructions are given an offset from the binding root. Adding the offset to the
|
||||
* binding root gives the first index where the bindings are stored. In component views, the binding
|
||||
* root is the bindingStartIndex. In host bindings, the binding root is the hostBindingStartIndex +
|
||||
* any hostVars in directives evaluated before it.
|
||||
* root is the bindingStartIndex. In host bindings, the binding root is the expandoStartIndex +
|
||||
* any directive instances + any hostVars in directives evaluated before it.
|
||||
*
|
||||
* See VIEW_DATA.md for more information about host binding resolution.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,7 +24,7 @@ import {DirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfac
|
|||
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
||||
import {LQueries, QueryReadType, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
||||
import {DIRECTIVES, LViewData, TVIEW} from './interfaces/view';
|
||||
import {LViewData, TVIEW} from './interfaces/view';
|
||||
import {flatten, isContentQueryHost} from './util';
|
||||
import {createElementRef, createTemplateRef} from './view_engine_compatibility';
|
||||
|
||||
|
@ -251,7 +251,7 @@ function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
|
|||
*/
|
||||
function getIdxOfMatchingDirective(tNode: TNode, currentView: LViewData, type: Type<any>): number|
|
||||
null {
|
||||
const defs = currentView[TVIEW].directives;
|
||||
const defs = currentView[TVIEW].data;
|
||||
if (defs) {
|
||||
const flags = tNode.flags;
|
||||
const count = flags & TNodeFlags.DirectiveCountMask;
|
||||
|
@ -275,7 +275,7 @@ function queryRead(tNode: TNode, currentView: LViewData, read: any): any {
|
|||
} else {
|
||||
const matchingIdx = getIdxOfMatchingDirective(tNode, currentView, read as Type<any>);
|
||||
if (matchingIdx !== null) {
|
||||
return currentView[DIRECTIVES] ![matchingIdx];
|
||||
return currentView[matchingIdx];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -314,7 +314,7 @@ function add(
|
|||
result = queryRead(tNode, currentView, predicate.read);
|
||||
} else {
|
||||
if (directiveIdx > -1) {
|
||||
result = currentView[DIRECTIVES] ![directiveIdx];
|
||||
result = currentView[directiveIdx];
|
||||
} else {
|
||||
// if read token and / or strategy is not specified,
|
||||
// detect it using appropriate tNode type
|
||||
|
|
|
@ -22,4 +22,4 @@ import {createTemplateRef} from './view_engine_compatibility';
|
|||
*/
|
||||
export function templateRefExtractor(tNode: TNode, currentView: LViewData) {
|
||||
return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, currentView);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEn
|
|||
|
||||
import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, getRendererFactory, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
|
||||
import {TViewNode} from './interfaces/node';
|
||||
import {DIRECTIVES, FLAGS, LViewData, LViewFlags, PARENT} from './interfaces/view';
|
||||
import {FLAGS, LViewData, LViewFlags, PARENT} from './interfaces/view';
|
||||
import {destroyLView} from './node_manipulation';
|
||||
|
||||
|
||||
|
@ -256,7 +256,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
|||
attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; }
|
||||
|
||||
private _lookUpContext(): T {
|
||||
return this._context = this._view[PARENT] ![DIRECTIVES] ![this._componentIndex] as T;
|
||||
return this._context = this._view[PARENT] ![this._componentIndex] as T;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,9 +38,6 @@
|
|||
{
|
||||
"name": "DECLARATION_VIEW"
|
||||
},
|
||||
{
|
||||
"name": "DIRECTIVES"
|
||||
},
|
||||
{
|
||||
"name": "DefaultIterableDiffer"
|
||||
},
|
||||
|
@ -164,6 +161,9 @@
|
|||
{
|
||||
"name": "RENDER_PARENT"
|
||||
},
|
||||
{
|
||||
"name": "ROOT_EXPANDO_INSTRUCTIONS"
|
||||
},
|
||||
{
|
||||
"name": "RecordViewTuple"
|
||||
},
|
||||
|
@ -527,6 +527,9 @@
|
|||
{
|
||||
"name": "firstTemplatePass"
|
||||
},
|
||||
{
|
||||
"name": "generateExpandoBlock"
|
||||
},
|
||||
{
|
||||
"name": "generateInitialInputs"
|
||||
},
|
||||
|
@ -833,6 +836,9 @@
|
|||
{
|
||||
"name": "pointers"
|
||||
},
|
||||
{
|
||||
"name": "prefillHostVars"
|
||||
},
|
||||
{
|
||||
"name": "prepareInitialFlag"
|
||||
},
|
||||
|
@ -905,9 +911,6 @@
|
|||
{
|
||||
"name": "resolveDirective"
|
||||
},
|
||||
{
|
||||
"name": "sameHostView"
|
||||
},
|
||||
{
|
||||
"name": "saveNameToExportMap"
|
||||
},
|
||||
|
@ -968,6 +971,9 @@
|
|||
{
|
||||
"name": "setValue"
|
||||
},
|
||||
{
|
||||
"name": "shouldNotSearchParent"
|
||||
},
|
||||
{
|
||||
"name": "storeCleanupFn"
|
||||
},
|
||||
|
|
|
@ -23,9 +23,6 @@
|
|||
{
|
||||
"name": "DECLARATION_VIEW"
|
||||
},
|
||||
{
|
||||
"name": "DIRECTIVES"
|
||||
},
|
||||
{
|
||||
"name": "EMPTY$1"
|
||||
},
|
||||
|
@ -95,6 +92,9 @@
|
|||
{
|
||||
"name": "RENDER_PARENT"
|
||||
},
|
||||
{
|
||||
"name": "ROOT_EXPANDO_INSTRUCTIONS"
|
||||
},
|
||||
{
|
||||
"name": "SANITIZER"
|
||||
},
|
||||
|
@ -302,6 +302,9 @@
|
|||
{
|
||||
"name": "nextNgElementId"
|
||||
},
|
||||
{
|
||||
"name": "prefillHostVars"
|
||||
},
|
||||
{
|
||||
"name": "queueHostBindingForCheck"
|
||||
},
|
||||
|
|
|
@ -29,9 +29,6 @@
|
|||
{
|
||||
"name": "DECLARATION_VIEW"
|
||||
},
|
||||
{
|
||||
"name": "DIRECTIVES"
|
||||
},
|
||||
{
|
||||
"name": "DefaultIterableDiffer"
|
||||
},
|
||||
|
@ -155,6 +152,9 @@
|
|||
{
|
||||
"name": "RENDER_PARENT"
|
||||
},
|
||||
{
|
||||
"name": "ROOT_EXPANDO_INSTRUCTIONS"
|
||||
},
|
||||
{
|
||||
"name": "RecordViewTuple"
|
||||
},
|
||||
|
@ -578,6 +578,9 @@
|
|||
{
|
||||
"name": "firstTemplatePass"
|
||||
},
|
||||
{
|
||||
"name": "generateExpandoBlock"
|
||||
},
|
||||
{
|
||||
"name": "generateInitialInputs"
|
||||
},
|
||||
|
@ -854,6 +857,9 @@
|
|||
{
|
||||
"name": "pointers"
|
||||
},
|
||||
{
|
||||
"name": "prefillHostVars"
|
||||
},
|
||||
{
|
||||
"name": "prepareInitialFlag"
|
||||
},
|
||||
|
@ -932,9 +938,6 @@
|
|||
{
|
||||
"name": "restoreView"
|
||||
},
|
||||
{
|
||||
"name": "sameHostView"
|
||||
},
|
||||
{
|
||||
"name": "saveNameToExportMap"
|
||||
},
|
||||
|
@ -992,6 +995,9 @@
|
|||
{
|
||||
"name": "setValue"
|
||||
},
|
||||
{
|
||||
"name": "shouldNotSearchParent"
|
||||
},
|
||||
{
|
||||
"name": "storeCleanupFn"
|
||||
},
|
||||
|
|
|
@ -191,9 +191,6 @@
|
|||
{
|
||||
"name": "DIGIT_CHAR"
|
||||
},
|
||||
{
|
||||
"name": "DIRECTIVES"
|
||||
},
|
||||
{
|
||||
"name": "DOCUMENT"
|
||||
},
|
||||
|
@ -692,6 +689,9 @@
|
|||
{
|
||||
"name": "ROOT_CONTEXT"
|
||||
},
|
||||
{
|
||||
"name": "ROOT_EXPANDO_INSTRUCTIONS"
|
||||
},
|
||||
{
|
||||
"name": "RecordViewTuple"
|
||||
},
|
||||
|
@ -1577,6 +1577,9 @@
|
|||
{
|
||||
"name": "fromPromise"
|
||||
},
|
||||
{
|
||||
"name": "generateExpandoBlock"
|
||||
},
|
||||
{
|
||||
"name": "generateInitialInputs"
|
||||
},
|
||||
|
@ -2165,6 +2168,9 @@
|
|||
{
|
||||
"name": "pointers"
|
||||
},
|
||||
{
|
||||
"name": "prefillHostVars"
|
||||
},
|
||||
{
|
||||
"name": "prepareInitialFlag"
|
||||
},
|
||||
|
@ -2273,9 +2279,6 @@
|
|||
{
|
||||
"name": "rxSubscriber"
|
||||
},
|
||||
{
|
||||
"name": "sameHostView"
|
||||
},
|
||||
{
|
||||
"name": "sanitizeSrcset"
|
||||
},
|
||||
|
@ -2357,6 +2360,9 @@
|
|||
{
|
||||
"name": "shimHostAttribute"
|
||||
},
|
||||
{
|
||||
"name": "shouldNotSearchParent"
|
||||
},
|
||||
{
|
||||
"name": "staticError"
|
||||
},
|
||||
|
|
|
@ -105,7 +105,7 @@ describe('components & directives', () => {
|
|||
hostVars: 1,
|
||||
hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
|
||||
$r3$.ɵelementProperty(
|
||||
elIndex, 'id', $r3$.ɵbind($r3$.ɵloadDirective<HostBindingDir>(dirIndex).dirId));
|
||||
elIndex, 'id', $r3$.ɵbind($r3$.ɵload<HostBindingDir>(dirIndex).dirId));
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
|
@ -256,8 +256,7 @@ describe('components & directives', () => {
|
|||
hostVars: 1,
|
||||
hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
|
||||
$r3$.ɵelementAttribute(
|
||||
elIndex, 'aria-label',
|
||||
$r3$.ɵbind($r3$.ɵloadDirective<HostBindingDir>(dirIndex).label));
|
||||
elIndex, 'aria-label', $r3$.ɵbind($r3$.ɵload<HostBindingDir>(dirIndex).label));
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
|
@ -752,7 +751,7 @@ describe('components & directives', () => {
|
|||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory() { return new MyApp(); },
|
||||
consts: 1,
|
||||
vars: 10,
|
||||
vars: 11,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, c: $any$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'my-comp');
|
||||
|
|
|
@ -121,7 +121,7 @@ describe('queries', () => {
|
|||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(
|
||||
dirIndex: $number$, queryStartIndex: $number$) {
|
||||
let $tmp$: any;
|
||||
const $instance$ = $r3$.ɵloadDirective<ContentQueryComponent>(dirIndex);
|
||||
const $instance$ = $r3$.ɵload<ContentQueryComponent>(dirIndex);
|
||||
$r3$.ɵqueryRefresh($tmp$ = $r3$.ɵloadQueryList<any>(queryStartIndex)) &&
|
||||
($instance$.someDir = $tmp$.first);
|
||||
$r3$.ɵqueryRefresh($tmp$ = $r3$.ɵloadQueryList<any>(queryStartIndex + 1)) &&
|
||||
|
|
|
@ -10,10 +10,10 @@ import {Attribute, ChangeDetectorRef, ElementRef, Host, InjectFlags, Injector, O
|
|||
import {RenderFlags} from '@angular/core/src/render3/interfaces/definition';
|
||||
|
||||
import {defineComponent} from '../../src/render3/definition';
|
||||
import {bloomAdd, bloomHashBitOrFactory as bloomHash, getOrCreateInjectable, getOrCreateNodeInjector, injectAttribute, injectorHasToken} from '../../src/render3/di';
|
||||
import {bloomAdd, bloomHashBitOrFactory as bloomHash, getOrCreateNodeInjector, injectAttribute, injectorHasToken} from '../../src/render3/di';
|
||||
import {PublicFeature, defineDirective, directiveInject, elementProperty, injectRenderer2, load, templateRefExtractor} from '../../src/render3/index';
|
||||
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, loadDirective, elementContainerStart, elementContainerEnd, _getViewData, getTNode} from '../../src/render3/instructions';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions';
|
||||
import {isProceduralRenderer} from '../../src/render3/interfaces/renderer';
|
||||
import {AttributeMarker, LContainerNode, LElementNode, TNodeType} from '../../src/render3/interfaces/node';
|
||||
|
||||
|
@ -677,8 +677,7 @@ describe('di', () => {
|
|||
factory: () => hostBindingDir = new HostBindingDir(),
|
||||
hostVars: 1,
|
||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||
elementProperty(
|
||||
elementIndex, 'id', bind(loadDirective<HostBindingDir>(directiveIndex).id));
|
||||
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1298,7 +1297,8 @@ describe('di', () => {
|
|||
projectionDef();
|
||||
projection(0);
|
||||
}
|
||||
}
|
||||
},
|
||||
features: [PublicFeature]
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1323,7 +1323,8 @@ describe('di', () => {
|
|||
type: DirectiveSameInstance,
|
||||
selectors: [['', 'dirSame', '']],
|
||||
factory: () => dirSameInstance =
|
||||
new DirectiveSameInstance(directiveInject(ChangeDetectorRef as any))
|
||||
new DirectiveSameInstance(directiveInject(ChangeDetectorRef as any)),
|
||||
features: [PublicFeature]
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1419,7 +1420,8 @@ describe('di', () => {
|
|||
textBinding(3, bind(tmp.value));
|
||||
}
|
||||
},
|
||||
directives: directives
|
||||
directives: directives,
|
||||
features: [PublicFeature]
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1563,6 +1565,7 @@ describe('di', () => {
|
|||
});
|
||||
|
||||
describe('@Attribute', () => {
|
||||
let myDirectiveInstance !: MyDirective | null;
|
||||
|
||||
class MyDirective {
|
||||
exists = 'wrong' as string | undefined;
|
||||
|
@ -1577,10 +1580,13 @@ describe('di', () => {
|
|||
static ngDirectiveDef = defineDirective({
|
||||
type: MyDirective,
|
||||
selectors: [['', 'myDirective', '']],
|
||||
factory: () => new MyDirective(injectAttribute('exist'), injectAttribute('myDirective'))
|
||||
factory: () => myDirectiveInstance =
|
||||
new MyDirective(injectAttribute('exist'), injectAttribute('myDirective'))
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => myDirectiveInstance = null);
|
||||
|
||||
it('should inject attribute', () => {
|
||||
let exist = 'wrong' as string | undefined;
|
||||
let nonExist = 'wrong' as string | undefined;
|
||||
|
@ -1843,7 +1849,7 @@ describe('di', () => {
|
|||
(parentTNode as{parent: any}).parent = undefined;
|
||||
|
||||
const injector: any = getOrCreateNodeInjector(); // TODO: Review use of `any` here (#19904)
|
||||
expect(injector).not.toBe(null);
|
||||
expect(injector).not.toEqual(-1);
|
||||
} finally {
|
||||
leaveView(oldView);
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
|||
import {RendererStyleFlags2, RendererType2} from '../../src/render/api';
|
||||
import {AttributeMarker, defineComponent, defineDirective} from '../../src/render3/index';
|
||||
|
||||
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, enableBindings, disableBindings, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, listener, load, loadDirective, projection, projectionDef, reference, text, textBinding, template} from '../../src/render3/instructions';
|
||||
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, enableBindings, disableBindings, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, projection, projectionDef, reference, text, textBinding, template} from '../../src/render3/instructions';
|
||||
import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3, RText, RComment, RNode, RendererStyleFlags3, ProceduralRenderer3} from '../../src/render3/interfaces/renderer';
|
||||
import {HEADER_OFFSET, CONTEXT, DIRECTIVES} from '../../src/render3/interfaces/view';
|
||||
import {HEADER_OFFSET, CONTEXT} from '../../src/render3/interfaces/view';
|
||||
import {sanitizeUrl} from '../../src/sanitization/sanitization';
|
||||
import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
|
||||
|
||||
|
@ -449,8 +449,7 @@ describe('render3 integration test', () => {
|
|||
hostBindings: function(directiveIndex: number, elementIndex: number): void {
|
||||
// host bindings
|
||||
elementProperty(
|
||||
elementIndex, 'title',
|
||||
bind(loadDirective<TodoComponentHostBinding>(directiveIndex).title));
|
||||
elementIndex, 'title', bind(load<TodoComponentHostBinding>(directiveIndex).title));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -750,7 +749,7 @@ describe('render3 integration test', () => {
|
|||
type: TestDirective,
|
||||
selectors: [['', 'testDirective', '']],
|
||||
factory:
|
||||
() => new TestDirective(
|
||||
() => testDirective = new TestDirective(
|
||||
directiveInject(TemplateRef as any), directiveInject(ViewContainerRef as any)),
|
||||
});
|
||||
}
|
||||
|
@ -777,9 +776,6 @@ describe('render3 integration test', () => {
|
|||
template(
|
||||
0, embeddedTemplate, 2, 0, null, [AttributeMarker.SelectOnly, 'testDirective']);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
testDirective = loadDirective<TestDirective>(0);
|
||||
}
|
||||
}, 1, 0, [TestDirective]);
|
||||
|
||||
const fixture = new ComponentFixture(TestCmpt);
|
||||
|
@ -845,6 +841,7 @@ describe('render3 integration test', () => {
|
|||
});
|
||||
|
||||
it('should render inside another ng-container at the root of a delayed view', () => {
|
||||
let testDirective: TestDirective;
|
||||
|
||||
class TestDirective {
|
||||
constructor(private _tplRef: TemplateRef<any>, private _vcRef: ViewContainerRef) {}
|
||||
|
@ -857,12 +854,11 @@ describe('render3 integration test', () => {
|
|||
type: TestDirective,
|
||||
selectors: [['', 'testDirective', '']],
|
||||
factory:
|
||||
() => new TestDirective(
|
||||
() => testDirective = new TestDirective(
|
||||
directiveInject(TemplateRef as any), directiveInject(ViewContainerRef as any)),
|
||||
});
|
||||
}
|
||||
|
||||
let testDirective: TestDirective;
|
||||
|
||||
function embeddedTemplate(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
|
@ -895,9 +891,6 @@ describe('render3 integration test', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
template(0, embeddedTemplate, 4, 0, null, [AttributeMarker.SelectOnly, 'testDirective']);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
testDirective = loadDirective<TestDirective>(0);
|
||||
}
|
||||
}, 1, 0, [TestDirective]);
|
||||
|
||||
function App() { element(0, 'test-cmpt'); }
|
||||
|
@ -926,7 +919,7 @@ describe('render3 integration test', () => {
|
|||
static ngDirectiveDef = defineDirective({
|
||||
type: Directive,
|
||||
selectors: [['', 'dir', '']],
|
||||
factory: () => new Directive(directiveInject(ElementRef)),
|
||||
factory: () => directive = new Directive(directiveInject(ElementRef)),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -940,7 +933,6 @@ describe('render3 integration test', () => {
|
|||
{
|
||||
elementContainerStart(1, [AttributeMarker.SelectOnly, 'dir']);
|
||||
elementContainerEnd();
|
||||
directive = loadDirective<Directive>(0);
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
|
@ -1277,8 +1269,7 @@ describe('render3 integration test', () => {
|
|||
},
|
||||
hostVars: 1,
|
||||
hostBindings: function HostBindingDir_HostBindings(dirIndex: number, elIndex: number) {
|
||||
elementAttribute(
|
||||
elIndex, 'aria-label', bind(loadDirective<HostBindingDir>(dirIndex).label));
|
||||
elementAttribute(elIndex, 'aria-label', bind(load<HostBindingDir>(dirIndex).label));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2112,11 +2103,10 @@ describe('render3 integration test', () => {
|
|||
const context = getContext(hostElm) !;
|
||||
const elementNode = context.lViewData[context.nodeIndex];
|
||||
const elmData = elementNode.data !;
|
||||
const dirs = elmData[DIRECTIVES];
|
||||
|
||||
expect(dirs).toContain(myDir1Instance);
|
||||
expect(dirs).toContain(myDir2Instance);
|
||||
expect(dirs).toContain(myDir3Instance);
|
||||
expect(elmData).toContain(myDir1Instance);
|
||||
expect(elmData).toContain(myDir2Instance);
|
||||
expect(elmData).toContain(myDir3Instance);
|
||||
|
||||
expect(Array.isArray((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy();
|
||||
expect(Array.isArray((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy();
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
|
||||
import {EventEmitter} from '@angular/core';
|
||||
|
||||
import {AttributeMarker, PublicFeature, defineComponent, defineDirective} from '../../src/render3/index';
|
||||
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, listener, loadDirective, reference, text, textBinding} from '../../src/render3/instructions';
|
||||
import {AttributeMarker, PublicFeature, defineComponent, template, defineDirective} from '../../src/render3/index';
|
||||
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, listener, load, reference, text, textBinding} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {pureFunction1, pureFunction2} from '../../src/render3/pure_function';
|
||||
|
||||
import {ComponentFixture, TemplateFixture, createComponent, renderToHtml} from './render_util';
|
||||
import {ComponentFixture, TemplateFixture, createComponent, renderToHtml, createDirective} from './render_util';
|
||||
import {NgForOf} from './common_with_def';
|
||||
|
||||
describe('elementProperty', () => {
|
||||
|
||||
|
@ -109,8 +110,7 @@ describe('elementProperty', () => {
|
|||
factory: () => directiveInstance = new Directive,
|
||||
hostVars: 1,
|
||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||
elementProperty(
|
||||
elementIndex, 'className', bind(loadDirective<Directive>(directiveIndex).klass));
|
||||
elementProperty(elementIndex, 'className', bind(load<Directive>(directiveIndex).klass));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ describe('elementProperty', () => {
|
|||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
const instance = loadDirective(dirIndex) as HostBindingComp;
|
||||
const instance = load(dirIndex) as HostBindingComp;
|
||||
elementProperty(elIndex, 'id', bind(instance.id));
|
||||
},
|
||||
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
|
||||
|
@ -153,6 +153,71 @@ describe('elementProperty', () => {
|
|||
expect(fixture.hostElement.id).toBe('other-id');
|
||||
});
|
||||
|
||||
it('should support host bindings on multiple nodes', () => {
|
||||
let hostBindingDir !: HostBindingDir;
|
||||
|
||||
class HostBindingDir {
|
||||
// @HostBinding()
|
||||
id = 'foo';
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: HostBindingDir,
|
||||
selectors: [['', 'hostBindingDir', '']],
|
||||
factory: () => hostBindingDir = new HostBindingDir(),
|
||||
hostVars: 1,
|
||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
|
||||
},
|
||||
features: [PublicFeature]
|
||||
});
|
||||
}
|
||||
|
||||
const SomeDir = createDirective('someDir');
|
||||
|
||||
class HostBindingComp {
|
||||
// @HostBinding()
|
||||
title = 'my-title';
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: HostBindingComp,
|
||||
selectors: [['host-binding-comp']],
|
||||
factory: () => new HostBindingComp(),
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
const ctx = load(dirIndex) as HostBindingComp;
|
||||
elementProperty(elIndex, 'title', bind(ctx.title));
|
||||
},
|
||||
template: (rf: RenderFlags, ctx: HostBindingComp) => {},
|
||||
features: [PublicFeature]
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <div hostBindingDir></div>
|
||||
* <div someDir></div>
|
||||
* <host-binding-comp></host-binding-comp>
|
||||
*/
|
||||
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(0, 'div', ['hostBindingDir', '']);
|
||||
element(1, 'div', ['someDir', '']);
|
||||
element(2, 'host-binding-comp');
|
||||
}
|
||||
}, 3, 0, [HostBindingDir, SomeDir, HostBindingComp]);
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
const hostBindingDiv = fixture.hostElement.querySelector('div') as HTMLElement;
|
||||
const hostBindingComp = fixture.hostElement.querySelector('host-binding-comp') as HTMLElement;
|
||||
expect(hostBindingDiv.id).toEqual('foo');
|
||||
expect(hostBindingComp.title).toEqual('my-title');
|
||||
|
||||
hostBindingDir.id = 'bar';
|
||||
fixture.update();
|
||||
expect(hostBindingDiv.id).toEqual('bar');
|
||||
});
|
||||
|
||||
it('should support host bindings on second template pass', () => {
|
||||
class HostBindingDir {
|
||||
// @HostBinding()
|
||||
|
@ -164,8 +229,7 @@ describe('elementProperty', () => {
|
|||
factory: () => new HostBindingDir(),
|
||||
hostVars: 1,
|
||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||
elementProperty(
|
||||
elementIndex, 'id', bind(loadDirective<HostBindingDir>(directiveIndex).id));
|
||||
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
|
||||
},
|
||||
features: [PublicFeature]
|
||||
});
|
||||
|
@ -195,6 +259,55 @@ describe('elementProperty', () => {
|
|||
expect(divs[1].id).toEqual('foo');
|
||||
});
|
||||
|
||||
it('should support host bindings in for loop', () => {
|
||||
class HostBindingDir {
|
||||
// @HostBinding()
|
||||
id = 'foo';
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: HostBindingDir,
|
||||
selectors: [['', 'hostBindingDir', '']],
|
||||
factory: () => new HostBindingDir(),
|
||||
hostVars: 1,
|
||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
|
||||
},
|
||||
features: [PublicFeature]
|
||||
});
|
||||
}
|
||||
|
||||
function NgForTemplate(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'div');
|
||||
{ element(1, 'p', ['hostBindingDir', '']); }
|
||||
elementEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <div *ngFor="let row of rows">
|
||||
* <p hostBindingDir></p>
|
||||
* </div>
|
||||
*/
|
||||
const App = createComponent('parent', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
template(0, NgForTemplate, 2, 0, null, ['ngForOf', '']);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'ngForOf', bind(ctx.rows));
|
||||
}
|
||||
}, 1, 1, [HostBindingDir, NgForOf]);
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
fixture.component.rows = [1, 2, 3];
|
||||
fixture.update();
|
||||
|
||||
const paragraphs = fixture.hostElement.querySelectorAll('p');
|
||||
expect(paragraphs[0].id).toEqual('foo');
|
||||
expect(paragraphs[1].id).toEqual('foo');
|
||||
expect(paragraphs[2].id).toEqual('foo');
|
||||
});
|
||||
|
||||
it('should support component with host bindings and array literals', () => {
|
||||
const ff = (v: any) => ['Nancy', v, 'Ned'];
|
||||
|
||||
|
@ -210,7 +323,7 @@ describe('elementProperty', () => {
|
|||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
const ctx = loadDirective(dirIndex) as HostBindingComp;
|
||||
const ctx = load(dirIndex) as HostBindingComp;
|
||||
elementProperty(elIndex, 'id', bind(ctx.id));
|
||||
},
|
||||
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
|
||||
|
@ -283,7 +396,7 @@ describe('elementProperty', () => {
|
|||
vars: 0,
|
||||
hostVars: 8,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
const ctx = loadDirective(dirIndex) as HostBindingComp;
|
||||
const ctx = load(dirIndex) as HostBindingComp;
|
||||
// LViewData: [..., id, dir, title, ctx.id, pf1, ctx.title, ctx.otherTitle, pf2]
|
||||
elementProperty(elIndex, 'id', bind(pureFunction1(3, ff, ctx.id)));
|
||||
elementProperty(elIndex, 'dir', bind(ctx.dir));
|
||||
|
@ -359,7 +472,7 @@ describe('elementProperty', () => {
|
|||
hostVars: 3,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
// LViewData: [..., id, ctx.id, pf1]
|
||||
const ctx = loadDirective(dirIndex) as HostBindingComp;
|
||||
const ctx = load(dirIndex) as HostBindingComp;
|
||||
elementProperty(elIndex, 'id', bind(pureFunction1(1, ff, ctx.id)));
|
||||
},
|
||||
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
|
||||
|
@ -387,7 +500,7 @@ describe('elementProperty', () => {
|
|||
hostVars: 3,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
// LViewData [..., title, ctx.title, pf1]
|
||||
const ctx = loadDirective(dirIndex) as HostBindingDir;
|
||||
const ctx = load(dirIndex) as HostBindingDir;
|
||||
elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)));
|
||||
}
|
||||
});
|
||||
|
@ -448,7 +561,7 @@ describe('elementProperty', () => {
|
|||
hostVars: 6,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
// LViewData: [..., id, title, ctx.id, pf1, ctx.title, pf1]
|
||||
const ctx = loadDirective(dirIndex) as HostBindingComp;
|
||||
const ctx = load(dirIndex) as HostBindingComp;
|
||||
elementProperty(
|
||||
elIndex, 'id', bind(ctx.condition ? pureFunction1(2, ff, ctx.id) : 'green'));
|
||||
elementProperty(
|
||||
|
|
|
@ -14,7 +14,7 @@ import {directiveInject} from '../../src/render3/di';
|
|||
|
||||
import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges} from '../../src/render3/index';
|
||||
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective, loadElement, loadQueryList, reference, registerContentQuery, template} from '../../src/render3/instructions';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadElement, loadQueryList, reference, registerContentQuery, template} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {query, queryRefresh} from '../../src/render3/query';
|
||||
import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
|
||||
|
@ -1914,7 +1914,7 @@ describe('query', () => {
|
|||
contentQueries: () => { registerContentQuery(query(null, ['foo'], true)); },
|
||||
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||
let tmp: any;
|
||||
withContentInstance = loadDirective<WithContentDirective>(dirIndex);
|
||||
withContentInstance = load<WithContentDirective>(dirIndex);
|
||||
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||
(withContentInstance.foos = tmp);
|
||||
}
|
||||
|
@ -1935,7 +1935,7 @@ describe('query', () => {
|
|||
contentQueries: () => { registerContentQuery(query(null, ['foo'], false)); },
|
||||
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||
let tmp: any;
|
||||
shallowCompInstance = loadDirective<ShallowComp>(dirIndex);
|
||||
shallowCompInstance = load<ShallowComp>(dirIndex);
|
||||
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||
(shallowCompInstance.foos = tmp);
|
||||
}
|
||||
|
@ -2116,7 +2116,7 @@ describe('query', () => {
|
|||
},
|
||||
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||
let tmp: any;
|
||||
const instance = loadDirective<QueryDirective>(dirIndex);
|
||||
const instance = load<QueryDirective>(dirIndex);
|
||||
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||
(instance.fooBars = tmp);
|
||||
},
|
||||
|
@ -2180,7 +2180,7 @@ describe('query', () => {
|
|||
},
|
||||
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||
let tmp: any;
|
||||
const instance = loadDirective<QueryDirective>(dirIndex);
|
||||
const instance = load<QueryDirective>(dirIndex);
|
||||
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||
(instance.fooBars = tmp);
|
||||
},
|
||||
|
@ -2233,7 +2233,7 @@ describe('query', () => {
|
|||
},
|
||||
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||
let tmp: any;
|
||||
const instance = loadDirective<ShallowQueryDirective>(dirIndex);
|
||||
const instance = load<ShallowQueryDirective>(dirIndex);
|
||||
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||
(instance.foos = tmp);
|
||||
},
|
||||
|
@ -2253,7 +2253,7 @@ describe('query', () => {
|
|||
},
|
||||
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||
let tmp: any;
|
||||
const instance = loadDirective<DeepQueryDirective>(dirIndex);
|
||||
const instance = load<DeepQueryDirective>(dirIndex);
|
||||
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||
(instance.foos = tmp);
|
||||
},
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
import {Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
|
||||
import {ViewEncapsulation} from '../../src/metadata';
|
||||
import {directiveInject} from '../../src/render3/di';
|
||||
import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver} from '../../src/render3/index';
|
||||
import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, load} from '../../src/render3/index';
|
||||
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, loadDirective, nextContext, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, nextContext, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
|
||||
import {NgModuleFactory} from '../../src/render3/ng_module_ref';
|
||||
|
@ -1728,7 +1728,7 @@ describe('ViewContainerRef', () => {
|
|||
hostVars: 1,
|
||||
attributes: ['id', 'attribute'],
|
||||
hostBindings: function(dirIndex, elIndex) {
|
||||
const cmptInstance = loadDirective<HostBindingCmpt>(dirIndex);
|
||||
const cmptInstance = load<HostBindingCmpt>(dirIndex);
|
||||
elementProperty(elIndex, 'title', bind(cmptInstance.title));
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue