194 lines
7.3 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 {assertDataInRange, assertEqual} from '../../util/assert';
import {assertHasParent} from '../assert';
import {attachPatchData} from '../context_discovery';
import {executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags, registerPostOrderHooks} from '../hooks';
import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/container';
import {ComponentTemplate} from '../interfaces/definition';
import {LocalRefExtractor, TAttributes, TContainerNode, TNode, TNodeFlags, TNodeType, TViewNode} from '../interfaces/node';
import {isDirectiveHost} from '../interfaces/type_checks';
import {BINDING_INDEX, FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
import {assertNodeType} from '../node_assert';
import {appendChild, removeView} from '../node_manipulation';
import {getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state';
import {getNativeByTNode, load} from '../util/view_utils';
import {addToViewTree, createDirectivesInstances, createLContainer, createTNode, createTView, getOrCreateTNode, resolveDirectives, saveResolvedLocalsInData} from './shared';
/**
* Creates an LContainer for inline views, e.g.
*
* % if (showing) {
* <div></div>
* % }
*
* @param index The index of the container in the data array
*
* @codeGenApi
*/
export function ɵɵcontainer(index: number): void {
const lView = getLView();
const tNode = containerInternal(lView, index, null, null);
if (lView[TVIEW].firstTemplatePass) {
tNode.tViews = [];
}
setIsNotParent();
}
/**
* Creates an LContainer for an ng-template (dynamically-inserted view), e.g.
*
* <ng-template #foo>
* <div></div>
* </ng-template>
*
* @param index The index of the container in the data array
* @param templateFn Inline template
* @param consts The number of nodes, local refs, and pipes for this template
* @param vars The number of bindings for this template
* @param tagName The name of the container element, if applicable
* @param attrs The attrs attached to the container, if applicable
* @param localRefs A set of local reference bindings on the element.
* @param localRefExtractor A function which extracts local-refs values from the template.
* Defaults to the current element associated with the local-ref.
*
* @codeGenApi
*/
export function ɵɵtemplate(
index: number, templateFn: ComponentTemplate<any>| null, consts: number, vars: number,
tagName?: string | null, attrs?: TAttributes | null, localRefs?: string[] | null,
localRefExtractor?: LocalRefExtractor) {
const lView = getLView();
const tView = lView[TVIEW];
// TODO: consider a separate node type for templates
const tContainerNode = containerInternal(lView, index, tagName || null, attrs || null);
if (tView.firstTemplatePass) {
ngDevMode && ngDevMode.firstTemplatePass++;
resolveDirectives(tView, lView, tContainerNode, localRefs || null);
registerPostOrderHooks(tView, tContainerNode);
const embeddedTView = tContainerNode.tViews = createTView(
-1, templateFn, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null,
tView.schemas);
const embeddedTViewNode = createTNode(tView, null, TNodeType.View, -1, null, null) as TViewNode;
embeddedTViewNode.injectorIndex = tContainerNode.injectorIndex;
embeddedTView.node = embeddedTViewNode;
if (tView.queries !== null) {
tView.queries.template(tView, tContainerNode);
embeddedTView.queries = tView.queries.embeddedTView(tContainerNode);
}
}
if (isDirectiveHost(tContainerNode)) {
createDirectivesInstances(tView, lView, tContainerNode);
}
if (localRefs != null) {
saveResolvedLocalsInData(lView, tContainerNode, localRefExtractor);
}
setIsNotParent();
}
/**
* Sets a container up to receive views.
*
* @param index The index of the container in the data array
*
* @codeGenApi
*/
export function ɵɵcontainerRefreshStart(index: number): void {
const lView = getLView();
const tView = lView[TVIEW];
let previousOrParentTNode = load(tView.data, index) as TNode;
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
setPreviousOrParentTNode(previousOrParentTNode, true);
lView[index + HEADER_OFFSET][ACTIVE_INDEX] = 0;
// We need to execute init hooks here so ngOnInit hooks are called in top level views
// before they are called in embedded views (for backwards compatibility).
if (!getCheckNoChangesMode()) {
const hooksInitPhaseCompleted =
(lView[FLAGS] & LViewFlags.InitPhaseStateMask) === InitPhaseState.InitPhaseCompleted;
if (hooksInitPhaseCompleted) {
const preOrderCheckHooks = tView.preOrderCheckHooks;
if (preOrderCheckHooks !== null) {
executeCheckHooks(lView, preOrderCheckHooks, null);
}
} else {
const preOrderHooks = tView.preOrderHooks;
if (preOrderHooks !== null) {
executeInitAndCheckHooks(lView, preOrderHooks, InitPhaseState.OnInitHooksToBeRun, null);
}
incrementInitPhaseFlags(lView, InitPhaseState.OnInitHooksToBeRun);
}
}
}
/**
* Marks the end of the LContainer.
*
* Marking the end of LContainer is the time when to child views get inserted or removed.
*
* @codeGenApi
*/
export function ɵɵcontainerRefreshEnd(): void {
let previousOrParentTNode = getPreviousOrParentTNode();
if (getIsParent()) {
setIsNotParent();
} else {
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.View);
ngDevMode && assertHasParent(previousOrParentTNode);
previousOrParentTNode = previousOrParentTNode.parent !;
setPreviousOrParentTNode(previousOrParentTNode, false);
}
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
const lContainer: LContainer = getLView()[previousOrParentTNode.index];
const nextIndex = lContainer[ACTIVE_INDEX];
// remove extra views at the end of the container
while (nextIndex < lContainer.length - CONTAINER_HEADER_OFFSET) {
removeView(lContainer, nextIndex);
}
}
function containerInternal(
lView: LView, nodeIndex: number, tagName: string | null,
attrs: TAttributes | null): TContainerNode {
ngDevMode && assertEqual(
lView[BINDING_INDEX], lView[TVIEW].bindingStartIndex,
'container nodes should be created before any bindings');
const adjustedIndex = nodeIndex + HEADER_OFFSET;
ngDevMode && assertDataInRange(lView, nodeIndex + HEADER_OFFSET);
ngDevMode && ngDevMode.rendererCreateComment++;
const comment = lView[adjustedIndex] =
lView[RENDERER].createComment(ngDevMode ? 'container' : '');
const tNode =
getOrCreateTNode(lView[TVIEW], lView[T_HOST], nodeIndex, TNodeType.Container, tagName, attrs);
const lContainer = lView[adjustedIndex] = createLContainer(comment, lView, comment, tNode);
appendChild(comment, tNode, lView);
attachPatchData(getNativeByTNode(tNode, lView), lView);
// Containers are added to the current view tree instead of their embedded views
// because views can be removed and re-inserted.
addToViewTree(lView, lContainer);
ngDevMode && assertNodeType(getPreviousOrParentTNode(), TNodeType.Container);
return tNode;
}