refactor: move angular source to /packages rather than modules/@angular
This commit is contained in:
487
packages/core/src/view/provider.ts
Normal file
487
packages/core/src/view/provider.ts
Normal file
@ -0,0 +1,487 @@
|
||||
/**
|
||||
* @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} from '../change_detection/change_detection';
|
||||
import {Injector} from '../di';
|
||||
import {ElementRef} from '../linker/element_ref';
|
||||
import {TemplateRef} from '../linker/template_ref';
|
||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||
import {ViewEncapsulation} from '../metadata/view';
|
||||
import {Renderer as RendererV1, Renderer2, RendererFactory2, RendererType2} from '../render/api';
|
||||
|
||||
import {createChangeDetectorRef, createInjector, createRendererV1, createTemplateRef, createViewContainerRef} from './refs';
|
||||
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, OutputDef, OutputType, ProviderData, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
|
||||
import {checkBinding, dispatchEvent, filterQueryId, isComponentView, 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);
|
||||
|
||||
const NOT_CREATED = new Object();
|
||||
|
||||
export function directiveDef(
|
||||
flags: NodeFlags, matchedQueries: [string | number, QueryValueType][], childCount: number,
|
||||
ctor: any, deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
|
||||
outputs?: {[name: string]: string}): NodeDef {
|
||||
const bindings: BindingDef[] = [];
|
||||
if (props) {
|
||||
for (let prop in props) {
|
||||
const [bindingIndex, nonMinifiedName] = props[prop];
|
||||
bindings[bindingIndex] = {
|
||||
type: BindingType.DirectiveProperty,
|
||||
name: prop, nonMinifiedName,
|
||||
ns: undefined,
|
||||
securityContext: undefined,
|
||||
suffix: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
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(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(flags, null, 0, ctor, ctor, deps);
|
||||
}
|
||||
|
||||
export function providerDef(
|
||||
flags: NodeFlags, matchedQueries: [string | number, QueryValueType][], token: any, value: any,
|
||||
deps: ([DepFlags, any] | any)[]): NodeDef {
|
||||
return _def(flags, matchedQueries, 0, token, value, deps);
|
||||
}
|
||||
|
||||
export function _def(
|
||||
flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][], 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 = [];
|
||||
}
|
||||
|
||||
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)};
|
||||
});
|
||||
|
||||
return {
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
// regular values
|
||||
flags,
|
||||
childFlags: 0,
|
||||
directChildFlags: 0,
|
||||
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references,
|
||||
ngContentIndex: undefined, childCount, bindings, outputs,
|
||||
element: undefined,
|
||||
provider: {token, tokenKey: tokenKey(token), value, deps: depDefs},
|
||||
text: undefined,
|
||||
query: undefined,
|
||||
ngContent: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export function createProviderInstance(view: ViewData, def: NodeDef): any {
|
||||
return def.flags & NodeFlags.LazyProvider ? NOT_CREATED : _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;
|
||||
const providerDef = def.provider;
|
||||
// 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.index, 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.index);
|
||||
const directive = providerData.instance;
|
||||
let changed = false;
|
||||
let changes: SimpleChanges;
|
||||
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 ((view.state & ViewState.FirstCheck) && (def.flags & NodeFlags.OnInit)) {
|
||||
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.index);
|
||||
const directive = providerData.instance;
|
||||
let changed = false;
|
||||
let changes: SimpleChanges;
|
||||
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 ((view.state & ViewState.FirstCheck) && (def.flags & NodeFlags.OnInit)) {
|
||||
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;
|
||||
let injectable: any;
|
||||
switch (def.flags & NodeFlags.Types) {
|
||||
case NodeFlags.TypeClassProvider:
|
||||
injectable =
|
||||
createClass(view, def.parent, allowPrivateServices, providerDef.value, providerDef.deps);
|
||||
break;
|
||||
case NodeFlags.TypeFactoryProvider:
|
||||
injectable =
|
||||
callFactory(view, def.parent, allowPrivateServices, providerDef.value, providerDef.deps);
|
||||
break;
|
||||
case NodeFlags.TypeUseExistingProvider:
|
||||
injectable = resolveDep(view, def.parent, allowPrivateServices, providerDef.deps[0]);
|
||||
break;
|
||||
case NodeFlags.TypeValueProvider:
|
||||
injectable = providerDef.value;
|
||||
break;
|
||||
}
|
||||
return injectable;
|
||||
}
|
||||
|
||||
function createClass(
|
||||
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, 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, elDef, allowPrivateServices, deps[0]));
|
||||
break;
|
||||
case 2:
|
||||
injectable = new ctor(
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[0]),
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[1]));
|
||||
break;
|
||||
case 3:
|
||||
injectable = new ctor(
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[0]),
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[1]),
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[2]));
|
||||
break;
|
||||
default:
|
||||
const depValues = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
depValues[i] = resolveDep(view, elDef, allowPrivateServices, deps[i]);
|
||||
}
|
||||
injectable = new ctor(...depValues);
|
||||
}
|
||||
return injectable;
|
||||
}
|
||||
|
||||
function callFactory(
|
||||
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, factory: any,
|
||||
deps: DepDef[]): any {
|
||||
const len = deps.length;
|
||||
let injectable: any;
|
||||
switch (len) {
|
||||
case 0:
|
||||
injectable = factory();
|
||||
break;
|
||||
case 1:
|
||||
injectable = factory(resolveDep(view, elDef, allowPrivateServices, deps[0]));
|
||||
break;
|
||||
case 2:
|
||||
injectable = factory(
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[0]),
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[1]));
|
||||
break;
|
||||
case 3:
|
||||
injectable = factory(
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[0]),
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[1]),
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[2]));
|
||||
break;
|
||||
default:
|
||||
const depValues = Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
depValues[i] = resolveDep(view, elDef, allowPrivateServices, deps[i]);
|
||||
}
|
||||
injectable = factory(...depValues);
|
||||
}
|
||||
return injectable;
|
||||
}
|
||||
|
||||
export function resolveDep(
|
||||
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef,
|
||||
notFoundValue = 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 (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.index).renderElement);
|
||||
case ViewContainerRefTokenKey:
|
||||
return createViewContainerRef(view, elDef);
|
||||
case TemplateRefTokenKey: {
|
||||
if (elDef.element.template) {
|
||||
return createTemplateRef(view, elDef);
|
||||
}
|
||||
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) {
|
||||
const providerData = asProviderData(view, providerDef.index);
|
||||
if (providerData.instance === NOT_CREATED) {
|
||||
providerData.instance = _createProviderInstance(view, providerDef);
|
||||
}
|
||||
return providerData.instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
allowPrivateServices = isComponentView(view);
|
||||
elDef = viewParentEl(view);
|
||||
view = view.parent;
|
||||
}
|
||||
return startView.root.injector.get(depDef.token, notFoundValue);
|
||||
}
|
||||
|
||||
function findCompView(view: ViewData, elDef: NodeDef, allowPrivateServices: boolean) {
|
||||
let compView: ViewData;
|
||||
if (allowPrivateServices) {
|
||||
compView = asElementData(view, elDef.index).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.index).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 = 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;
|
||||
}
|
||||
|
||||
export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: NodeFlags) {
|
||||
if (!(view.def.nodeFlags & lifecycles)) {
|
||||
return;
|
||||
}
|
||||
const nodes = view.def.nodes;
|
||||
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);
|
||||
}
|
||||
if ((nodeDef.childFlags & lifecycles) === 0) {
|
||||
// no child matches one of the lifecycles
|
||||
i += nodeDef.childCount;
|
||||
}
|
||||
while (parent && (parent.flags & NodeFlags.TypeElement) &&
|
||||
i === parent.index + parent.childCount) {
|
||||
// last child of an element
|
||||
if (parent.directChildFlags & lifecycles) {
|
||||
callElementProvidersLifecycles(view, parent, lifecycles);
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function callElementProvidersLifecycles(view: ViewData, elDef: NodeDef, lifecycles: NodeFlags) {
|
||||
for (let i = elDef.index + 1; i <= elDef.index + elDef.childCount; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
if (nodeDef.flags & lifecycles) {
|
||||
callProviderLifecycles(view, i, nodeDef.flags & lifecycles);
|
||||
}
|
||||
// only visit direct children
|
||||
i += nodeDef.childCount;
|
||||
}
|
||||
}
|
||||
|
||||
function callProviderLifecycles(view: ViewData, index: number, lifecycles: NodeFlags) {
|
||||
const provider = asProviderData(view, index).instance;
|
||||
Services.setCurrentNode(view, index);
|
||||
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();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user