@angular/core
Version:
Angular - the core framework
1,241 lines • 245 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
import { ErrorHandler } from '../../error_handler';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '../../metadata/schema';
import { validateAgainstEventAttributes, validateAgainstEventProperties } from '../../sanitization/sanitization';
import { assertDataInRange, assertDefined, assertDomNode, assertEqual, assertNotEqual, assertNotSame } from '../../util/assert';
import { createNamedArrayType } from '../../util/named_array_type';
import { normalizeDebugBindingName, normalizeDebugBindingValue } from '../../util/ng_reflect';
import { assertLView, assertPreviousIsParent } from '../assert';
import { attachPatchData, getComponentViewByInstance } from '../context_discovery';
import { diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode } from '../di';
import { throwMultipleComponentError } from '../errors';
import { executeHooks, executePreOrderHooks, registerPreOrderHooks } from '../hooks';
import { ACTIVE_INDEX, CONTAINER_HEADER_OFFSET } from '../interfaces/container';
import { INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory } from '../interfaces/injector';
import { isProceduralRenderer } from '../interfaces/renderer';
import { BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, INJECTOR, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TVIEW, T_HOST } from '../interfaces/view';
import { assertNodeOfPossibleTypes, assertNodeType } from '../node_assert';
import { isNodeMatchingSelectorList } from '../node_selector_matcher';
import { enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, getSelectedIndex, incrementActiveDirectiveId, isCreationMode, leaveView, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex, ɵɵnamespaceHTML } from '../state';
import { initializeStaticContext as initializeStaticStylingContext } from '../styling/class_and_style_bindings';
import { ANIMATION_PROP_PREFIX, isAnimationProp } from '../styling/util';
import { NO_CHANGE } from '../tokens';
import { attrsStylingIndexOf } from '../util/attrs_utils';
import { INTERPOLATION_DELIMITER, renderStringify, stringifyForError } from '../util/misc_utils';
import { getLViewParent, getRootContext } from '../util/view_traversal_utils';
import { getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isComponent, isComponentDef, isContentQueryHost, isLContainer, isRootView, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector } from '../util/view_utils';
import { LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeInitialData, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor, attachLContainerDebug, attachLViewDebug, cloneToLView, cloneToTViewData } from './lview_debug';
import { selectInternal } from './select';
const ɵ0 = /**
* @return {?}
*/
() => Promise.resolve(null);
/**
* A permanent marker promise which signifies that the current CD tree is
* clean.
* @type {?}
*/
const _CLEAN_PROMISE = ((ɵ0))();
/** @enum {number} */
const BindingDirection = {
Input: 0,
Output: 1,
};
export { BindingDirection };
/**
* Refreshes the view, executing the following steps in that order:
* triggers init hooks, refreshes dynamic embedded views, triggers content hooks, sets host
* bindings, refreshes child components.
* Note: view hooks are triggered later when leaving the view.
* @param {?} lView
* @return {?}
*/
export function refreshDescendantViews(lView) {
/** @type {?} */
const tView = lView[TVIEW];
/** @type {?} */
const creationMode = isCreationMode(lView);
// This needs to be set before children are processed to support recursive components
tView.firstTemplatePass = false;
// Resetting the bindingIndex of the current LView as the next steps may trigger change detection.
lView[BINDING_INDEX] = tView.bindingStartIndex;
// If this is a creation pass, we should not call lifecycle hooks or evaluate bindings.
// This will be done in the update pass.
if (!creationMode) {
/** @type {?} */
const checkNoChangesMode = getCheckNoChangesMode();
executePreOrderHooks(lView, tView, checkNoChangesMode, undefined);
refreshDynamicEmbeddedViews(lView);
// Content query results must be refreshed before content hooks are called.
refreshContentQueries(tView, lView);
resetPreOrderHookFlags(lView);
executeHooks(lView, tView.contentHooks, tView.contentCheckHooks, checkNoChangesMode, 1 /* AfterContentInitHooksToBeRun */, undefined);
setHostBindings(tView, lView);
}
// We resolve content queries specifically marked as `static` in creation mode. Dynamic
// content queries are resolved during change detection (i.e. update mode), after embedded
// views are refreshed (see block above).
if (creationMode && tView.staticContentQueries) {
refreshContentQueries(tView, lView);
}
refreshChildComponents(tView.components);
}
/**
* Sets the host bindings for the current view.
* @param {?} tView
* @param {?} viewData
* @return {?}
*/
export function setHostBindings(tView, viewData) {
/** @type {?} */
const selectedIndex = getSelectedIndex();
try {
if (tView.expandoInstructions) {
/** @type {?} */
let bindingRootIndex = viewData[BINDING_INDEX] = tView.expandoStartIndex;
setBindingRoot(bindingRootIndex);
/** @type {?} */
let currentDirectiveIndex = -1;
/** @type {?} */
let currentElementIndex = -1;
for (let i = 0; i < tView.expandoInstructions.length; i++) {
/** @type {?} */
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;
setActiveHostElement(currentElementIndex);
// Injector block and providers are taken into account.
/** @type {?} */
const providerCount = ((/** @type {?} */ (tView.expandoInstructions[++i])));
bindingRootIndex += INJECTOR_BLOOM_PARENT_SIZE + providerCount;
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;
}
setBindingRoot(bindingRootIndex);
}
else {
// If it's not a number, it's a host binding function that needs to be executed.
if (instruction !== null) {
viewData[BINDING_INDEX] = bindingRootIndex;
/** @type {?} */
const hostCtx = unwrapRNode(viewData[currentDirectiveIndex]);
instruction(2 /* Update */, hostCtx, currentElementIndex);
// Each directive gets a uniqueId value that is the same for both
// create and update calls when the hostBindings function is called. The
// directive uniqueId is not set anywhere--it is just incremented between
// each hostBindings call and is useful for helping instruction code
// uniquely determine which directive is currently active when executed.
incrementActiveDirectiveId();
}
currentDirectiveIndex++;
}
}
}
}
finally {
setActiveHostElement(selectedIndex);
}
}
/**
* Refreshes content queries for all directives in the given view.
* @param {?} tView
* @param {?} lView
* @return {?}
*/
function refreshContentQueries(tView, lView) {
if (tView.contentQueries != null) {
setCurrentQueryIndex(0);
for (let i = 0; i < tView.contentQueries.length; i++) {
/** @type {?} */
const directiveDefIdx = tView.contentQueries[i];
/** @type {?} */
const directiveDef = (/** @type {?} */ (tView.data[directiveDefIdx]));
ngDevMode &&
assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined');
(/** @type {?} */ (directiveDef.contentQueries))(2 /* Update */, lView[directiveDefIdx], directiveDefIdx);
}
}
}
/**
* Refreshes child components in the current view.
* @param {?} components
* @return {?}
*/
function refreshChildComponents(components) {
if (components != null) {
for (let i = 0; i < components.length; i++) {
componentRefresh(components[i]);
}
}
}
/**
* Creates a native element from a tag name, using a renderer.
* @param {?} name the tag name
* @param {?=} overriddenRenderer Optional A renderer to override the default one
* @return {?} the element created
*/
export function elementCreate(name, overriddenRenderer) {
/** @type {?} */
let native;
/** @type {?} */
const rendererToUse = overriddenRenderer || getLView()[RENDERER];
/** @type {?} */
const namespace = getNamespace();
if (isProceduralRenderer(rendererToUse)) {
native = rendererToUse.createElement(name, namespace);
}
else {
if (namespace === null) {
native = rendererToUse.createElement(name);
}
else {
native = rendererToUse.createElementNS(namespace, name);
}
}
return native;
}
/**
* @template T
* @param {?} parentLView
* @param {?} tView
* @param {?} context
* @param {?} flags
* @param {?} host
* @param {?} tHostNode
* @param {?=} rendererFactory
* @param {?=} renderer
* @param {?=} sanitizer
* @param {?=} injector
* @return {?}
*/
export function createLView(parentLView, tView, context, flags, host, tHostNode, rendererFactory, renderer, sanitizer, injector) {
/** @type {?} */
const lView = ngDevMode ? cloneToLView(tView.blueprint) : (/** @type {?} */ (tView.blueprint.slice()));
lView[HOST] = host;
lView[FLAGS] = flags | 4 /* CreationMode */ | 128 /* Attached */ | 8 /* FirstLViewPass */;
resetPreOrderHookFlags(lView);
lView[PARENT] = lView[DECLARATION_VIEW] = parentLView;
lView[CONTEXT] = context;
lView[RENDERER_FACTORY] = (/** @type {?} */ ((rendererFactory || parentLView && parentLView[RENDERER_FACTORY])));
ngDevMode && assertDefined(lView[RENDERER_FACTORY], 'RendererFactory is required');
lView[RENDERER] = (/** @type {?} */ ((renderer || parentLView && parentLView[RENDERER])));
ngDevMode && assertDefined(lView[RENDERER], 'Renderer is required');
lView[SANITIZER] = sanitizer || parentLView && parentLView[SANITIZER] || (/** @type {?} */ (null));
lView[(/** @type {?} */ (INJECTOR))] = injector || parentLView && parentLView[INJECTOR] || null;
lView[T_HOST] = tHostNode;
ngDevMode && attachLViewDebug(lView);
return lView;
}
/**
* @param {?} tView
* @param {?} tHostNode
* @param {?} index
* @param {?} type
* @param {?} name
* @param {?} attrs
* @return {?}
*/
export function getOrCreateTNode(tView, tHostNode, index, type, name, attrs) {
// Keep this function short, so that the VM will inline it.
/** @type {?} */
const adjustedIndex = index + HEADER_OFFSET;
/** @type {?} */
const tNode = (/** @type {?} */ (tView.data[adjustedIndex])) ||
createTNodeAtIndex(tView, tHostNode, adjustedIndex, type, name, attrs, index);
setPreviousOrParentTNode(tNode, true);
return (/** @type {?} */ (tNode));
}
/**
* @param {?} tView
* @param {?} tHostNode
* @param {?} adjustedIndex
* @param {?} type
* @param {?} name
* @param {?} attrs
* @param {?} index
* @return {?}
*/
function createTNodeAtIndex(tView, tHostNode, adjustedIndex, type, name, attrs, index) {
/** @type {?} */
const previousOrParentTNode = getPreviousOrParentTNode();
/** @type {?} */
const isParent = getIsParent();
/** @type {?} */
const parent = isParent ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent;
// Parents cannot cross component boundaries because components will be used in multiple places,
// so it's only set if the view is the same.
/** @type {?} */
const parentInSameView = parent && parent !== tHostNode;
/** @type {?} */
const tParentNode = parentInSameView ? (/** @type {?} */ (parent)) : null;
/** @type {?} */
const tNode = tView.data[adjustedIndex] =
createTNode(tParentNode, type, adjustedIndex, name, attrs);
// The first node is not always the one at index 0, in case of i18n, index 0 can be the
// instruction `i18nStart` and the first node has the index 1 or more
if (index === 0 || !tView.firstChild) {
tView.firstChild = tNode;
}
// Now link ourselves into the tree.
if (previousOrParentTNode) {
if (isParent && previousOrParentTNode.child == null &&
(tNode.parent !== null || previousOrParentTNode.type === 2 /* View */)) {
// We are in the same view, which means we are adding content node to the parent view.
previousOrParentTNode.child = tNode;
}
else if (!isParent) {
previousOrParentTNode.next = tNode;
}
}
return tNode;
}
/**
* @param {?} tView
* @param {?} tParentNode
* @param {?} index
* @param {?} lView
* @return {?}
*/
export function assignTViewNodeToLView(tView, tParentNode, index, lView) {
// View nodes are not stored in data because they can be added / removed at runtime (which
// would cause indices to change). Their TNodes are instead stored in tView.node.
/** @type {?} */
let tNode = tView.node;
if (tNode == null) {
ngDevMode && tParentNode &&
assertNodeOfPossibleTypes(tParentNode, 3 /* Element */, 0 /* Container */);
tView.node = tNode = (/** @type {?} */ (createTNode((/** @type {?} */ (tParentNode)), //
2 /* View */, index, null, null)));
}
return lView[T_HOST] = (/** @type {?} */ (tNode));
}
/**
* When elements are created dynamically after a view blueprint is created (e.g. through
* i18nApply() or ComponentFactory.create), we need to adjust the blueprint for future
* template passes.
* @param {?} view
* @param {?} numSlotsToAlloc
* @return {?}
*/
export function allocExpando(view, numSlotsToAlloc) {
/** @type {?} */
const tView = view[TVIEW];
if (tView.firstTemplatePass) {
for (let i = 0; i < numSlotsToAlloc; i++) {
tView.blueprint.push(null);
tView.data.push(null);
view.push(null);
}
// We should only increment the expando start index if there aren't already directives
// and injectors saved in the "expando" section
if (!tView.expandoInstructions) {
tView.expandoStartIndex += numSlotsToAlloc;
}
else {
// Since we're adding the dynamic nodes into the expando section, we need to let the host
// bindings know that they should skip x slots
tView.expandoInstructions.push(numSlotsToAlloc);
}
}
}
//////////////////////////
//// Render
//////////////////////////
/**
* Used for creating the LViewNode of a dynamic embedded view,
* either through ViewContainerRef.createEmbeddedView() or TemplateRef.createEmbeddedView().
* Such lViewNode will then be renderer with renderEmbeddedTemplate() (see below).
* @template T
* @param {?} tView
* @param {?} context
* @param {?} declarationView
* @param {?} queries
* @param {?} injectorIndex
* @return {?}
*/
export function createEmbeddedViewAndNode(tView, context, declarationView, queries, injectorIndex) {
/** @type {?} */
const _isParent = getIsParent();
/** @type {?} */
const _previousOrParentTNode = getPreviousOrParentTNode();
setPreviousOrParentTNode((/** @type {?} */ (null)), true);
/** @type {?} */
const lView = createLView(declarationView, tView, context, 16 /* CheckAlways */, null, null);
lView[DECLARATION_VIEW] = declarationView;
if (queries) {
lView[QUERIES] = queries.createView();
}
assignTViewNodeToLView(tView, null, -1, lView);
if (tView.firstTemplatePass) {
(/** @type {?} */ (tView.node)).injectorIndex = injectorIndex;
}
setPreviousOrParentTNode(_previousOrParentTNode, _isParent);
return lView;
}
/**
* Used for rendering embedded views (e.g. dynamically created views)
*
* Dynamically created views must store/retrieve their TViews differently from component views
* because their template functions are nested in the template functions of their hosts, creating
* closures. If their host template happens to be an embedded template in a loop (e.g. ngFor
* inside
* an ngFor), the nesting would mean we'd have multiple instances of the template function, so we
* can't store TViews in the template function itself (as we do for comps). Instead, we store the
* TView for dynamically created views on their host TNode, which only has one instance.
* @template T
* @param {?} viewToRender
* @param {?} tView
* @param {?} context
* @return {?}
*/
export function renderEmbeddedTemplate(viewToRender, tView, context) {
/** @type {?} */
const _isParent = getIsParent();
/** @type {?} */
const _previousOrParentTNode = getPreviousOrParentTNode();
/** @type {?} */
let oldView;
if (viewToRender[FLAGS] & 512 /* IsRoot */) {
// This is a root view inside the view tree
tickRootContext(getRootContext(viewToRender));
}
else {
// Will become true if the `try` block executes with no errors.
/** @type {?} */
let safeToRunHooks = false;
try {
setPreviousOrParentTNode((/** @type {?} */ (null)), true);
oldView = enterView(viewToRender, viewToRender[T_HOST]);
resetPreOrderHookFlags(viewToRender);
executeTemplate(viewToRender, (/** @type {?} */ (tView.template)), getRenderFlags(viewToRender), context);
// 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 = false;
refreshDescendantViews(viewToRender);
safeToRunHooks = true;
}
finally {
leaveView((/** @type {?} */ (oldView)), safeToRunHooks);
setPreviousOrParentTNode(_previousOrParentTNode, _isParent);
}
}
}
/**
* @template T
* @param {?} hostView
* @param {?} context
* @param {?=} templateFn
* @return {?}
*/
export function renderComponentOrTemplate(hostView, context, templateFn) {
/** @type {?} */
const rendererFactory = hostView[RENDERER_FACTORY];
/** @type {?} */
const oldView = enterView(hostView, hostView[T_HOST]);
/** @type {?} */
const normalExecutionPath = !getCheckNoChangesMode();
/** @type {?} */
const creationModeIsActive = isCreationMode(hostView);
// Will become true if the `try` block executes with no errors.
/** @type {?} */
let safeToRunHooks = false;
try {
if (normalExecutionPath && !creationModeIsActive && rendererFactory.begin) {
rendererFactory.begin();
}
if (creationModeIsActive) {
// creation mode pass
templateFn && executeTemplate(hostView, templateFn, 1 /* Create */, context);
refreshDescendantViews(hostView);
hostView[FLAGS] &= ~4 /* CreationMode */;
}
// update mode pass
resetPreOrderHookFlags(hostView);
templateFn && executeTemplate(hostView, templateFn, 2 /* Update */, context);
refreshDescendantViews(hostView);
safeToRunHooks = true;
}
finally {
if (normalExecutionPath && !creationModeIsActive && rendererFactory.end) {
rendererFactory.end();
}
leaveView(oldView, safeToRunHooks);
}
}
/**
* @template T
* @param {?} lView
* @param {?} templateFn
* @param {?} rf
* @param {?} context
* @return {?}
*/
function executeTemplate(lView, templateFn, rf, context) {
ɵɵnamespaceHTML();
/** @type {?} */
const prevSelectedIndex = getSelectedIndex();
try {
setActiveHostElement(null);
if (rf & 2 /* Update */) {
// When we're updating, have an inherent ɵɵselect(0) so we don't have to generate that
// instruction for most update blocks
selectInternal(lView, 0);
}
templateFn(rf, context);
}
finally {
setSelectedIndex(prevSelectedIndex);
}
}
/**
* This function returns the default configuration of rendering flags depending on when the
* template is in creation mode or update mode. Update block and create block are
* always run separately.
* @param {?} view
* @return {?}
*/
function getRenderFlags(view) {
return isCreationMode(view) ? 1 /* Create */ : 2 /* Update */;
}
//////////////////////////
//// Element
//////////////////////////
/**
* Appropriately sets `stylingTemplate` on a TNode
*
* Does not apply styles to DOM nodes
*
* @param {?} tView
* @param {?} tNode The node whose `stylingTemplate` to set
* @param {?} attrs The attribute array source to set the attributes from
* @param {?} attrsStartIndex Optional start index to start processing the `attrs` from
* @return {?}
*/
export function setNodeStylingTemplate(tView, tNode, attrs, attrsStartIndex) {
if (tView.firstTemplatePass && !tNode.stylingTemplate) {
/** @type {?} */
const stylingAttrsStartIndex = attrsStylingIndexOf(attrs, attrsStartIndex);
if (stylingAttrsStartIndex >= 0) {
tNode.stylingTemplate = initializeStaticStylingContext(attrs, stylingAttrsStartIndex);
}
}
}
/**
* @param {?} tView
* @param {?} tNode
* @param {?} lView
* @return {?}
*/
export function executeContentQueries(tView, tNode, lView) {
if (isContentQueryHost(tNode)) {
/** @type {?} */
const start = tNode.directiveStart;
/** @type {?} */
const end = tNode.directiveEnd;
for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
/** @type {?} */
const def = (/** @type {?} */ (tView.data[directiveIndex]));
if (def.contentQueries) {
def.contentQueries(1 /* Create */, lView[directiveIndex], directiveIndex);
}
}
}
}
/**
* Creates directive instances and populates local refs.
*
* @param {?} tView
* @param {?} lView
* @param {?} localRefs Local refs of the node in question
* @param {?=} localRefExtractor mapping function that extracts local ref value from TNode
* @return {?}
*/
export function createDirectivesAndLocals(tView, lView, localRefs, localRefExtractor = getNativeByTNode) {
if (!getBindingsEnabled())
return;
/** @type {?} */
const previousOrParentTNode = getPreviousOrParentTNode();
if (tView.firstTemplatePass) {
ngDevMode && ngDevMode.firstTemplatePass++;
resolveDirectives(tView, lView, findDirectiveMatches(tView, lView, previousOrParentTNode), previousOrParentTNode, localRefs || null);
}
instantiateAllDirectives(tView, lView, previousOrParentTNode);
invokeDirectivesHostBindings(tView, lView, previousOrParentTNode);
saveResolvedLocalsInData(lView, previousOrParentTNode, localRefExtractor);
setActiveHostElement(null);
}
/**
* Takes a list of local names and indices and pushes the resolved local variable values
* to LView in the same order as they are loaded in the template with load().
* @param {?} viewData
* @param {?} tNode
* @param {?} localRefExtractor
* @return {?}
*/
function saveResolvedLocalsInData(viewData, tNode, localRefExtractor) {
/** @type {?} */
const localNames = tNode.localNames;
if (localNames) {
/** @type {?} */
let localIndex = tNode.index + 1;
for (let i = 0; i < localNames.length; i += 2) {
/** @type {?} */
const index = (/** @type {?} */ (localNames[i + 1]));
/** @type {?} */
const value = index === -1 ?
localRefExtractor((/** @type {?} */ (tNode)), viewData) :
viewData[index];
viewData[localIndex++] = value;
}
}
}
/**
* Gets TView from a template function or creates a new TView
* if it doesn't already exist.
*
* @param {?} def ComponentDef
* @return {?} TView
*/
export function getOrCreateTView(def) {
return def.tView || (def.tView = createTView(-1, def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas));
}
/**
* Creates a TView instance
*
* @param {?} viewIndex The viewBlockId for inline views, or -1 if it's a component/dynamic
* @param {?} templateFn Template function
* @param {?} consts The number of nodes, local refs, and pipes in this template
* @param {?} vars
* @param {?} directives Registry of directives for this view
* @param {?} pipes Registry of pipes for this view
* @param {?} viewQuery View queries for this view
* @param {?} schemas Schemas for this view
* @return {?}
*/
export function createTView(viewIndex, templateFn, consts, vars, directives, pipes, viewQuery, schemas) {
ngDevMode && ngDevMode.tView++;
/** @type {?} */
const bindingStartIndex = HEADER_OFFSET + consts;
// This length does not yet contain host bindings from child directives because at this point,
// we don't know which directives are active on this template. As soon as a directive is matched
// that has a host binding, we will update the blueprint with that def's hostVars count.
/** @type {?} */
const initialViewLength = bindingStartIndex + vars;
/** @type {?} */
const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength);
return blueprint[(/** @type {?} */ (TVIEW))] = ngDevMode ?
new TViewConstructor(viewIndex, // id: number,
blueprint, // blueprint: LView,
templateFn, // template: ComponentTemplate<{}>|null,
viewQuery, (/** @type {?} */ (null)), // node: TViewNode|TElementNode|null,
cloneToTViewData(blueprint).fill(null, bindingStartIndex), // data: TData,
bindingStartIndex, // bindingStartIndex: number,
initialViewLength, // viewQueryStartIndex: number,
initialViewLength, // expandoStartIndex: number,
null, // expandoInstructions: ExpandoInstructions|null,
true, // firstTemplatePass: boolean,
false, // staticViewQueries: boolean,
false, // staticContentQueries: boolean,
null, // preOrderHooks: HookData|null,
null, // preOrderCheckHooks: HookData|null,
null, // contentHooks: HookData|null,
null, // contentCheckHooks: HookData|null,
null, // viewHooks: HookData|null,
null, // viewCheckHooks: HookData|null,
null, // destroyHooks: HookData|null,
null, // cleanup: any[]|null,
null, // contentQueries: number[]|null,
null, // components: number[]|null,
typeof directives === 'function' ?
directives() :
directives, // directiveRegistry: DirectiveDefList|null,
typeof pipes === 'function' ? pipes() : pipes, // pipeRegistry: PipeDefList|null,
null, // firstChild: TNode|null,
schemas) :
{
id: viewIndex,
blueprint: blueprint,
template: templateFn,
viewQuery: viewQuery,
node: (/** @type {?} */ (null)),
data: blueprint.slice().fill(null, bindingStartIndex),
bindingStartIndex: bindingStartIndex,
viewQueryStartIndex: initialViewLength,
expandoStartIndex: initialViewLength,
expandoInstructions: null,
firstTemplatePass: true,
staticViewQueries: false,
staticContentQueries: false,
preOrderHooks: null,
preOrderCheckHooks: null,
contentHooks: null,
contentCheckHooks: null,
viewHooks: null,
viewCheckHooks: null,
destroyHooks: null,
cleanup: null,
contentQueries: null,
components: null,
directiveRegistry: typeof directives === 'function' ? directives() : directives,
pipeRegistry: typeof pipes === 'function' ? pipes() : pipes,
firstChild: null,
schemas: schemas,
};
}
/**
* @param {?} bindingStartIndex
* @param {?} initialViewLength
* @return {?}
*/
function createViewBlueprint(bindingStartIndex, initialViewLength) {
/** @type {?} */
const blueprint = (/** @type {?} */ (new (ngDevMode ? (/** @type {?} */ (LViewBlueprint)) : Array)(initialViewLength)
.fill(null, 0, bindingStartIndex)
.fill(NO_CHANGE, bindingStartIndex)));
blueprint[BINDING_INDEX] = bindingStartIndex;
return blueprint;
}
/**
* @param {?} text
* @param {?} token
* @return {?}
*/
export function createError(text, token) {
return new Error(`Renderer: ${text} [${stringifyForError(token)}]`);
}
/**
* Locates the host native element, used for bootstrapping existing nodes into rendering pipeline.
*
* @param {?} factory
* @param {?} elementOrSelector Render element or CSS selector to locate the element.
* @return {?}
*/
export function locateHostElement(factory, elementOrSelector) {
/** @type {?} */
const defaultRenderer = factory.createRenderer(null, null);
/** @type {?} */
const rNode = typeof elementOrSelector === 'string' ?
(isProceduralRenderer(defaultRenderer) ?
defaultRenderer.selectRootElement(elementOrSelector) :
defaultRenderer.querySelector(elementOrSelector)) :
elementOrSelector;
if (ngDevMode && !rNode) {
if (typeof elementOrSelector === 'string') {
throw createError('Host node with selector not found:', elementOrSelector);
}
else {
throw createError('Host node is required:', elementOrSelector);
}
}
return rNode;
}
/**
* Saves context for this cleanup function in LView.cleanupInstances.
*
* On the first template pass, saves in TView:
* - Cleanup function
* - Index of context we just saved in LView.cleanupInstances
* @param {?} lView
* @param {?} context
* @param {?} cleanupFn
* @return {?}
*/
export function storeCleanupWithContext(lView, context, cleanupFn) {
/** @type {?} */
const lCleanup = getCleanup(lView);
lCleanup.push(context);
if (lView[TVIEW].firstTemplatePass) {
getTViewCleanup(lView).push(cleanupFn, lCleanup.length - 1);
}
}
/**
* Saves the cleanup function itself in LView.cleanupInstances.
*
* This is necessary for functions that are wrapped with their contexts, like in renderer2
* listeners.
*
* On the first template pass, the index of the cleanup function is saved in TView.
* @param {?} view
* @param {?} cleanupFn
* @return {?}
*/
export function storeCleanupFn(view, cleanupFn) {
getCleanup(view).push(cleanupFn);
if (view[TVIEW].firstTemplatePass) {
getTViewCleanup(view).push((/** @type {?} */ (view[CLEANUP])).length - 1, null);
}
}
/**
* Constructs a TNode object from the arguments.
*
* @param {?} tParent
* @param {?} type The type of the node
* @param {?} adjustedIndex The index of the TNode in TView.data, adjusted for HEADER_OFFSET
* @param {?} tagName The tag name of the node
* @param {?} attrs The attributes defined on this node
* @return {?} the TNode object
*/
export function createTNode(tParent, type, adjustedIndex, tagName, attrs) {
ngDevMode && ngDevMode.tNode++;
return {
type: type,
index: adjustedIndex,
injectorIndex: tParent ? tParent.injectorIndex : -1,
directiveStart: -1,
directiveEnd: -1,
propertyMetadataStartIndex: -1,
propertyMetadataEndIndex: -1,
flags: 0,
providerIndexes: 0,
tagName: tagName,
attrs: attrs,
localNames: null,
initialInputs: undefined,
inputs: undefined,
outputs: undefined,
tViews: null,
next: null,
projectionNext: null,
child: null,
parent: tParent,
stylingTemplate: null,
projection: null,
onElementCreationFns: null,
// TODO (matsko): rename this to `styles` once the old styling impl is gone
newStyles: null,
// TODO (matsko): rename this to `classes` once the old styling impl is gone
newClasses: null,
};
}
/**
* Consolidates all inputs or outputs of all directives on this logical node.
*
* @param {?} tNode
* @param {?} direction whether to consider inputs or outputs
* @return {?} PropertyAliases|null aggregate of all properties if any, `null` otherwise
*/
export function generatePropertyAliases(tNode, direction) {
/** @type {?} */
const tView = getLView()[TVIEW];
/** @type {?} */
let propStore = null;
/** @type {?} */
const start = tNode.directiveStart;
/** @type {?} */
const end = tNode.directiveEnd;
if (end > start) {
/** @type {?} */
const isInput = direction === 0 /* Input */;
/** @type {?} */
const defs = tView.data;
for (let i = start; i < end; i++) {
/** @type {?} */
const directiveDef = (/** @type {?} */ (defs[i]));
/** @type {?} */
const propertyAliasMap = isInput ? directiveDef.inputs : directiveDef.outputs;
for (let publicName in propertyAliasMap) {
if (propertyAliasMap.hasOwnProperty(publicName)) {
propStore = propStore || {};
/** @type {?} */
const internalName = propertyAliasMap[publicName];
/** @type {?} */
const hasProperty = propStore.hasOwnProperty(publicName);
hasProperty ? propStore[publicName].push(i, publicName, internalName) :
(propStore[publicName] = [i, publicName, internalName]);
}
}
}
}
return propStore;
}
/**
* Mapping between attributes names that don't correspond to their element property names.
* Note: this mapping has to be kept in sync with the equally named mapping in the template
* type-checking machinery of ngtsc.
* @type {?}
*/
const ATTR_TO_PROP = {
'class': 'className',
'for': 'htmlFor',
'formaction': 'formAction',
'innerHtml': 'innerHTML',
'readonly': 'readOnly',
'tabindex': 'tabIndex',
};
/**
* @template T
* @param {?} index
* @param {?} propName
* @param {?} value
* @param {?=} sanitizer
* @param {?=} nativeOnly
* @param {?=} loadRendererFn
* @return {?}
*/
export function elementPropertyInternal(index, propName, value, sanitizer, nativeOnly, loadRendererFn) {
ngDevMode && assertNotSame(value, (/** @type {?} */ (NO_CHANGE)), 'Incoming value should never be NO_CHANGE.');
/** @type {?} */
const lView = getLView();
/** @type {?} */
const element = (/** @type {?} */ (getNativeByIndex(index, lView)));
/** @type {?} */
const tNode = getTNode(index, lView);
/** @type {?} */
let inputData;
/** @type {?} */
let dataValue;
if (!nativeOnly && (inputData = initializeTNodeInputs(tNode)) &&
(dataValue = inputData[propName])) {
setInputsForProperty(lView, dataValue, value);
if (isComponent(tNode))
markDirtyIfOnPush(lView, index + HEADER_OFFSET);
if (ngDevMode) {
if (tNode.type === 3 /* Element */ || tNode.type === 0 /* Container */) {
/**
* dataValue is an array containing runtime input or output names for the directives:
* i+0: directive instance index
* i+1: publicName
* i+2: privateName
*
* e.g. [0, 'change', 'change-minified']
* we want to set the reflected property with the privateName: dataValue[i+2]
*/
for (let i = 0; i < dataValue.length; i += 3) {
setNgReflectProperty(lView, element, tNode.type, (/** @type {?} */ (dataValue[i + 2])), value);
}
}
}
}
else if (tNode.type === 3 /* Element */) {
propName = ATTR_TO_PROP[propName] || propName;
if (ngDevMode) {
validateAgainstEventProperties(propName);
validateAgainstUnknownProperties(lView, element, propName, tNode);
ngDevMode.rendererSetProperty++;
}
savePropertyDebugData(tNode, lView, propName, lView[TVIEW].data, nativeOnly);
/** @type {?} */
const renderer = loadRendererFn ? loadRendererFn(tNode, lView) : lView[RENDERER];
// It is assumed that the sanitizer is only added when the compiler determines that the
// property
// is risky, so sanitization can be done without further checks.
value = sanitizer != null ? ((/** @type {?} */ (sanitizer(value, tNode.tagName || '', propName)))) : value;
if (isProceduralRenderer(renderer)) {
renderer.setProperty((/** @type {?} */ (element)), propName, value);
}
else if (!isAnimationProp(propName)) {
((/** @type {?} */ (element))).setProperty ? ((/** @type {?} */ (element))).setProperty(propName, value) :
((/** @type {?} */ (element)))[propName] = value;
}
}
else if (tNode.type === 0 /* Container */) {
// If the node is a container and the property didn't
// match any of the inputs or schemas we should throw.
if (ngDevMode && !matchingSchemas(lView, tNode.tagName)) {
throw createUnknownPropertyError(propName, tNode);
}
}
}
/**
* If node is an OnPush component, marks its LView dirty.
* @param {?} lView
* @param {?} viewIndex
* @return {?}
*/
function markDirtyIfOnPush(lView, viewIndex) {
ngDevMode && assertLView(lView);
/** @type {?} */
const childComponentLView = getComponentViewByIndex(viewIndex, lView);
if (!(childComponentLView[FLAGS] & 16 /* CheckAlways */)) {
childComponentLView[FLAGS] |= 64 /* Dirty */;
}
}
/**
* @param {?} lView
* @param {?} element
* @param {?} type
* @param {?} attrName
* @param {?} value
* @return {?}
*/
export function setNgReflectProperty(lView, element, type, attrName, value) {
/** @type {?} */
const renderer = lView[RENDERER];
attrName = normalizeDebugBindingName(attrName);
/** @type {?} */
const debugValue = normalizeDebugBindingValue(value);
if (type === 3 /* Element */) {
if (value == null) {
isProceduralRenderer(renderer) ? renderer.removeAttribute(((/** @type {?} */ (element))), attrName) :
((/** @type {?} */ (element))).removeAttribute(attrName);
}
else {
isProceduralRenderer(renderer) ?
renderer.setAttribute(((/** @type {?} */ (element))), attrName, debugValue) :
((/** @type {?} */ (element))).setAttribute(attrName, debugValue);
}
}
else {
/** @type {?} */
const textContent = `bindings=${JSON.stringify({ [attrName]: debugValue }, null, 2)}`;
if (isProceduralRenderer(renderer)) {
renderer.setValue(((/** @type {?} */ (element))), textContent);
}
else {
((/** @type {?} */ (element))).textContent = textContent;
}
}
}
/**
* @param {?} hostView
* @param {?} element
* @param {?} propName
* @param {?} tNode
* @return {?}
*/
function validateAgainstUnknownProperties(hostView, element, propName, tNode) {
// If the tag matches any of the schemas we shouldn't throw.
if (matchingSchemas(hostView, tNode.tagName)) {
return;
}
// If prop is not a known property of the HTML element...
if (!(propName in element) &&
// and we are in a browser context... (web worker nodes should be skipped)
typeof Node === 'function' && element instanceof Node &&
// and isn't a synthetic animation property...
propName[0] !== ANIMATION_PROP_PREFIX) {
// ... it is probably a user error and we should throw.
throw createUnknownPropertyError(propName, tNode);
}
}
/**
* @param {?} hostView
* @param {?} tagName
* @return {?}
*/
function matchingSchemas(hostView, tagName) {
/** @type {?} */
const schemas = hostView[TVIEW].schemas;
if (schemas !== null) {
for (let i = 0; i < schemas.length; i++) {
/** @type {?} */
const schema = schemas[i];
if (schema === NO_ERRORS_SCHEMA ||
schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
return true;
}
}
}
return false;
}
/**
* Stores debugging data for this property binding on first template pass.
* This enables features like DebugElement.properties.
* @param {?} tNode
* @param {?} lView
* @param {?} propName
* @param {?} tData
* @param {?} nativeOnly
* @return {?}
*/
function savePropertyDebugData(tNode, lView, propName, tData, nativeOnly) {
/** @type {?} */
const lastBindingIndex = lView[BINDING_INDEX] - 1;
// Bind/interpolation functions save binding metadata in the last binding index,
// but leave the property name blank. If the interpolation delimiter is at the 0
// index, we know that this is our first pass and the property name still needs to
// be set.
/** @type {?} */
const bindingMetadata = (/** @type {?} */ (tData[lastBindingIndex]));
if (bindingMetadata[0] == INTERPOLATION_DELIMITER) {
tData[lastBindingIndex] = propName + bindingMetadata;
// We don't want to store indices for host bindings because they are stored in a
// different part of LView (the expando section).
if (!nativeOnly) {
if (tNode.propertyMetadataStartIndex == -1) {
tNode.propertyMetadataStartIndex = lastBindingIndex;
}
tNode.propertyMetadataEndIndex = lastBindingIndex + 1;
}
}
}
/**
* Creates an error that should be thrown when encountering an unknown property on an element.
* @param {?} propName Name of the invalid property.
* @param {?} tNode Node on which we encountered the error.
* @return {?}
*/
function createUnknownPropertyError(propName, tNode) {
return new Error(`Template error: Can't bind to '${propName}' since it isn't a known property of '${tNode.tagName}'.`);
}
/**
* Instantiate a root component.
* @template T
* @param {?} tView
* @param {?} viewData
* @param {?} def
* @return {?}
*/
export function instantiateRootComponent(tView, viewData, def) {
/** @type {?} */
const rootTNode = getPreviousOrParentTNode();
if (tView.firstTemplatePass) {
if (def.providersResolver)
def.providersResolver(def);
generateExpandoInstructionBlock(tView, rootTNode, 1);
baseResolveDirective(tView, viewData, def, def.factory);
}
/** @type {?} */
const directive = getNodeInjectable(tView.data, viewData, viewData.length - 1, (/** @type {?} */ (rootTNode)));
postProcessBaseDirective(viewData, rootTNode, directive);
return directive;
}
/**
* Resolve the matched directives on a node.
* @param {?} tView
* @param {?} viewData
* @param {?} directives
* @param {?} tNode
* @param {?} localRefs
* @return {?}
*/
function resolveDirectives(tView, viewData, directives, tNode, localRefs) {
// Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in
// tsickle.
ngDevMode && assertEqual(tView.firstTemplatePass, true, 'should run on first template pass only');
/** @type {?} */
const exportsMap = localRefs ? { '': -1 } : null;
if (directives) {
initNodeFlags(tNode, tView.data.length, directives.length);
// When the same token is provided by several directives on the same node, some rules apply in
// the viewEngine:
// - viewProviders have priority over providers
// - the last directive in NgModule.declarations has priority over the previous one
// So to match these rules, the order in which providers are added in the arrays is very
// important.
for (let i = 0; i < directives.length; i++) {
/** @type {?} */
const def = (/** @type {?} */ (directives[i]));
if (def.providersResolver)
def.providersResolver(def);
}
generateExpandoInstructionBlock(tView, tNode, directives.length);
/** @type {?} */
const initialPreOrderHooksLength = (tView.preOrderHooks && tView.preOrderHooks.length) || 0;
/** @type {?} */
const initialPreOrderCheckHooksLength = (tView.preOrderCheckHooks && tView.preOrderCheckHooks.length) || 0;
/** @type {?} */
const nodeIndex = tNode.index - HEADER_OFFSET;
for (let i = 0; i < directives.length; i++) {
/** @type {?} */
const def = (/** @type {?} */ (directives[i]));
/** @type {?} */
const directiveDefIdx = tView.data.length;
baseResolveDirective(tView, viewData, def, def.factory);
saveNameToExportMap((/** @type {?} */ (tView.data)).length - 1, def, exportsMap);
// Init hooks are queued now so ngOnInit is called in host components before
// any projected components.
registerPreOrderHooks(directiveDefIdx, def, tView, nodeIndex, initialPreOrderHooksLength, initialPreOrderCheckHooksLength);
}
}
if (exportsMap)
cacheMatchingLocalNames(tNode, localRefs, exportsMap);
}
/**
* Instantiate all the directives that were previously resolved on the current node.
* @param {?} tView
* @param {?} lView
* @param {?} tNode
* @return {?}
*/
function instantiateAllDirectives(tView, lView, tNode) {
/** @type {?} */
const start = tNode.directiveStart;
/** @type {?} */
const end = tNode.directiveEnd;
if (!tView.firstTemplatePass && start < end) {
getOrCreateNodeInjectorForNode((/** @type {?} */ (tNode)), lView);
}
for (let i = start; i < end; i++) {
/** @type {?} */
const def = (/** @type {?} */ (tView.data[i]));
if (isComponentDef(def)) {
addComponentLogic(lView, tNode, (/** @type {?} */ (def)));
}
/** @type {?} */
const directive = getNodeInjectable(tView.data, (/** @type {?} */ (lView)), i, (/** @type {?} */ (tNode)));
postProcessDirective(lView, directive, def, i);
}
}
/**
* @param {?} tView
* @param {?} viewData
* @param {?} tNode
* @return {?}
*/
function invokeDirectivesHostBindings(tView, viewData, tNode) {
/** @type {?} */
const start = tNode.directiveStart;
/** @type {?} */
const end = tNode.directiveEnd;
/** @type {?} */
const expando = (/** @type {?} */ (tView.expandoInstructions));
/** @type {?} */
const firstTemplatePass = tView.firstTemplatePass;
/** @type {?} */
const elementIndex = tNode.index - HEADER_OFFSET;
/** @type {?} */
const selectedIndex = getSelectedIndex();
try {
setActiveHostElement(elementIndex);
for (let i = start; i < end; i++) {
/** @type {?} */
const def = (/** @type {?} */ (tView.data[i]));
/** @type {?} */
const directive = viewData[i];
if (def.hostBindings) {
invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstTemplatePass);
// Each directive gets a uniqueId value that is the same for both
// create and update calls when the hostBindings function is called. The
// directive uniqueId is not set anywhere--it is just incremented between
// each hostBindings call and is useful for helping instruction code
// uniquely determine which directive is currently active when executed.
incrementActiveDirectiveId();
}
else if (firstTemplatePass) {
expando.push(null);
}
}
}
finally {
setActiveHostElement(selectedIndex);
}
}
/**
* @param {?} def
* @param {?} expando
* @param {?} directive
* @param {?} tNode
* @param {?} firstTemplatePass
* @return {?}
*/
export function invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstTemplatePass) {
/** @type {?} */
const previousExpandoLength = expando.length;
setCurrentDirectiveDef(def);
/** @type {?} */
const elementIndex = tNode.index - HEADER_OFFSET;
(/** @type {?} */ (def.hostBindings))(1 /* Create */, directive, elementI