2017-01-20 13:10:57 -08:00
|
|
|
/**
|
|
|
|
|
* @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 {SimpleChange, SimpleChanges} from '../change_detection/change_detection';
|
|
|
|
|
import {Injector} from '../di';
|
|
|
|
|
import {stringify} from '../facade/lang';
|
|
|
|
|
import {ElementRef} from '../linker/element_ref';
|
2017-01-23 16:59:20 -08:00
|
|
|
import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors';
|
|
|
|
|
import {QueryList} from '../linker/query_list';
|
2017-01-20 13:10:57 -08:00
|
|
|
import {TemplateRef} from '../linker/template_ref';
|
|
|
|
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
|
|
|
|
import {Renderer} from '../render/api';
|
|
|
|
|
|
2017-01-23 16:59:20 -08:00
|
|
|
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderOutputDef, QueryBindingType, QueryDef, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags} from './types';
|
|
|
|
|
import {checkAndUpdateBinding, checkAndUpdateBindingWithChange, declaredViewContainer, setBindingDebugInfo} from './util';
|
2017-01-20 13:10:57 -08:00
|
|
|
|
|
|
|
|
const _tokenKeyCache = new Map<any, string>();
|
|
|
|
|
|
|
|
|
|
const RendererTokenKey = tokenKey(Renderer);
|
|
|
|
|
const ElementRefTokenKey = tokenKey(ElementRef);
|
|
|
|
|
const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
|
|
|
|
|
const TemplateRefTokenKey = tokenKey(TemplateRef);
|
|
|
|
|
|
|
|
|
|
export function providerDef(
|
2017-01-23 16:59:20 -08:00
|
|
|
flags: NodeFlags, matchedQueries: [string, QueryValueType][], ctor: any,
|
|
|
|
|
deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
|
|
|
|
|
outputs?: {[name: string]: string},
|
|
|
|
|
contentQueries?: {[name: string]: [string, QueryBindingType]}, component?: () => ViewDefinition,
|
|
|
|
|
viewQueries?: {[name: string]: [string, QueryBindingType]}, ): NodeDef {
|
|
|
|
|
const matchedQueryDefs: {[queryId: string]: QueryValueType} = {};
|
|
|
|
|
if (matchedQueries) {
|
|
|
|
|
matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; });
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-20 13:10:57 -08:00
|
|
|
const bindings: BindingDef[] = [];
|
|
|
|
|
if (props) {
|
|
|
|
|
for (let prop in props) {
|
|
|
|
|
const [bindingIndex, nonMinifiedName] = props[prop];
|
|
|
|
|
bindings[bindingIndex] = {
|
|
|
|
|
type: BindingType.ProviderProperty,
|
|
|
|
|
name: prop, nonMinifiedName,
|
|
|
|
|
securityContext: undefined,
|
|
|
|
|
suffix: undefined
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-19 10:25:03 -08:00
|
|
|
const outputDefs: ProviderOutputDef[] = [];
|
|
|
|
|
if (outputs) {
|
|
|
|
|
for (let propName in outputs) {
|
|
|
|
|
outputDefs.push({propName, eventName: outputs[propName]});
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-20 13:10:57 -08:00
|
|
|
const depDefs: DepDef[] = deps.map(value => {
|
|
|
|
|
let token: any;
|
|
|
|
|
let flags: DepFlags;
|
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
|
[flags, token] = value;
|
|
|
|
|
} else {
|
|
|
|
|
flags = DepFlags.None;
|
|
|
|
|
token = value;
|
|
|
|
|
}
|
|
|
|
|
return {flags, token, tokenKey: tokenKey(token)};
|
|
|
|
|
});
|
2017-01-23 16:59:20 -08:00
|
|
|
const contentQueryDefs: QueryDef[] = [];
|
|
|
|
|
for (let propName in contentQueries) {
|
|
|
|
|
const [id, bindingType] = contentQueries[propName];
|
|
|
|
|
contentQueryDefs.push({id, propName, bindingType});
|
|
|
|
|
}
|
|
|
|
|
const viewQueryDefs: QueryDef[] = [];
|
|
|
|
|
for (let propName in viewQueries) {
|
|
|
|
|
const [id, bindingType] = viewQueries[propName];
|
|
|
|
|
viewQueryDefs.push({id, propName, bindingType});
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-20 13:10:57 -08:00
|
|
|
if (component) {
|
|
|
|
|
flags = flags | NodeFlags.HasComponent;
|
|
|
|
|
}
|
2017-01-23 16:59:20 -08:00
|
|
|
if (contentQueryDefs.length) {
|
|
|
|
|
flags = flags | NodeFlags.HasContentQuery;
|
|
|
|
|
}
|
|
|
|
|
if (viewQueryDefs.length) {
|
|
|
|
|
flags = flags | NodeFlags.HasViewQuery;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-20 13:10:57 -08:00
|
|
|
return {
|
|
|
|
|
type: NodeType.Provider,
|
|
|
|
|
// will bet set by the view definition
|
|
|
|
|
index: undefined,
|
|
|
|
|
reverseChildIndex: undefined,
|
|
|
|
|
parent: undefined,
|
|
|
|
|
childFlags: undefined,
|
2017-01-23 16:59:20 -08:00
|
|
|
childMatchedQueries: undefined,
|
2017-01-20 13:10:57 -08:00
|
|
|
bindingIndex: undefined,
|
2017-01-19 10:25:03 -08:00
|
|
|
disposableIndex: undefined,
|
2017-01-20 13:10:57 -08:00
|
|
|
providerIndices: undefined,
|
|
|
|
|
// regular values
|
|
|
|
|
flags,
|
2017-01-23 16:59:20 -08:00
|
|
|
matchedQueries: matchedQueryDefs,
|
2017-01-20 13:10:57 -08:00
|
|
|
childCount: 0, bindings,
|
2017-01-19 10:25:03 -08:00
|
|
|
disposableCount: outputDefs.length,
|
2017-01-20 13:10:57 -08:00
|
|
|
element: undefined,
|
2017-01-23 16:59:20 -08:00
|
|
|
provider: {
|
|
|
|
|
tokenKey: tokenKey(ctor),
|
|
|
|
|
ctor,
|
|
|
|
|
deps: depDefs,
|
|
|
|
|
outputs: outputDefs,
|
|
|
|
|
contentQueries: contentQueryDefs,
|
|
|
|
|
viewQueries: viewQueryDefs, component
|
|
|
|
|
},
|
2017-01-20 09:21:09 -08:00
|
|
|
text: undefined,
|
2017-01-23 16:59:20 -08:00
|
|
|
pureExpression: undefined,
|
2017-01-20 13:10:57 -08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function tokenKey(token: any): string {
|
|
|
|
|
let key = _tokenKeyCache.get(token);
|
|
|
|
|
if (!key) {
|
|
|
|
|
key = stringify(token) + '_' + _tokenKeyCache.size;
|
|
|
|
|
_tokenKeyCache.set(token, key);
|
|
|
|
|
}
|
|
|
|
|
return key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function createProvider(view: ViewData, def: NodeDef, componentView: ViewData): NodeData {
|
|
|
|
|
const providerDef = def.provider;
|
2017-01-19 10:25:03 -08:00
|
|
|
const provider = createInstance(view, def.parent, providerDef.ctor, providerDef.deps);
|
|
|
|
|
if (providerDef.outputs.length) {
|
|
|
|
|
for (let i = 0; i < providerDef.outputs.length; i++) {
|
|
|
|
|
const output = providerDef.outputs[i];
|
|
|
|
|
const subscription = provider[output.propName].subscribe(
|
|
|
|
|
view.def.handleEvent.bind(null, view, def.parent, output.eventName));
|
|
|
|
|
view.disposables[def.disposableIndex + i] = subscription.unsubscribe.bind(subscription);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-23 16:59:20 -08:00
|
|
|
let queries: {[queryId: string]: QueryList<any>};
|
|
|
|
|
if (providerDef.contentQueries.length || providerDef.viewQueries.length) {
|
|
|
|
|
queries = {};
|
|
|
|
|
for (let i = 0; i < providerDef.contentQueries.length; i++) {
|
|
|
|
|
const def = providerDef.contentQueries[i];
|
|
|
|
|
queries[def.id] = new QueryList<any>();
|
|
|
|
|
}
|
|
|
|
|
for (let i = 0; i < providerDef.viewQueries.length; i++) {
|
|
|
|
|
const def = providerDef.viewQueries[i];
|
|
|
|
|
queries[def.id] = new QueryList<any>();
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-20 13:10:57 -08:00
|
|
|
return {
|
2017-01-23 10:23:44 -08:00
|
|
|
elementOrText: undefined,
|
2017-01-23 16:59:20 -08:00
|
|
|
provider: {instance: provider, componentView: componentView, queries},
|
2017-01-23 10:23:44 -08:00
|
|
|
pureExpression: undefined,
|
2017-01-20 13:10:57 -08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function checkAndUpdateProviderInline(
|
|
|
|
|
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
|
|
|
|
|
v7: any, v8: any, v9: any) {
|
2017-01-23 10:23:44 -08:00
|
|
|
const provider = view.nodes[def.index].provider.instance;
|
2017-01-20 13:10:57 -08:00
|
|
|
let changes: SimpleChanges;
|
|
|
|
|
// Note: fallthrough is intended!
|
|
|
|
|
switch (def.bindings.length) {
|
|
|
|
|
case 10:
|
|
|
|
|
changes = checkAndUpdateProp(view, provider, def, 9, v9, changes);
|
|
|
|
|
case 9:
|
|
|
|
|
changes = checkAndUpdateProp(view, provider, def, 8, v8, changes);
|
|
|
|
|
case 8:
|
|
|
|
|
changes = checkAndUpdateProp(view, provider, def, 7, v7, changes);
|
|
|
|
|
case 7:
|
|
|
|
|
changes = checkAndUpdateProp(view, provider, def, 6, v6, changes);
|
|
|
|
|
case 6:
|
|
|
|
|
changes = checkAndUpdateProp(view, provider, def, 5, v5, changes);
|
|
|
|
|
case 5:
|
|
|
|
|
changes = checkAndUpdateProp(view, provider, def, 4, v4, changes);
|
|
|
|
|
case 4:
|
|
|
|
|
changes = checkAndUpdateProp(view, provider, def, 3, v3, changes);
|
|
|
|
|
case 3:
|
|
|
|
|
changes = checkAndUpdateProp(view, provider, def, 2, v2, changes);
|
|
|
|
|
case 2:
|
|
|
|
|
changes = checkAndUpdateProp(view, provider, def, 1, v1, changes);
|
|
|
|
|
case 1:
|
|
|
|
|
changes = checkAndUpdateProp(view, provider, def, 0, v0, changes);
|
|
|
|
|
}
|
|
|
|
|
if (changes) {
|
|
|
|
|
provider.ngOnChanges(changes);
|
|
|
|
|
}
|
|
|
|
|
if (view.firstChange && (def.flags & NodeFlags.OnInit)) {
|
|
|
|
|
provider.ngOnInit();
|
|
|
|
|
}
|
|
|
|
|
if (def.flags & NodeFlags.DoCheck) {
|
|
|
|
|
provider.ngDoCheck();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-20 09:21:09 -08:00
|
|
|
export function checkAndUpdateProviderDynamic(view: ViewData, def: NodeDef, values: any[]) {
|
2017-01-23 10:23:44 -08:00
|
|
|
const provider = view.nodes[def.index].provider.instance;
|
2017-01-20 13:10:57 -08:00
|
|
|
let changes: SimpleChanges;
|
|
|
|
|
for (let i = 0; i < values.length; i++) {
|
|
|
|
|
changes = checkAndUpdateProp(view, provider, def, i, values[i], changes);
|
|
|
|
|
}
|
|
|
|
|
if (changes) {
|
|
|
|
|
provider.ngOnChanges(changes);
|
|
|
|
|
}
|
|
|
|
|
if (view.firstChange && (def.flags & NodeFlags.OnInit)) {
|
|
|
|
|
provider.ngOnInit();
|
|
|
|
|
}
|
|
|
|
|
if (def.flags & NodeFlags.DoCheck) {
|
|
|
|
|
provider.ngDoCheck();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createInstance(view: ViewData, elIndex: number, ctor: any, deps: DepDef[]): any {
|
|
|
|
|
const len = deps.length;
|
|
|
|
|
let injectable: any;
|
|
|
|
|
switch (len) {
|
|
|
|
|
case 0:
|
|
|
|
|
injectable = new ctor();
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
injectable = new ctor(resolveDep(view, elIndex, deps[0]));
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
injectable = new ctor(resolveDep(view, elIndex, deps[0]), resolveDep(view, elIndex, deps[1]));
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
injectable = new ctor(
|
|
|
|
|
resolveDep(view, elIndex, deps[0]), resolveDep(view, elIndex, deps[1]),
|
|
|
|
|
resolveDep(view, elIndex, deps[2]));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
const depValues = new Array(len);
|
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
|
depValues[i] = resolveDep(view, elIndex, deps[i]);
|
|
|
|
|
}
|
|
|
|
|
injectable = new ctor(...depValues);
|
|
|
|
|
}
|
|
|
|
|
return injectable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function resolveDep(
|
|
|
|
|
view: ViewData, elIndex: number, depDef: DepDef,
|
|
|
|
|
notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
|
|
|
|
const tokenKey = depDef.tokenKey;
|
|
|
|
|
|
|
|
|
|
if (depDef.flags & DepFlags.SkipSelf) {
|
|
|
|
|
const elDef = view.def.nodes[elIndex];
|
|
|
|
|
if (elDef.parent != null) {
|
|
|
|
|
elIndex = elDef.parent;
|
|
|
|
|
} else {
|
2017-01-23 16:59:20 -08:00
|
|
|
elIndex = view.parentDiIndex;
|
2017-01-20 13:10:57 -08:00
|
|
|
view = view.parent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (view) {
|
|
|
|
|
const elDef = view.def.nodes[elIndex];
|
|
|
|
|
switch (tokenKey) {
|
|
|
|
|
case RendererTokenKey:
|
|
|
|
|
if (view.renderer) {
|
|
|
|
|
return view.renderer;
|
|
|
|
|
} else {
|
|
|
|
|
return Injector.NULL.get(depDef.token, notFoundValue);
|
|
|
|
|
}
|
|
|
|
|
case ElementRefTokenKey:
|
2017-01-23 10:23:44 -08:00
|
|
|
return new ElementRef(view.nodes[elIndex].elementOrText.node);
|
2017-01-20 13:10:57 -08:00
|
|
|
case ViewContainerRefTokenKey:
|
|
|
|
|
return view.services.createViewContainerRef(view.nodes[elIndex]);
|
|
|
|
|
case TemplateRefTokenKey:
|
|
|
|
|
return view.services.createTemplateRef(view, elDef);
|
|
|
|
|
default:
|
|
|
|
|
const providerIndex = elDef.providerIndices[tokenKey];
|
|
|
|
|
if (providerIndex != null) {
|
2017-01-23 10:23:44 -08:00
|
|
|
return view.nodes[providerIndex].provider.instance;
|
2017-01-20 13:10:57 -08:00
|
|
|
}
|
|
|
|
|
}
|
2017-01-23 16:59:20 -08:00
|
|
|
elIndex = view.parentDiIndex;
|
2017-01-20 13:10:57 -08:00
|
|
|
view = view.parent;
|
|
|
|
|
}
|
|
|
|
|
return Injector.NULL.get(depDef.token, notFoundValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function checkAndUpdateProp(
|
|
|
|
|
view: ViewData, provider: any, def: NodeDef, bindingIdx: number, value: any,
|
|
|
|
|
changes: SimpleChanges): SimpleChanges {
|
|
|
|
|
let change: SimpleChange;
|
|
|
|
|
let changed: boolean;
|
|
|
|
|
if (def.flags & NodeFlags.OnChanges) {
|
|
|
|
|
change = checkAndUpdateBindingWithChange(view, def, bindingIdx, value);
|
|
|
|
|
changed = !!change;
|
|
|
|
|
} else {
|
|
|
|
|
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
|
|
|
|
|
}
|
|
|
|
|
if (changed) {
|
|
|
|
|
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.
|
|
|
|
|
provider[propName] = value;
|
|
|
|
|
|
|
|
|
|
if (view.def.flags & ViewFlags.LogBindingUpdate) {
|
2017-01-23 11:23:15 -08:00
|
|
|
setBindingDebugInfo(
|
2017-01-23 10:23:44 -08:00
|
|
|
view.renderer, view.nodes[def.parent].elementOrText.node, binding.nonMinifiedName, value);
|
2017-01-20 13:10:57 -08:00
|
|
|
}
|
|
|
|
|
if (change) {
|
|
|
|
|
changes = changes || {};
|
|
|
|
|
changes[binding.nonMinifiedName] = change;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return changes;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-23 16:59:20 -08:00
|
|
|
export enum QueryAction {
|
|
|
|
|
CheckNoChanges,
|
|
|
|
|
CheckAndUpdate,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function execContentQueriesAction(view: ViewData, action: QueryAction) {
|
|
|
|
|
if (!(view.def.nodeFlags & NodeFlags.HasContentQuery)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (let i = 0; i < view.nodes.length; i++) {
|
|
|
|
|
const nodeDef = view.def.nodes[i];
|
|
|
|
|
if (nodeDef.flags & NodeFlags.HasContentQuery) {
|
|
|
|
|
execContentQuery(view, nodeDef, action);
|
|
|
|
|
} else if ((nodeDef.childFlags & NodeFlags.HasContentQuery) === 0) {
|
|
|
|
|
// no child has a content query
|
|
|
|
|
// then skip the children
|
|
|
|
|
i += nodeDef.childCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function updateViewQueries(view: ViewData, action: QueryAction) {
|
|
|
|
|
if (!(view.def.nodeFlags & NodeFlags.HasViewQuery)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (let i = 0; i < view.nodes.length; i++) {
|
|
|
|
|
const nodeDef = view.def.nodes[i];
|
|
|
|
|
if (nodeDef.flags & NodeFlags.HasViewQuery) {
|
|
|
|
|
updateViewQuery(view, nodeDef, action);
|
|
|
|
|
} else if ((nodeDef.childFlags & NodeFlags.HasViewQuery) === 0) {
|
|
|
|
|
// no child has a view query
|
|
|
|
|
// then skip the children
|
|
|
|
|
i += nodeDef.childCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function execContentQuery(view: ViewData, nodeDef: NodeDef, action: QueryAction) {
|
|
|
|
|
const providerData = view.nodes[nodeDef.index].provider;
|
|
|
|
|
for (let i = 0; i < nodeDef.provider.contentQueries.length; i++) {
|
|
|
|
|
const queryDef = nodeDef.provider.contentQueries[i];
|
|
|
|
|
const queryId = queryDef.id;
|
|
|
|
|
const queryList = providerData.queries[queryId];
|
|
|
|
|
if (queryList.dirty) {
|
|
|
|
|
const elementDef = view.def.nodes[nodeDef.parent];
|
|
|
|
|
const newValues = calcQueryValues(
|
|
|
|
|
view, elementDef.index, elementDef.index + elementDef.childCount, queryId, []);
|
|
|
|
|
execQueryAction(view, providerData.instance, queryList, queryDef, newValues, action);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateViewQuery(view: ViewData, nodeDef: NodeDef, action: QueryAction) {
|
|
|
|
|
for (let i = 0; i < nodeDef.provider.viewQueries.length; i++) {
|
|
|
|
|
const queryDef = nodeDef.provider.viewQueries[i];
|
|
|
|
|
const queryId = queryDef.id;
|
|
|
|
|
const providerData = view.nodes[nodeDef.index].provider;
|
|
|
|
|
const queryList = providerData.queries[queryId];
|
|
|
|
|
if (queryList.dirty) {
|
|
|
|
|
const componentView = providerData.componentView;
|
|
|
|
|
const newValues =
|
|
|
|
|
calcQueryValues(componentView, 0, componentView.nodes.length - 1, queryId, []);
|
|
|
|
|
execQueryAction(view, providerData.instance, queryList, queryDef, newValues, action);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function execQueryAction(
|
|
|
|
|
view: ViewData, provider: any, queryList: QueryList<any>, queryDef: QueryDef, newValues: any[],
|
|
|
|
|
action: QueryAction) {
|
|
|
|
|
switch (action) {
|
|
|
|
|
case QueryAction.CheckAndUpdate:
|
|
|
|
|
queryList.reset(newValues);
|
|
|
|
|
let boundValue: any;
|
|
|
|
|
switch (queryDef.bindingType) {
|
|
|
|
|
case QueryBindingType.First:
|
|
|
|
|
boundValue = queryList.first;
|
|
|
|
|
break;
|
|
|
|
|
case QueryBindingType.All:
|
|
|
|
|
boundValue = queryList;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
provider[queryDef.propName] = boundValue;
|
|
|
|
|
break;
|
|
|
|
|
case QueryAction.CheckNoChanges:
|
|
|
|
|
// queries should always be non dirty when we go into checkNoChanges!
|
|
|
|
|
const oldValuesStr = queryList.toArray().map(v => stringify(v));
|
|
|
|
|
const newValuesStr = newValues.map(v => stringify(v));
|
|
|
|
|
throw new ExpressionChangedAfterItHasBeenCheckedError(
|
|
|
|
|
oldValuesStr, newValuesStr, view.firstChange);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function calcQueryValues(
|
|
|
|
|
view: ViewData, startIndex: number, endIndex: number, queryId: string, values: any[]): any[] {
|
|
|
|
|
const len = view.def.nodes.length;
|
|
|
|
|
for (let i = startIndex; i <= endIndex; i++) {
|
|
|
|
|
const nodeDef = view.def.nodes[i];
|
|
|
|
|
const queryValueType = <QueryValueType>nodeDef.matchedQueries[queryId];
|
|
|
|
|
if (queryValueType != null) {
|
|
|
|
|
// a match
|
|
|
|
|
let value: any;
|
|
|
|
|
switch (queryValueType) {
|
|
|
|
|
case QueryValueType.ElementRef:
|
|
|
|
|
value = new ElementRef(view.nodes[i].elementOrText.node);
|
|
|
|
|
break;
|
|
|
|
|
case QueryValueType.TemplateRef:
|
|
|
|
|
value = view.services.createTemplateRef(view, nodeDef);
|
|
|
|
|
break;
|
|
|
|
|
case QueryValueType.ViewContainerRef:
|
|
|
|
|
value = view.services.createViewContainerRef(view.nodes[i]);
|
|
|
|
|
break;
|
|
|
|
|
case QueryValueType.Provider:
|
|
|
|
|
value = view.nodes[i].provider.instance;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
values.push(value);
|
|
|
|
|
}
|
|
|
|
|
if (nodeDef.flags & NodeFlags.HasEmbeddedViews &&
|
|
|
|
|
queryId in nodeDef.element.template.nodeMatchedQueries) {
|
|
|
|
|
// check embedded views that were attached at the place of their template.
|
|
|
|
|
const nodeData = view.nodes[i];
|
|
|
|
|
const embeddedViews = nodeData.elementOrText.embeddedViews;
|
|
|
|
|
for (let k = 0; k < embeddedViews.length; k++) {
|
|
|
|
|
const embeddedView = embeddedViews[k];
|
|
|
|
|
const dvc = declaredViewContainer(embeddedView);
|
|
|
|
|
if (dvc && dvc === nodeData) {
|
|
|
|
|
calcQueryValues(embeddedView, 0, embeddedView.nodes.length - 1, queryId, values);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const projectedViews = nodeData.elementOrText.projectedViews;
|
|
|
|
|
if (projectedViews) {
|
|
|
|
|
for (let k = 0; k < projectedViews.length; k++) {
|
|
|
|
|
const projectedView = projectedViews[k];
|
|
|
|
|
calcQueryValues(projectedView, 0, projectedView.nodes.length - 1, queryId, values);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!(queryId in nodeDef.childMatchedQueries)) {
|
|
|
|
|
// If don't check descendants, skip the children.
|
|
|
|
|
// Or: no child matches the query, then skip the children as well.
|
|
|
|
|
i += nodeDef.childCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return values;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-20 13:10:57 -08:00
|
|
|
export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: NodeFlags) {
|
|
|
|
|
if (!(view.def.nodeFlags & lifecycles)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const len = view.def.nodes.length;
|
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
|
// We use the provider post order to call providers of children first.
|
|
|
|
|
const nodeDef = view.def.reverseChildNodes[i];
|
|
|
|
|
const nodeIndex = nodeDef.index;
|
|
|
|
|
if (nodeDef.flags & lifecycles) {
|
|
|
|
|
// a leaf
|
2017-01-23 10:23:44 -08:00
|
|
|
callProviderLifecycles(view.nodes[nodeIndex].provider.instance, nodeDef.flags & lifecycles);
|
2017-01-20 13:10:57 -08:00
|
|
|
} else if ((nodeDef.childFlags & lifecycles) === 0) {
|
|
|
|
|
// a parent with leafs
|
|
|
|
|
// no child matches one of the lifecycles,
|
|
|
|
|
// then skip the children
|
|
|
|
|
i += nodeDef.childCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function callProviderLifecycles(provider: any, lifecycles: NodeFlags) {
|
|
|
|
|
if (lifecycles & NodeFlags.AfterContentInit) {
|
|
|
|
|
provider.ngAfterContentInit();
|
|
|
|
|
}
|
|
|
|
|
if (lifecycles & NodeFlags.AfterContentChecked) {
|
|
|
|
|
provider.ngAfterContentChecked();
|
|
|
|
|
}
|
|
|
|
|
if (lifecycles & NodeFlags.AfterViewInit) {
|
|
|
|
|
provider.ngAfterViewInit();
|
|
|
|
|
}
|
|
|
|
|
if (lifecycles & NodeFlags.AfterViewChecked) {
|
|
|
|
|
provider.ngAfterViewChecked();
|
|
|
|
|
}
|
|
|
|
|
if (lifecycles & NodeFlags.OnDestroy) {
|
|
|
|
|
provider.ngOnDestroy();
|
|
|
|
|
}
|
|
|
|
|
}
|