- Improve `WrappedValue` by adding `unwrap` symetrical to `wrap`. - remove dead code - `ValueUnwrapper` The property `wrapped` is an implementation details and should never be accessed directly - use `unwrap(wrappedValue)`. Will change to protected in Angular 7. PR Close #20997
562 lines
21 KiB
TypeScript
562 lines
21 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import {ChangeDetectorRef, SimpleChange, SimpleChanges, WrappedValue} from '../change_detection/change_detection';
|
|
import {Injector, resolveForwardRef} from '../di';
|
|
import {ElementRef} from '../linker/element_ref';
|
|
import {TemplateRef} from '../linker/template_ref';
|
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
|
import {Renderer as RendererV1, Renderer2} from '../render/api';
|
|
import {stringify} from '../util';
|
|
import {createChangeDetectorRef, createInjector, createRendererV1} from './refs';
|
|
import {BindingDef, BindingFlags, DepDef, DepFlags, NodeDef, NodeFlags, OutputDef, OutputType, ProviderData, QueryValueType, Services, ViewData, ViewFlags, ViewState, asElementData, asProviderData, shouldCallLifecycleInitHook} from './types';
|
|
import {calcBindingFlags, checkBinding, dispatchEvent, isComponentView, splitDepsDsl, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
|
|
|
|
const RendererV1TokenKey = tokenKey(RendererV1);
|
|
const Renderer2TokenKey = tokenKey(Renderer2);
|
|
const ElementRefTokenKey = tokenKey(ElementRef);
|
|
const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
|
|
const TemplateRefTokenKey = tokenKey(TemplateRef);
|
|
const ChangeDetectorRefTokenKey = tokenKey(ChangeDetectorRef);
|
|
const InjectorRefTokenKey = tokenKey(Injector);
|
|
|
|
export function directiveDef(
|
|
checkIndex: number, flags: NodeFlags,
|
|
matchedQueries: null | [string | number, QueryValueType][], childCount: number, ctor: any,
|
|
deps: ([DepFlags, any] | any)[], props?: null | {[name: string]: [number, string]},
|
|
outputs?: null | {[name: string]: string}): NodeDef {
|
|
const bindings: BindingDef[] = [];
|
|
if (props) {
|
|
for (let prop in props) {
|
|
const [bindingIndex, nonMinifiedName] = props[prop];
|
|
bindings[bindingIndex] = {
|
|
flags: BindingFlags.TypeProperty,
|
|
name: prop, nonMinifiedName,
|
|
ns: null,
|
|
securityContext: null,
|
|
suffix: null
|
|
};
|
|
}
|
|
}
|
|
const outputDefs: OutputDef[] = [];
|
|
if (outputs) {
|
|
for (let propName in outputs) {
|
|
outputDefs.push(
|
|
{type: OutputType.DirectiveOutput, propName, target: null, eventName: outputs[propName]});
|
|
}
|
|
}
|
|
flags |= NodeFlags.TypeDirective;
|
|
return _def(
|
|
checkIndex, flags, matchedQueries, childCount, ctor, ctor, deps, bindings, outputDefs);
|
|
}
|
|
|
|
export function pipeDef(flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[]): NodeDef {
|
|
flags |= NodeFlags.TypePipe;
|
|
return _def(-1, flags, null, 0, ctor, ctor, deps);
|
|
}
|
|
|
|
export function providerDef(
|
|
flags: NodeFlags, matchedQueries: null | [string | number, QueryValueType][], token: any,
|
|
value: any, deps: ([DepFlags, any] | any)[]): NodeDef {
|
|
return _def(-1, flags, matchedQueries, 0, token, value, deps);
|
|
}
|
|
|
|
export function _def(
|
|
checkIndex: number, flags: NodeFlags,
|
|
matchedQueriesDsl: [string | number, QueryValueType][] | null, childCount: number, token: any,
|
|
value: any, deps: ([DepFlags, any] | any)[], bindings?: BindingDef[],
|
|
outputs?: OutputDef[]): NodeDef {
|
|
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
|
|
if (!outputs) {
|
|
outputs = [];
|
|
}
|
|
if (!bindings) {
|
|
bindings = [];
|
|
}
|
|
// Need to resolve forwardRefs as e.g. for `useValue` we
|
|
// lowered the expression and then stopped evaluating it,
|
|
// i.e. also didn't unwrap it.
|
|
value = resolveForwardRef(value);
|
|
|
|
const depDefs = splitDepsDsl(deps, stringify(token));
|
|
|
|
return {
|
|
// will bet set by the view definition
|
|
nodeIndex: -1,
|
|
parent: null,
|
|
renderParent: null,
|
|
bindingIndex: -1,
|
|
outputIndex: -1,
|
|
// regular values
|
|
checkIndex,
|
|
flags,
|
|
childFlags: 0,
|
|
directChildFlags: 0,
|
|
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references,
|
|
ngContentIndex: -1, childCount, bindings,
|
|
bindingFlags: calcBindingFlags(bindings), outputs,
|
|
element: null,
|
|
provider: {token, value, deps: depDefs},
|
|
text: null,
|
|
query: null,
|
|
ngContent: null
|
|
};
|
|
}
|
|
|
|
export function createProviderInstance(view: ViewData, def: NodeDef): any {
|
|
return _createProviderInstance(view, def);
|
|
}
|
|
|
|
export function createPipeInstance(view: ViewData, def: NodeDef): any {
|
|
// deps are looked up from component.
|
|
let compView = view;
|
|
while (compView.parent && !isComponentView(compView)) {
|
|
compView = compView.parent;
|
|
}
|
|
// pipes can see the private services of the component
|
|
const allowPrivateServices = true;
|
|
// pipes are always eager and classes!
|
|
return createClass(
|
|
compView.parent !, viewParentEl(compView) !, allowPrivateServices, def.provider !.value,
|
|
def.provider !.deps);
|
|
}
|
|
|
|
export function createDirectiveInstance(view: ViewData, def: NodeDef): any {
|
|
// components can see other private services, other directives can't.
|
|
const allowPrivateServices = (def.flags & NodeFlags.Component) > 0;
|
|
// directives are always eager and classes!
|
|
const instance = createClass(
|
|
view, def.parent !, allowPrivateServices, def.provider !.value, def.provider !.deps);
|
|
if (def.outputs.length) {
|
|
for (let i = 0; i < def.outputs.length; i++) {
|
|
const output = def.outputs[i];
|
|
const subscription = instance[output.propName !].subscribe(
|
|
eventHandlerClosure(view, def.parent !.nodeIndex, output.eventName));
|
|
view.disposables ![def.outputIndex + i] = subscription.unsubscribe.bind(subscription);
|
|
}
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
function eventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
|
return (event: any) => dispatchEvent(view, index, eventName, event);
|
|
}
|
|
|
|
export function checkAndUpdateDirectiveInline(
|
|
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
|
|
v7: any, v8: any, v9: any): boolean {
|
|
const providerData = asProviderData(view, def.nodeIndex);
|
|
const directive = providerData.instance;
|
|
let changed = false;
|
|
let changes: SimpleChanges = undefined !;
|
|
const bindLen = def.bindings.length;
|
|
if (bindLen > 0 && checkBinding(view, def, 0, v0)) {
|
|
changed = true;
|
|
changes = updateProp(view, providerData, def, 0, v0, changes);
|
|
}
|
|
if (bindLen > 1 && checkBinding(view, def, 1, v1)) {
|
|
changed = true;
|
|
changes = updateProp(view, providerData, def, 1, v1, changes);
|
|
}
|
|
if (bindLen > 2 && checkBinding(view, def, 2, v2)) {
|
|
changed = true;
|
|
changes = updateProp(view, providerData, def, 2, v2, changes);
|
|
}
|
|
if (bindLen > 3 && checkBinding(view, def, 3, v3)) {
|
|
changed = true;
|
|
changes = updateProp(view, providerData, def, 3, v3, changes);
|
|
}
|
|
if (bindLen > 4 && checkBinding(view, def, 4, v4)) {
|
|
changed = true;
|
|
changes = updateProp(view, providerData, def, 4, v4, changes);
|
|
}
|
|
if (bindLen > 5 && checkBinding(view, def, 5, v5)) {
|
|
changed = true;
|
|
changes = updateProp(view, providerData, def, 5, v5, changes);
|
|
}
|
|
if (bindLen > 6 && checkBinding(view, def, 6, v6)) {
|
|
changed = true;
|
|
changes = updateProp(view, providerData, def, 6, v6, changes);
|
|
}
|
|
if (bindLen > 7 && checkBinding(view, def, 7, v7)) {
|
|
changed = true;
|
|
changes = updateProp(view, providerData, def, 7, v7, changes);
|
|
}
|
|
if (bindLen > 8 && checkBinding(view, def, 8, v8)) {
|
|
changed = true;
|
|
changes = updateProp(view, providerData, def, 8, v8, changes);
|
|
}
|
|
if (bindLen > 9 && checkBinding(view, def, 9, v9)) {
|
|
changed = true;
|
|
changes = updateProp(view, providerData, def, 9, v9, changes);
|
|
}
|
|
if (changes) {
|
|
directive.ngOnChanges(changes);
|
|
}
|
|
if ((def.flags & NodeFlags.OnInit) &&
|
|
shouldCallLifecycleInitHook(view, ViewState.InitState_CallingOnInit, def.nodeIndex)) {
|
|
directive.ngOnInit();
|
|
}
|
|
if (def.flags & NodeFlags.DoCheck) {
|
|
directive.ngDoCheck();
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
export function checkAndUpdateDirectiveDynamic(
|
|
view: ViewData, def: NodeDef, values: any[]): boolean {
|
|
const providerData = asProviderData(view, def.nodeIndex);
|
|
const directive = providerData.instance;
|
|
let changed = false;
|
|
let changes: SimpleChanges = undefined !;
|
|
for (let i = 0; i < values.length; i++) {
|
|
if (checkBinding(view, def, i, values[i])) {
|
|
changed = true;
|
|
changes = updateProp(view, providerData, def, i, values[i], changes);
|
|
}
|
|
}
|
|
if (changes) {
|
|
directive.ngOnChanges(changes);
|
|
}
|
|
if ((def.flags & NodeFlags.OnInit) &&
|
|
shouldCallLifecycleInitHook(view, ViewState.InitState_CallingOnInit, def.nodeIndex)) {
|
|
directive.ngOnInit();
|
|
}
|
|
if (def.flags & NodeFlags.DoCheck) {
|
|
directive.ngDoCheck();
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
function _createProviderInstance(view: ViewData, def: NodeDef): any {
|
|
// private services can see other private services
|
|
const allowPrivateServices = (def.flags & NodeFlags.PrivateProvider) > 0;
|
|
const providerDef = def.provider;
|
|
switch (def.flags & NodeFlags.Types) {
|
|
case NodeFlags.TypeClassProvider:
|
|
return createClass(
|
|
view, def.parent !, allowPrivateServices, providerDef !.value, providerDef !.deps);
|
|
case NodeFlags.TypeFactoryProvider:
|
|
return callFactory(
|
|
view, def.parent !, allowPrivateServices, providerDef !.value, providerDef !.deps);
|
|
case NodeFlags.TypeUseExistingProvider:
|
|
return resolveDep(view, def.parent !, allowPrivateServices, providerDef !.deps[0]);
|
|
case NodeFlags.TypeValueProvider:
|
|
return providerDef !.value;
|
|
}
|
|
}
|
|
|
|
function createClass(
|
|
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, ctor: any, deps: DepDef[]): any {
|
|
const len = deps.length;
|
|
switch (len) {
|
|
case 0:
|
|
return new ctor();
|
|
case 1:
|
|
return new ctor(resolveDep(view, elDef, allowPrivateServices, deps[0]));
|
|
case 2:
|
|
return new ctor(
|
|
resolveDep(view, elDef, allowPrivateServices, deps[0]),
|
|
resolveDep(view, elDef, allowPrivateServices, deps[1]));
|
|
case 3:
|
|
return new ctor(
|
|
resolveDep(view, elDef, allowPrivateServices, deps[0]),
|
|
resolveDep(view, elDef, allowPrivateServices, deps[1]),
|
|
resolveDep(view, elDef, allowPrivateServices, deps[2]));
|
|
default:
|
|
const depValues = new Array(len);
|
|
for (let i = 0; i < len; i++) {
|
|
depValues[i] = resolveDep(view, elDef, allowPrivateServices, deps[i]);
|
|
}
|
|
return new ctor(...depValues);
|
|
}
|
|
}
|
|
|
|
function callFactory(
|
|
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, factory: any,
|
|
deps: DepDef[]): any {
|
|
const len = deps.length;
|
|
switch (len) {
|
|
case 0:
|
|
return factory();
|
|
case 1:
|
|
return factory(resolveDep(view, elDef, allowPrivateServices, deps[0]));
|
|
case 2:
|
|
return factory(
|
|
resolveDep(view, elDef, allowPrivateServices, deps[0]),
|
|
resolveDep(view, elDef, allowPrivateServices, deps[1]));
|
|
case 3:
|
|
return factory(
|
|
resolveDep(view, elDef, allowPrivateServices, deps[0]),
|
|
resolveDep(view, elDef, allowPrivateServices, deps[1]),
|
|
resolveDep(view, elDef, allowPrivateServices, deps[2]));
|
|
default:
|
|
const depValues = Array(len);
|
|
for (let i = 0; i < len; i++) {
|
|
depValues[i] = resolveDep(view, elDef, allowPrivateServices, deps[i]);
|
|
}
|
|
return factory(...depValues);
|
|
}
|
|
}
|
|
|
|
// This default value is when checking the hierarchy for a token.
|
|
//
|
|
// It means both:
|
|
// - the token is not provided by the current injector,
|
|
// - only the element injectors should be checked (ie do not check module injectors
|
|
//
|
|
// mod1
|
|
// /
|
|
// el1 mod2
|
|
// \ /
|
|
// el2
|
|
//
|
|
// When requesting el2.injector.get(token), we should check in the following order and return the
|
|
// first found value:
|
|
// - el2.injector.get(token, default)
|
|
// - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
|
|
// - mod2.injector.get(token, default)
|
|
export const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
|
|
|
|
export function resolveDep(
|
|
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef,
|
|
notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
|
if (depDef.flags & DepFlags.Value) {
|
|
return depDef.token;
|
|
}
|
|
const startView = view;
|
|
if (depDef.flags & DepFlags.Optional) {
|
|
notFoundValue = null;
|
|
}
|
|
const tokenKey = depDef.tokenKey;
|
|
|
|
if (tokenKey === ChangeDetectorRefTokenKey) {
|
|
// directives on the same element as a component should be able to control the change detector
|
|
// of that component as well.
|
|
allowPrivateServices = !!(elDef && elDef.element !.componentView);
|
|
}
|
|
|
|
if (elDef && (depDef.flags & DepFlags.SkipSelf)) {
|
|
allowPrivateServices = false;
|
|
elDef = elDef.parent !;
|
|
}
|
|
|
|
while (view) {
|
|
if (elDef) {
|
|
switch (tokenKey) {
|
|
case RendererV1TokenKey: {
|
|
const compView = findCompView(view, elDef, allowPrivateServices);
|
|
return createRendererV1(compView);
|
|
}
|
|
case Renderer2TokenKey: {
|
|
const compView = findCompView(view, elDef, allowPrivateServices);
|
|
return compView.renderer;
|
|
}
|
|
case ElementRefTokenKey:
|
|
return new ElementRef(asElementData(view, elDef.nodeIndex).renderElement);
|
|
case ViewContainerRefTokenKey:
|
|
return asElementData(view, elDef.nodeIndex).viewContainer;
|
|
case TemplateRefTokenKey: {
|
|
if (elDef.element !.template) {
|
|
return asElementData(view, elDef.nodeIndex).template;
|
|
}
|
|
break;
|
|
}
|
|
case ChangeDetectorRefTokenKey: {
|
|
let cdView = findCompView(view, elDef, allowPrivateServices);
|
|
return createChangeDetectorRef(cdView);
|
|
}
|
|
case InjectorRefTokenKey:
|
|
return createInjector(view, elDef);
|
|
default:
|
|
const providerDef =
|
|
(allowPrivateServices ? elDef.element !.allProviders :
|
|
elDef.element !.publicProviders) ![tokenKey];
|
|
if (providerDef) {
|
|
let providerData = asProviderData(view, providerDef.nodeIndex);
|
|
if (!providerData) {
|
|
providerData = {instance: _createProviderInstance(view, providerDef)};
|
|
view.nodes[providerDef.nodeIndex] = providerData as any;
|
|
}
|
|
return providerData.instance;
|
|
}
|
|
}
|
|
}
|
|
allowPrivateServices = isComponentView(view);
|
|
elDef = viewParentEl(view) !;
|
|
view = view.parent !;
|
|
}
|
|
|
|
const value = startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR);
|
|
|
|
if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
|
|
notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
|
|
// Return the value from the root element injector when
|
|
// - it provides it
|
|
// (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
|
// - the module injector should not be checked
|
|
// (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
|
return value;
|
|
}
|
|
|
|
return startView.root.ngModule.injector.get(depDef.token, notFoundValue);
|
|
}
|
|
|
|
function findCompView(view: ViewData, elDef: NodeDef, allowPrivateServices: boolean) {
|
|
let compView: ViewData;
|
|
if (allowPrivateServices) {
|
|
compView = asElementData(view, elDef.nodeIndex).componentView;
|
|
} else {
|
|
compView = view;
|
|
while (compView.parent && !isComponentView(compView)) {
|
|
compView = compView.parent;
|
|
}
|
|
}
|
|
return compView;
|
|
}
|
|
|
|
function updateProp(
|
|
view: ViewData, providerData: ProviderData, def: NodeDef, bindingIdx: number, value: any,
|
|
changes: SimpleChanges): SimpleChanges {
|
|
if (def.flags & NodeFlags.Component) {
|
|
const compView = asElementData(view, def.parent !.nodeIndex).componentView;
|
|
if (compView.def.flags & ViewFlags.OnPush) {
|
|
compView.state |= ViewState.ChecksEnabled;
|
|
}
|
|
}
|
|
const binding = def.bindings[bindingIdx];
|
|
const propName = binding.name !;
|
|
// Note: This is still safe with Closure Compiler as
|
|
// the user passed in the property name as an object has to `providerDef`,
|
|
// so Closure Compiler will have renamed the property correctly already.
|
|
providerData.instance[propName] = value;
|
|
if (def.flags & NodeFlags.OnChanges) {
|
|
changes = changes || {};
|
|
const oldValue = WrappedValue.unwrap(view.oldValues[def.bindingIndex + bindingIdx]);
|
|
const binding = def.bindings[bindingIdx];
|
|
changes[binding.nonMinifiedName !] =
|
|
new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0);
|
|
}
|
|
view.oldValues[def.bindingIndex + bindingIdx] = value;
|
|
return changes;
|
|
}
|
|
|
|
// This function calls the ngAfterContentCheck, ngAfterContentInit,
|
|
// ngAfterViewCheck, and ngAfterViewInit lifecycle hooks (depending on the node
|
|
// flags in lifecycle). Unlike ngDoCheck, ngOnChanges and ngOnInit, which are
|
|
// called during a pre-order traversal of the view tree (that is calling the
|
|
// parent hooks before the child hooks) these events are sent in using a
|
|
// post-order traversal of the tree (children before parents). This changes the
|
|
// meaning of initIndex in the view state. For ngOnInit, initIndex tracks the
|
|
// expected nodeIndex which a ngOnInit should be called. When sending
|
|
// ngAfterContentInit and ngAfterViewInit it is the expected count of
|
|
// ngAfterContentInit or ngAfterViewInit methods that have been called. This
|
|
// ensure that dispite being called recursively or after picking up after an
|
|
// exception, the ngAfterContentInit or ngAfterViewInit will be called on the
|
|
// correct nodes. Consider for example, the following (where E is an element
|
|
// and D is a directive)
|
|
// Tree: pre-order index post-order index
|
|
// E1 0 6
|
|
// E2 1 1
|
|
// D3 2 0
|
|
// E4 3 5
|
|
// E5 4 4
|
|
// E6 5 2
|
|
// E7 6 3
|
|
// As can be seen, the post-order index has an unclear relationship to the
|
|
// pre-order index (postOrderIndex === preOrderIndex - parentCount +
|
|
// childCount). Since number of calls to ngAfterContentInit and ngAfterViewInit
|
|
// are stable (will be the same for the same view regardless of exceptions or
|
|
// recursion) we just need to count them which will roughly correspond to the
|
|
// post-order index (it skips elements and directives that do not have
|
|
// lifecycle hooks).
|
|
//
|
|
// For example, if an exception is raised in the E6.onAfterViewInit() the
|
|
// initIndex is left at 3 (by shouldCallLifecycleInitHook() which set it to
|
|
// initIndex + 1). When checkAndUpdateView() is called again D3, E2 and E6 will
|
|
// not have their ngAfterViewInit() called but, starting with E7, the rest of
|
|
// the view will begin getting ngAfterViewInit() called until a check and
|
|
// pass is complete.
|
|
//
|
|
// This algorthim also handles recursion. Consider if E4's ngAfterViewInit()
|
|
// indirectly calls E1's ChangeDetectorRef.detectChanges(). The expected
|
|
// initIndex is set to 6, the recusive checkAndUpdateView() starts walk again.
|
|
// D3, E2, E6, E7, E5 and E4 are skipped, ngAfterViewInit() is called on E1.
|
|
// When the recursion returns the initIndex will be 7 so E1 is skipped as it
|
|
// has already been called in the recursively called checkAnUpdateView().
|
|
export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: NodeFlags) {
|
|
if (!(view.def.nodeFlags & lifecycles)) {
|
|
return;
|
|
}
|
|
const nodes = view.def.nodes;
|
|
let initIndex = 0;
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
const nodeDef = nodes[i];
|
|
let parent = nodeDef.parent;
|
|
if (!parent && nodeDef.flags & lifecycles) {
|
|
// matching root node (e.g. a pipe)
|
|
callProviderLifecycles(view, i, nodeDef.flags & lifecycles, initIndex++);
|
|
}
|
|
if ((nodeDef.childFlags & lifecycles) === 0) {
|
|
// no child matches one of the lifecycles
|
|
i += nodeDef.childCount;
|
|
}
|
|
while (parent && (parent.flags & NodeFlags.TypeElement) &&
|
|
i === parent.nodeIndex + parent.childCount) {
|
|
// last child of an element
|
|
if (parent.directChildFlags & lifecycles) {
|
|
initIndex = callElementProvidersLifecycles(view, parent, lifecycles, initIndex);
|
|
}
|
|
parent = parent.parent;
|
|
}
|
|
}
|
|
}
|
|
|
|
function callElementProvidersLifecycles(
|
|
view: ViewData, elDef: NodeDef, lifecycles: NodeFlags, initIndex: number): number {
|
|
for (let i = elDef.nodeIndex + 1; i <= elDef.nodeIndex + elDef.childCount; i++) {
|
|
const nodeDef = view.def.nodes[i];
|
|
if (nodeDef.flags & lifecycles) {
|
|
callProviderLifecycles(view, i, nodeDef.flags & lifecycles, initIndex++);
|
|
}
|
|
// only visit direct children
|
|
i += nodeDef.childCount;
|
|
}
|
|
return initIndex;
|
|
}
|
|
|
|
function callProviderLifecycles(
|
|
view: ViewData, index: number, lifecycles: NodeFlags, initIndex: number) {
|
|
const providerData = asProviderData(view, index);
|
|
if (!providerData) {
|
|
return;
|
|
}
|
|
const provider = providerData.instance;
|
|
if (!provider) {
|
|
return;
|
|
}
|
|
Services.setCurrentNode(view, index);
|
|
if (lifecycles & NodeFlags.AfterContentInit &&
|
|
shouldCallLifecycleInitHook(view, ViewState.InitState_CallingAfterContentInit, initIndex)) {
|
|
provider.ngAfterContentInit();
|
|
}
|
|
if (lifecycles & NodeFlags.AfterContentChecked) {
|
|
provider.ngAfterContentChecked();
|
|
}
|
|
if (lifecycles & NodeFlags.AfterViewInit &&
|
|
shouldCallLifecycleInitHook(view, ViewState.InitState_CallingAfterViewInit, initIndex)) {
|
|
provider.ngAfterViewInit();
|
|
}
|
|
if (lifecycles & NodeFlags.AfterViewChecked) {
|
|
provider.ngAfterViewChecked();
|
|
}
|
|
if (lifecycles & NodeFlags.OnDestroy) {
|
|
provider.ngOnDestroy();
|
|
}
|
|
}
|