refactor(core): Create `TNodeType.Text` to display full template in `TView` debug (#39233)

When looking at `TView` debug template only Element nodes were displayed
as `TNode.Element` was used for both `RElement` and `RText`.
Additionally no text was stored in `TNode.value`. The result was that
the whole template could not be reconstructed. This refactoring creates
`TNodeType.Text` and store the text value in `TNode.value`.

The refactoring also changes `TNodeType` into flag-like structure make
it more efficient to check many different types at once.

PR Close #39233
This commit is contained in:
Misko Hevery 2020-10-12 21:38:06 -07:00 committed by Alex Rickabaugh
parent d50df92568
commit 70f1e2e04a
26 changed files with 264 additions and 248 deletions

View File

@ -529,7 +529,7 @@ function _queryNodeChildrenR3(
ngDevMode && assertTNodeForLView(tNode, lView);
const nativeNode = getNativeByTNodeOrNull(tNode, lView);
// For each type of TNode, specific logic is executed.
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
if (tNode.type & (TNodeType.AnyRNode | TNodeType.ElementContainer)) {
// Case 1: the TNode is an element
// The native node has to be checked.
_addQueryMatchR3(nativeNode, predicate, matches, elementsOnly, rootNativeNode);
@ -565,14 +565,14 @@ function _queryNodeChildrenR3(
_queryNodeChildrenInContainerR3(
nodeOrContainer, predicate, matches, elementsOnly, rootNativeNode);
}
} else if (tNode.type === TNodeType.Container) {
} else if (tNode.type & TNodeType.Container) {
// Case 2: the TNode is a container
// The native node has to be checked.
const lContainer = lView[tNode.index];
_addQueryMatchR3(lContainer[NATIVE], predicate, matches, elementsOnly, rootNativeNode);
// Each view inside the container has to be processed.
_queryNodeChildrenInContainerR3(lContainer, predicate, matches, elementsOnly, rootNativeNode);
} else if (tNode.type === TNodeType.Projection) {
} else if (tNode.type & TNodeType.Projection) {
// Case 3: the TNode is a projection insertion point (i.e. a <ng-content>).
// The nodes projected at this location all need to be processed.
const componentView = lView![DECLARATION_COMPONENT_VIEW];

View File

@ -13,7 +13,7 @@ import {injectRootLimpMode, setInjectImplementation} from '../di/injector_compat
import {getInjectorDef} from '../di/interface/defs';
import {InjectFlags} from '../di/interface/injector';
import {Type} from '../interface/type';
import {assertDefined, assertEqual, assertIndexInRange, throwError} from '../util/assert';
import {assertDefined, assertEqual, assertIndexInRange} from '../util/assert';
import {noSideEffects} from '../util/closure';
import {assertDirectiveDef, assertNodeInjector, assertTNodeForLView} from './assert';
@ -25,7 +25,7 @@ import {isFactory, NO_PARENT_INJECTOR, NodeInjectorFactory, NodeInjectorOffset,
import {AttributeMarker, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeProviderIndexes, TNodeType} from './interfaces/node';
import {isComponentDef, isComponentHost} from './interfaces/type_checks';
import {DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, INJECTOR, LView, T_HOST, TData, TVIEW, TView, TViewType} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
import {assertTNodeType} from './node_assert';
import {enterDI, leaveDI} from './state';
import {isNameOnlyAttributeMarker} from './util/attrs_utils';
import {getParentInjectorIndex, getParentInjectorView, hasParentInjector} from './util/injector_utils';
@ -301,9 +301,7 @@ export function diPublicInInjector(
* @publicApi
*/
export function injectAttributeImpl(tNode: TNode, attrNameToInject: string): string|null {
ngDevMode &&
assertNodeOfPossibleTypes(
tNode, [TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer]);
ngDevMode && assertTNodeType(tNode, TNodeType.AnyContainer | TNodeType.AnyRNode);
ngDevMode && assertDefined(tNode, 'expecting tNode');
if (attrNameToInject === 'class') {
return tNode.classes;
@ -506,7 +504,7 @@ function searchTokensOnInjector<T>(
// - AND the parent TNode is an Element.
// This means that we just came from the Component's View and therefore are allowed to see
// into the ViewProviders.
(previousTView != currentTView && (tNode.type === TNodeType.Element));
(previousTView != currentTView && ((tNode.type & TNodeType.AnyRNode) !== 0));
// This special case happens when there is a @host on the inject and when we are searching
// on the host element node.

View File

@ -10,7 +10,7 @@ import {getPluralCase} from '../../i18n/localization';
import {assertDefined, assertDomNode, assertEqual, assertGreaterThan, assertIndexInRange, throwError} from '../../util/assert';
import {assertIndexInExpandoRange, assertTIcu} from '../assert';
import {attachPatchData} from '../context_discovery';
import {elementPropertyInternal, setElementAttribute, textBindingInternal} from '../instructions/shared';
import {elementPropertyInternal, setElementAttribute} from '../instructions/shared';
import {COMMENT_MARKER, ELEMENT_MARKER, getCurrentICUCaseIndex, getParentFromI18nMutateOpCode, getRefFromI18nMutateOpCode, I18nCreateOpCode, I18nCreateOpCodes, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, IcuType, TI18n, TIcu} from '../interfaces/i18n';
import {TNode} from '../interfaces/node';
import {RElement, RNode, RText} from '../interfaces/renderer';

View File

@ -63,7 +63,7 @@ export function addTNodeAndUpdateInsertBeforeIndex(previousTNodes: TNode[], newT
}
function isI18nText(tNode: TNode): boolean {
return tNode.type !== TNodeType.Placeholder;
return !(tNode.type & TNodeType.Placeholder);
}
function isNewTNodeCreatedBefore(existingTNode: TNode, newTNode: TNode): boolean {

View File

@ -17,7 +17,7 @@ import {loadIcuContainerVisitor} from '../instructions/i18n_icu_container_visito
import {allocExpando, createTNodeAtIndex, elementAttributeInternal, setInputsForProperty, setNgReflectProperties} from '../instructions/shared';
import {getDocument} from '../interfaces/document';
import {COMMENT_MARKER, ELEMENT_MARKER, ensureIcuContainerVisitorLoaded, I18nCreateOpCode, I18nCreateOpCodes, I18nMutateOpCode, i18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, IcuExpression, IcuType, TI18n, TIcu} from '../interfaces/i18n';
import {TIcuContainerNode, TNode, TNodeType} from '../interfaces/node';
import {TNode, TNodeType} from '../interfaces/node';
import {RComment, RElement} from '../interfaces/renderer';
import {SanitizerFn} from '../interfaces/sanitization';
import {HEADER_OFFSET, LView, TView} from '../interfaces/view';
@ -152,7 +152,7 @@ export function i18nStartFirstCreatePass(
*/
function createTNodeAndAddOpCode(
tView: TView, rootTNode: TNode|null, existingTNodes: TNode[], lView: LView,
createOpCodes: I18nCreateOpCodes, text: string, isICU: boolean): TNode {
createOpCodes: I18nCreateOpCodes, text: string|null, isICU: boolean): TNode {
const i18nNodeIdx = allocExpando(tView, lView, 1);
let opCode = i18nNodeIdx << I18nCreateOpCode.SHIFT;
let parentTNode = getCurrentParentTNode();
@ -174,9 +174,12 @@ function createTNodeAndAddOpCode(
opCode |= I18nCreateOpCode.COMMENT;
ensureIcuContainerVisitorLoaded(loadIcuContainerVisitor);
}
createOpCodes.push(opCode, text);
createOpCodes.push(opCode, text === null ? '' : text);
// We store `{{?}}` so that when looking at debug `TNodeType.template` we can see where the
// bindings are.
const tNode = createTNodeAtIndex(
tView, i18nNodeIdx, isICU ? TNodeType.IcuContainer : TNodeType.Element, null, null);
tView, i18nNodeIdx, isICU ? TNodeType.Icu : TNodeType.Text,
text === null ? (ngDevMode ? '{{?}}' : '') : text, null);
addTNodeAndUpdateInsertBeforeIndex(existingTNodes, tNode);
const tNodeIdx = tNode.index;
setCurrentTNode(tNode, false /* Text nodes are self closing */);
@ -212,7 +215,7 @@ function i18nStartFirstCreatePassProcessTextNode(
updateOpCodes: I18nUpdateOpCodes, lView: LView, text: string): void {
const hasBinding = text.match(BINDING_REGEXP);
const tNode = createTNodeAndAddOpCode(
tView, rootTNode, existingTNodes, lView, createOpCodes, hasBinding ? '' : text, false);
tView, rootTNode, existingTNodes, lView, createOpCodes, hasBinding ? null : text, false);
if (hasBinding) {
generateBindingUpdateOpCodes(updateOpCodes, text, tNode.index);
}
@ -251,7 +254,7 @@ export function i18nAttributesFirstPass(
const tNode = getTNode(tView, previousElementIndex - HEADER_OFFSET);
// Set attributes for Elements only, for other types (like ElementContainer),
// only set inputs below
if (tNode.type === TNodeType.Element) {
if (tNode.type & TNodeType.AnyRNode) {
elementAttributeInternal(tNode, lView, attrName, value, null, null);
}
// Check if that attribute is a directive input

View File

@ -12,7 +12,7 @@ import {createTNodeAtIndex} from '../instructions/shared';
import {TIcu} from '../interfaces/i18n';
import {TIcuContainerNode, TNode, TNodeType} from '../interfaces/node';
import {TView} from '../interfaces/view';
import {assertNodeType} from '../node_assert';
import {assertTNodeType} from '../node_assert';
import {addTNodeAndUpdateInsertBeforeIndex} from './i18n_insert_before_index';
@ -69,7 +69,7 @@ export function setTIcu(tView: TView, index: number, tIcu: TIcu): void {
if (tNode === null) {
tView.data[index] = tIcu;
} else {
ngDevMode && assertNodeType(tNode, TNodeType.IcuContainer);
ngDevMode && assertTNodeType(tNode, TNodeType.Icu);
tNode.value = tIcu;
}
}

View File

@ -14,7 +14,7 @@ import {hasClassInput, hasStyleInput, TAttributes, TElementNode, TNode, TNodeFla
import {RElement} from '../interfaces/renderer';
import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks';
import {HEADER_OFFSET, LView, RENDERER, TView} from '../interfaces/view';
import {assertNodeType} from '../node_assert';
import {assertTNodeType} from '../node_assert';
import {appendChild, createElementNode, writeDirectClass, writeDirectStyle} from '../node_manipulation';
import {decreaseElementDepthCount, getBindingIndex, getCurrentTNode, getElementDepthCount, getLView, getNamespace, getTView, increaseElementDepthCount, isCurrentTNodeParent, setCurrentTNode, setCurrentTNodeAsNotParent} from '../state';
import {computeStaticStyling} from '../styling/static_styling';
@ -24,6 +24,7 @@ import {setDirectiveInputsWhichShadowsStyling} from './property';
import {createDirectivesInstances, executeContentQueries, getOrCreateTNode, matchingSchemas, resolveDirectives, saveResolvedLocalsInData} from './shared';
function elementStartFirstCreatePass(
index: number, tView: TView, lView: LView, native: RElement, name: string,
attrsIndex?: number|null, localRefsIndex?: number): TElementNode {
@ -140,7 +141,7 @@ export function ɵɵelementEnd(): void {
}
const tNode = currentTNode;
ngDevMode && assertNodeType(tNode, TNodeType.Element);
ngDevMode && assertTNodeType(tNode, TNodeType.AnyRNode);
decreaseElementDepthCount();

View File

@ -12,7 +12,7 @@ import {registerPostOrderHooks} from '../hooks';
import {TAttributes, TElementContainerNode, TNodeType} from '../interfaces/node';
import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks';
import {HEADER_OFFSET, LView, RENDERER, T_HOST, TView} from '../interfaces/view';
import {assertNodeType} from '../node_assert';
import {assertTNodeType} from '../node_assert';
import {appendChild} from '../node_manipulation';
import {getBindingIndex, getCurrentTNode, getLView, getTView, isCurrentTNodeParent, setCurrentTNode, setCurrentTNodeAsNotParent} from '../state';
import {computeStaticStyling} from '../styling/static_styling';
@ -108,7 +108,7 @@ export function ɵɵelementContainerEnd(): void {
setCurrentTNode(currentTNode, false);
}
ngDevMode && assertNodeType(currentTNode, TNodeType.ElementContainer);
ngDevMode && assertTNodeType(currentTNode, TNodeType.ElementContainer);
if (tView.firstCreatePass) {
registerPostOrderHooks(tView, currentTNode);

View File

@ -62,7 +62,7 @@ export function ɵɵi18nStart(
const parentRNode = getClosestRElement(tView, sameViewParentTNode, lView);
// If `parentTNode` is an `ElementContainer` than it has `<!--ng-container--->`.
// When we do inserts we have to make sure to insert in front of `<!--ng-container--->`.
const insertInFrontOf = parentTNode && parentTNode.type === TNodeType.ElementContainer ?
const insertInFrontOf = parentTNode && (parentTNode.type & TNodeType.ElementContainer) ?
lView[parentTNode.index] :
null;
applyCreateOpCodes(lView, tI18n.create, parentRNode, insertInFrontOf);

View File

@ -14,7 +14,7 @@ import {PropertyAliasValue, TNode, TNodeFlags, TNodeType} from '../interfaces/no
import {GlobalTargetResolver, isProceduralRenderer, RElement, Renderer3} from '../interfaces/renderer';
import {isDirectiveHost} from '../interfaces/type_checks';
import {CLEANUP, FLAGS, LView, LViewFlags, RENDERER, TView} from '../interfaces/view';
import {assertNodeOfPossibleTypes} from '../node_assert';
import {assertTNodeType} from '../node_assert';
import {getCurrentDirectiveDef, getCurrentTNode, getLView, getTView} from '../state';
import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode} from '../util/view_utils';
@ -126,14 +126,12 @@ function listenerInternal(
// register a listener and store its cleanup function on LView.
const lCleanup = getLCleanup(lView);
ngDevMode &&
assertNodeOfPossibleTypes(
tNode, [TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer]);
ngDevMode && assertTNodeType(tNode, TNodeType.AnyRNode | TNodeType.AnyContainer);
let processOutputs = true;
// add native event listener - applicable to elements only
if (tNode.type === TNodeType.Element) {
if (tNode.type & TNodeType.AnyRNode) {
const native = getNativeByTNode(tNode, lView) as RElement;
const resolved = eventTargetResolver ? eventTargetResolver(native) : EMPTY_OBJ as any;
const target = resolved.target || native;

View File

@ -17,7 +17,7 @@ import {getInjectorIndex} from '../di';
import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS, NATIVE} from '../interfaces/container';
import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition';
import {NO_PARENT_INJECTOR, NodeInjectorOffset} from '../interfaces/injector';
import {AttributeMarker, InsertBeforeIndex, PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TNodeTypeAsString} from '../interfaces/node';
import {AttributeMarker, InsertBeforeIndex, PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, toTNodeTypeAsString} from '../interfaces/node';
import {SelectorFlags} from '../interfaces/projection';
import {LQueries, TQueries} from '../interfaces/query';
import {RComment, RElement, Renderer3, RendererFactory3, RNode} from '../interfaces/renderer';
@ -239,7 +239,7 @@ class TNode implements ITNode {
}
get type_(): string {
return TNodeTypeAsString[this.type] || `TNodeType.?${this.type}?`;
return toTNodeTypeAsString(this.type) || `TNodeType.?${this.type}?`;
}
get flags_(): string {
@ -256,7 +256,7 @@ class TNode implements ITNode {
}
get template_(): string {
if (this.value === null && this.type === TNodeType.Element) return '#text';
if (this.type & TNodeType.Text) return this.value!;
const buf: string[] = [];
const tagName = typeof this.value === 'string' && this.value || this.type_;
buf.push('<', tagName);
@ -594,7 +594,7 @@ export function buildDebugNode(tNode: ITNode, lView: LView): DebugNode {
}
return {
html: toHtml(native),
type: TNodeTypeAsString[tNode.type],
type: toTNodeTypeAsString(tNode.type),
native: native as any,
children: toDebugNodes(tNode.child, lView),
factories,

View File

@ -5,43 +5,44 @@
* 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 { Injector } from '../../di';
import { ErrorHandler } from '../../error_handler';
import { DoCheck, OnChanges, OnInit } from '../../interface/lifecycle_hooks';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata } from '../../metadata/schema';
import { ViewEncapsulation } from '../../metadata/view';
import { validateAgainstEventAttributes, validateAgainstEventProperties } from '../../sanitization/sanitization';
import { Sanitizer } from '../../sanitization/sanitizer';
import { assertDefined, assertDomNode, assertEqual, assertGreaterThan, assertIndexInRange, assertLessThan, assertNotEqual, assertNotSame, assertSame, assertString } from '../../util/assert';
import { createNamedArrayType } from '../../util/named_array_type';
import { initNgDevMode } from '../../util/ng_dev_mode';
import { normalizeDebugBindingName, normalizeDebugBindingValue } from '../../util/ng_reflect';
import { stringify } from '../../util/stringify';
import { assertFirstCreatePass, assertFirstUpdatePass, assertLContainer, assertLView, assertTNodeForLView, assertTNodeForTView } from '../assert';
import { attachPatchData } from '../context_discovery';
import { getFactoryDef } from '../definition';
import { diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode } from '../di';
import { throwMultipleComponentError } from '../errors';
import { executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags } from '../hooks';
import { CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS } from '../interfaces/container';
import { ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction } from '../interfaces/definition';
import { NodeInjectorFactory, NodeInjectorOffset } from '../interfaces/injector';
import { AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliases, PropertyAliasValue, TAttributes, TConstantsOrFactory, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode } from '../interfaces/node';
import { isProceduralRenderer, RComment, RElement, Renderer3, RendererFactory3, RNode, RText } from '../interfaces/renderer';
import { SanitizerFn } from '../interfaces/sanitization';
import { isComponentDef, isComponentHost, isContentQueryHost, isRootView } from '../interfaces/type_checks';
import { CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, InitPhaseState, INJECTOR, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TRANSPLANTED_VIEWS_TO_REFRESH, TVIEW, TView, TViewType, T_HOST } from '../interfaces/view';
import { assertNodeNotOfTypes, assertNodeOfPossibleTypes } from '../node_assert';
import { updateTextNode } from '../node_manipulation';
import { isInlineTemplate, isNodeMatchingSelectorList } from '../node_selector_matcher';
import { enterView, getBindingsEnabled, getCurrentDirectiveIndex, getCurrentParentTNode, getCurrentTNode, getCurrentTNodePlaceholderOk, getSelectedIndex, isCurrentTNodeParent, isInCheckNoChangesMode, isInI18nBlock, leaveView, setBindingIndex, setBindingRootForHostBindings, setCurrentDirectiveIndex, setCurrentQueryIndex, setCurrentTNode, setIsInCheckNoChangesMode, setSelectedIndex } from '../state';
import { NO_CHANGE } from '../tokens';
import { isAnimationProp, mergeHostAttrs } from '../util/attrs_utils';
import { INTERPOLATION_DELIMITER, renderStringify, stringifyForError } from '../util/misc_utils';
import { getFirstLContainer, getLViewParent, getNextLContainer } from '../util/view_traversal_utils';
import { getComponentLViewByIndex, getNativeByIndex, getNativeByTNode, isCreationMode, readPatchedLView, resetPreOrderHookFlags, unwrapLView, updateTransplantedViewCount, viewAttachedToChangeDetector } from '../util/view_utils';
import { selectIndexInternal } from './advance';
import { attachLContainerDebug, attachLViewDebug, cloneToLViewFromTViewBlueprint, cloneToTViewData, LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeDebug, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor } from './lview_debug';
import {Injector} from '../../di';
import {ErrorHandler} from '../../error_handler';
import {DoCheck, OnChanges, OnInit} from '../../interface/lifecycle_hooks';
import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from '../../metadata/schema';
import {ViewEncapsulation} from '../../metadata/view';
import {validateAgainstEventAttributes, validateAgainstEventProperties} from '../../sanitization/sanitization';
import {Sanitizer} from '../../sanitization/sanitizer';
import {assertDefined, assertDomNode, assertEqual, assertGreaterThan, assertIndexInRange, assertLessThan, assertNotEqual, assertNotSame, assertSame, assertString} from '../../util/assert';
import {createNamedArrayType} from '../../util/named_array_type';
import {initNgDevMode} from '../../util/ng_dev_mode';
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect';
import {stringify} from '../../util/stringify';
import {assertFirstCreatePass, assertFirstUpdatePass, assertLContainer, assertLView, assertTNodeForLView, assertTNodeForTView} from '../assert';
import {attachPatchData} from '../context_discovery';
import {getFactoryDef} from '../definition';
import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from '../di';
import {throwMultipleComponentError} from '../errors';
import {executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags} from '../hooks';
import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS} from '../interfaces/container';
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
import {NodeInjectorFactory, NodeInjectorOffset} from '../interfaces/injector';
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliases, PropertyAliasValue, TAttributes, TConstantsOrFactory, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode} from '../interfaces/node';
import {isProceduralRenderer, RComment, RElement, Renderer3, RendererFactory3, RNode, RText} from '../interfaces/renderer';
import {SanitizerFn} from '../interfaces/sanitization';
import {isComponentDef, isComponentHost, isContentQueryHost, isRootView} from '../interfaces/type_checks';
import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, InitPhaseState, INJECTOR, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, T_HOST, TData, TRANSPLANTED_VIEWS_TO_REFRESH, TVIEW, TView, TViewType} from '../interfaces/view';
import {assertPureTNodeType, assertTNodeType} from '../node_assert';
import {updateTextNode} from '../node_manipulation';
import {isInlineTemplate, isNodeMatchingSelectorList} from '../node_selector_matcher';
import {enterView, getBindingsEnabled, getCurrentDirectiveIndex, getCurrentParentTNode, getCurrentTNode, getCurrentTNodePlaceholderOk, getSelectedIndex, isCurrentTNodeParent, isInCheckNoChangesMode, isInI18nBlock, leaveView, setBindingIndex, setBindingRootForHostBindings, setCurrentDirectiveIndex, setCurrentQueryIndex, setCurrentTNode, setIsInCheckNoChangesMode, setSelectedIndex} from '../state';
import {NO_CHANGE} from '../tokens';
import {isAnimationProp, mergeHostAttrs} from '../util/attrs_utils';
import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils';
import {getFirstLContainer, getLViewParent, getNextLContainer} from '../util/view_traversal_utils';
import {getComponentLViewByIndex, getNativeByIndex, getNativeByTNode, isCreationMode, readPatchedLView, resetPreOrderHookFlags, unwrapLView, updateTransplantedViewCount, viewAttachedToChangeDetector} from '../util/view_utils';
import {selectIndexInternal} from './advance';
import {attachLContainerDebug, attachLViewDebug, cloneToLViewFromTViewBlueprint, cloneToTViewData, LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeDebug, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor} from './lview_debug';
@ -196,7 +197,7 @@ export function createLView<T>(
* @param attrs Any attrs for the native element, if applicable
*/
export function getOrCreateTNode(
tView: TView, index: number, type: TNodeType.Element, name: string|null,
tView: TView, index: number, type: TNodeType.Element|TNodeType.Text, name: string|null,
attrs: TAttributes|null): TElementNode;
export function getOrCreateTNode(
tView: TView, index: number, type: TNodeType.Container, name: string|null,
@ -208,12 +209,13 @@ export function getOrCreateTNode(
tView: TView, index: number, type: TNodeType.ElementContainer, name: string|null,
attrs: TAttributes|null): TElementContainerNode;
export function getOrCreateTNode(
tView: TView, index: number, type: TNodeType.IcuContainer, name: null,
tView: TView, index: number, type: TNodeType.Icu, name: null,
attrs: TAttributes|null): TElementContainerNode;
export function getOrCreateTNode(
tView: TView, index: number, type: TNodeType, name: string|null, attrs: TAttributes|null):
TElementNode&TContainerNode&TElementContainerNode&TProjectionNode&TIcuContainerNode {
// Keep this function short, so that the VM will inline it.
ngDevMode && assertPureTNodeType(type);
const adjustedIndex = index + HEADER_OFFSET;
let tNode = tView.data[adjustedIndex] as TNode;
if (tNode === null) {
@ -225,7 +227,7 @@ export function getOrCreateTNode(
// removed, so we mark it as detached.
tNode.flags |= TNodeFlags.isDetached;
}
} else if (tNode.type == TNodeType.Placeholder) {
} else if (tNode.type & TNodeType.Placeholder) {
tNode.type = type;
tNode.value = name;
tNode.attrs = attrs;
@ -818,13 +820,13 @@ export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Container,
adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TContainerNode;
export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Element,
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Element|TNodeType.Text,
adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TElementNode;
export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.ElementContainer,
adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TElementContainerNode;
export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.IcuContainer,
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Icu,
adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TIcuContainerNode;
export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Projection,
@ -1012,7 +1014,7 @@ export function elementPropertyInternal<T>(
if (ngDevMode) {
setNgReflectProperties(lView, element, tNode.type, dataValue, value);
}
} else if (tNode.type === TNodeType.Element) {
} else if (tNode.type & TNodeType.AnyRNode) {
propName = mapPropName(propName);
if (ngDevMode) {
@ -1034,7 +1036,7 @@ export function elementPropertyInternal<T>(
(element as RElement).setProperty ? (element as any).setProperty(propName, value) :
(element as any)[propName] = value;
}
} else if (tNode.type === TNodeType.Container || tNode.type === TNodeType.ElementContainer) {
} else if (tNode.type & TNodeType.AnyContainer) {
// If the node is a container and the property didn't
// match any of the inputs or schemas we should throw.
if (ngDevMode && !matchingSchemas(tView, tNode.value)) {
@ -1057,7 +1059,7 @@ function setNgReflectProperty(
const renderer = lView[RENDERER];
attrName = normalizeDebugBindingName(attrName);
const debugValue = normalizeDebugBindingValue(value);
if (type === TNodeType.Element) {
if (type & TNodeType.AnyRNode) {
if (value == null) {
isProceduralRenderer(renderer) ? renderer.removeAttribute((element as RElement), attrName) :
(element as RElement).removeAttribute(attrName);
@ -1079,7 +1081,7 @@ function setNgReflectProperty(
export function setNgReflectProperties(
lView: LView, element: RElement|RComment, type: TNodeType, dataValue: PropertyAliasValue,
value: any) {
if (type === TNodeType.Element || type === TNodeType.Container) {
if (type & (TNodeType.AnyRNode | TNodeType.Container)) {
/**
* dataValue is an array containing runtime input or output names for the directives:
* i+0: directive instance index
@ -1300,7 +1302,7 @@ function instantiateAllDirectives(
const isComponent = isComponentDef(def);
if (isComponent) {
ngDevMode && assertNodeOfPossibleTypes(tNode, [TNodeType.Element]);
ngDevMode && assertTNodeType(tNode, TNodeType.AnyRNode);
addComponentLogic(lView, tNode as TElementNode, def as ComponentDef<any>);
}
@ -1386,9 +1388,7 @@ function findDirectiveDefMatches(
tView: TView, viewData: LView,
tNode: TElementNode|TContainerNode|TElementContainerNode): DirectiveDef<any>[]|null {
ngDevMode && assertFirstCreatePass(tView);
ngDevMode &&
assertNodeOfPossibleTypes(
tNode, [TNodeType.Element, TNodeType.ElementContainer, TNodeType.Container]);
ngDevMode && assertTNodeType(tNode, TNodeType.AnyRNode | TNodeType.AnyContainer);
const registry = tView.directiveRegistry;
let matches: any[]|null = null;
@ -1401,8 +1401,8 @@ function findDirectiveDefMatches(
if (isComponentDef(def)) {
if (ngDevMode) {
assertNodeOfPossibleTypes(
tNode, [TNodeType.Element],
assertTNodeType(
tNode, TNodeType.Element,
`"${tNode.value}" tags cannot be used as component hosts. ` +
`Please use a different tag to activate the ${stringify(def.type)} component.`);
@ -1518,8 +1518,8 @@ export function elementAttributeInternal(
if (ngDevMode) {
assertNotSame(value, NO_CHANGE as any, 'Incoming value should never be NO_CHANGE.');
validateAgainstEventAttributes(name);
assertNodeNotOfTypes(
tNode, [TNodeType.Container, TNodeType.ElementContainer],
assertTNodeType(
tNode, TNodeType.Element,
`Attempted to set attribute \`${name}\` on a container node. ` +
`Host bindings are not valid on ng-container or ng-template.`);
}

View File

@ -705,7 +705,7 @@ function updateStylingMap(
function updateStyling(
tView: TView, tNode: TNode, lView: LView, renderer: Renderer3, prop: string,
value: string|undefined|null|boolean, isClassBased: boolean, bindingIndex: number) {
if (tNode.type !== TNodeType.Element) {
if (!(tNode.type & TNodeType.AnyRNode)) {
// It is possible to have styling on non-elements (such as ng-container).
// This is rare, but it does happen. In such a case, just ignore the binding.
return;

View File

@ -35,7 +35,7 @@ export function ɵɵtext(index: number, value: string = ''): void {
ngDevMode && assertIndexInRange(lView, adjustedIndex);
const tNode = tView.firstCreatePass ?
getOrCreateTNode(tView, index, TNodeType.Element, null, null) :
getOrCreateTNode(tView, index, TNodeType.Text, value, null) :
tView.data[adjustedIndex] as TElementNode;
const textNative = lView[adjustedIndex] = createTextNode(lView[RENDERER], value);

View File

@ -15,30 +15,47 @@ import {LView, TView} from './view';
/**
* TNodeType corresponds to the {@link TNode} `type` property.
*
* NOTE: type IDs are such that we use each bit to denote a type. This is done so that we can easily
* check if the `TNode` is of more than one type.
*
* `if (tNode.type === TNodeType.Text || tNode.type === TNode.Element)`
* can be written as:
* `if (tNode.type & (TNodeType.Text | TNodeType.Element))`
*
* However any given `TNode` can only be of one type.
*/
export const enum TNodeType {
// FIXME(misko): Add `Text` type so that it would be much easier to reason/debug about `TNode`s.
/**
* The TNode contains information about a DOM element aka {@link RText}.
*/
Text = 0b1,
/**
* The TNode contains information about a DOM element aka {@link RElement}.
*/
Element = 0b10,
/**
* The TNode contains information about an {@link LContainer} for embedded views.
*/
// FIXME(misko): Verify that we still need a `Container`, at the very least update the text.
Container = 0,
/**
* The TNode contains information about an `<ng-content>` projection
*/
Projection = 1,
/**
* The TNode contains information about a DOM element aka {@link RNode}.
*/
Element = 2,
Container = 0b100,
/**
* The TNode contains information about an `<ng-container>` element {@link RNode}.
*/
ElementContainer = 3,
ElementContainer = 0b1000,
/**
* The TNode contains information about an `<ng-content>` projection
*/
Projection = 0b10000,
/**
* The TNode contains information about an ICU comment used in `i18n`.
*/
IcuContainer = 4,
Icu = 0b100000,
/**
* Special node type representing a placeholder for future `TNode` at this location.
*
@ -52,22 +69,31 @@ export const enum TNodeType {
* location. Seeing a `Placeholder` `TNode` already there tells the system that it should reuse
* existing `TNode` (rather than create a new one) and just update the missing information.
*/
Placeholder = 5,
Placeholder = 0b1000000,
// Combined Types These should never be used for `TNode.type` only as a useful way to check
// if `TNode.type` is one of several choices.
// See: https://github.com/microsoft/TypeScript/issues/35875 why we can't refer to existing enum.
AnyRNode = 0b11, // Text | Element,
AnyContainer = 0b1100, // Container | ElementContainer, // See:
}
/**
* Converts `TNodeType` into human readable text.
* Make sure this matches with `TNodeType`
*/
export const TNodeTypeAsString = [
'Container', // 0
'Projection', // 1
'Element', // 2
'ElementContainer', // 3
'IcuContainer', // 4
'Placeholder', // 5
] as const;
export function toTNodeTypeAsString(tNodeType: TNodeType): string {
let text = '';
(tNodeType & TNodeType.Text) && (text += '|Text');
(tNodeType & TNodeType.Element) && (text += '|Element');
(tNodeType & TNodeType.Container) && (text += '|Container');
(tNodeType & TNodeType.ElementContainer) && (text += '|ElementContainer');
(tNodeType & TNodeType.Projection) && (text += '|Projection');
(tNodeType & TNodeType.Icu) && (text += '|IcuContainer');
(tNodeType & TNodeType.Placeholder) && (text += '|Placeholder');
return text.length > 0 ? text.substring(1) : text;
}
/**
* Corresponds to the TNode.flags property.
@ -223,7 +249,8 @@ export const enum AttributeMarker {
Template = 4,
/**
* Signals that the following attribute is `ngProjectAs` and its value is a parsed `CssSelector`.
* Signals that the following attribute is `ngProjectAs` and its value is a parsed
* `CssSelector`.
*
* For example, given the following HTML:
*
@ -273,10 +300,10 @@ export type TAttributes = (string|AttributeMarker|CssSelector)[];
export type TConstants = (TAttributes|string)[];
/**
* Factory function that returns an array of consts. Consts can be represented as a function in case
* any additional statements are required to define consts in the list. An example is i18n where
* additional i18n calls are generated, which should be executed when consts are requested for the
* first time.
* Factory function that returns an array of consts. Consts can be represented as a function in
* case any additional statements are required to define consts in the list. An example is i18n
* where additional i18n calls are generated, which should be executed when consts are requested
* for the first time.
*/
export type TConstantsFactory = () => TConstants;
@ -332,8 +359,8 @@ export interface TNode {
* nodes. It can also insert `Hello ` and `!` text node as a child of `<div>`, but it can't
* insert `World` because the `<span>` node has not yet been created. In such a case the
* `<span>` `TNode` will have an array which will direct the `<span>` to not only insert
* itself in front of `!` but also to insert the `World` (created by `ɵɵi18nStart`) into `<span>`
* itself.
* itself in front of `!` but also to insert the `World` (created by `ɵɵi18nStart`) into
* `<span>` itself.
*
* Pseudo code:
* ```
@ -369,10 +396,11 @@ export interface TNode {
*
* If the index === -1, there is no injector on this node or any ancestor node in this view.
*
* If the index !== -1, it is the index of this node's injector OR the index of a parent injector
* in the same view. We pass the parent injector index down the node tree of a view so it's
* possible to find the parent injector without walking a potentially deep node tree. Injector
* indices are not set across view boundaries because there could be multiple component hosts.
* If the index !== -1, it is the index of this node's injector OR the index of a parent
* injector in the same view. We pass the parent injector index down the node tree of a view so
* it's possible to find the parent injector without walking a potentially deep node tree.
* Injector indices are not set across view boundaries because there could be multiple component
* hosts.
*
* If tNode.injectorIndex === tNode.parent.injectorIndex, then the index belongs to a parent
* injector.
@ -398,8 +426,8 @@ export interface TNode {
*
* Valid values are:
* - `-1` No `hostBindings` instruction has executed.
* - `directiveStart <= directiveStylingLast < directiveEnd`: Points to the `DirectiveDef` of the
* last styling instruction which executed in the `hostBindings`.
* - `directiveStart <= directiveStylingLast < directiveEnd`: Points to the `DirectiveDef` of
* the last styling instruction which executed in the `hostBindings`.
*
* This data is needed so that styling instructions know which static styling data needs to be
* collected from the `DirectiveDef.hostAttrs`. A styling instruction needs to collect all data
@ -408,13 +436,14 @@ export interface TNode {
directiveStylingLast: number;
/**
* Stores indexes of property bindings. This field is only set in the ngDevMode and holds indexes
* of property bindings so TestBed can get bound property metadata for a given node.
* Stores indexes of property bindings. This field is only set in the ngDevMode and holds
* indexes of property bindings so TestBed can get bound property metadata for a given node.
*/
propertyBindings: number[]|null;
/**
* Stores if Node isComponent, isProjected, hasContentQuery, hasClassInput and hasStyleInput etc.
* Stores if Node isComponent, isProjected, hasContentQuery, hasClassInput and hasStyleInput
* etc.
*/
flags: TNodeFlags;
@ -437,8 +466,8 @@ export interface TNode {
value: any;
/**
* Attributes associated with an element. We need to store attributes to support various use-cases
* (attribute injection, content projection with selectors, directives matching).
* Attributes associated with an element. We need to store attributes to support various
* use-cases (attribute injection, content projection with selectors, directives matching).
* Attributes are stored statically because reading them from the DOM would be way too slow for
* content projection and queries.
*
@ -528,10 +557,10 @@ export interface TNode {
next: TNode|null;
/**
* The next projected sibling. Since in Angular content projection works on the node-by-node basis
* the act of projecting nodes might change nodes relationship at the insertion point (target
* view). At the same time we need to keep initial relationship between nodes as expressed in
* content view.
* The next projected sibling. Since in Angular content projection works on the node-by-node
* basis the act of projecting nodes might change nodes relationship at the insertion point
* (target view). At the same time we need to keep initial relationship between nodes as
* expressed in content view.
*/
projectionNext: TNode|null;
@ -584,8 +613,8 @@ export interface TNode {
* - `projection` size is equal to the number of projections `<ng-content>`. The size of
* `c1` will be `1` because `<child>` has only one `<ng-content>`.
* - we store `projection` with the host (`c1`, `c2`) rather than the `<ng-content>` (`cont1`)
* because the same component (`<child>`) can be used in multiple locations (`c1`, `c2`) and as
* a result have different set of nodes to project.
* because the same component (`<child>`) can be used in multiple locations (`c1`, `c2`) and
* as a result have different set of nodes to project.
* - without `projection` it would be difficult to efficiently traverse nodes to be projected.
*
* If `typeof projection == 'number'` then `TNode` is a `<ng-content>` element:
@ -619,9 +648,9 @@ export interface TNode {
* (e.g. `<div style="width:200px;">`)
* Must be stored separately from `tNode.styles` to facilitate setting directive
* inputs that shadow the `style` property. If we used `tNode.styles` as is for shadowed inputs,
* we would feed host styles back into directives as "inputs". If we used `tNode.attrs`, we would
* have to concatenate the attributes on every template pass. Instead, we process once on first
* create pass and store here.
* we would feed host styles back into directives as "inputs". If we used `tNode.attrs`, we
* would have to concatenate the attributes on every template pass. Instead, we process once on
* first create pass and store here.
*/
stylesWithoutHost: string|null;
@ -629,8 +658,8 @@ export interface TNode {
* A `KeyValueArray` version of residual `styles`.
*
* When there are styling instructions than each instruction stores the static styling
* which is of lower priority than itself. This means that there may be a higher priority styling
* than the instruction.
* which is of lower priority than itself. This means that there may be a higher priority
* styling than the instruction.
*
* Imagine:
* ```
@ -671,10 +700,10 @@ export interface TNode {
* Populated when there are one or more initial classes on an element
* (e.g. `<div class="SOME_CLASS">`)
* Must be stored separately from `tNode.classes` to facilitate setting directive
* inputs that shadow the `class` property. If we used `tNode.classes` as is for shadowed inputs,
* we would feed host classes back into directives as "inputs". If we used `tNode.attrs`, we would
* have to concatenate the attributes on every template pass. Instead, we process once on first
* create pass and store here.
* inputs that shadow the `class` property. If we used `tNode.classes` as is for shadowed
* inputs, we would feed host classes back into directives as "inputs". If we used
* `tNode.attrs`, we would have to concatenate the attributes on every template pass. Instead,
* we process once on first create pass and store here.
*/
classesWithoutHost: string|null;
@ -740,8 +769,8 @@ export interface TElementNode extends TNode {
/**
* If this is a component TNode with projection, this will be an array of projected
* TNodes or native nodes (see TNode.projection for more info). If it's a regular element node or
* a component without projection, it will be null.
* TNodes or native nodes (see TNode.projection for more info). If it's a regular element node
* or a component without projection, it will be null.
*/
projection: (TNode|RNode[])[]|null;

View File

@ -15,7 +15,7 @@ import {Sanitizer} from '../../sanitization/sanitizer';
import {LContainer} from './container';
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBindingsFunction, PipeDef, PipeDefList, ViewQueriesFunction} from './definition';
import {I18nUpdateOpCodes, TI18n, TIcu} from './i18n';
import {TConstants, TNode, TNodeTypeAsString} from './node';
import {TConstants, TNode} from './node';
import {PlayerHandler} from './player';
import {LQueries, TQueries} from './query';
import {RComment, RElement, Renderer3, RendererFactory3} from './renderer';
@ -1025,7 +1025,7 @@ export interface DebugNode {
/**
* Human readable node type.
*/
type: typeof TNodeTypeAsString[number];
type: string;
/**
* DOM native node.

View File

@ -6,44 +6,29 @@
* found in the LICENSE file at https://angular.io/license
*/
import {assertDefined, assertEqual} from '../util/assert';
import {assertDefined, throwError} from '../util/assert';
import {TNode, TNodeType, toTNodeTypeAsString} from './interfaces/node';
import {TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeType, TNodeTypeAsString, TProjectionNode} from './interfaces/node';
export function assertNodeType(
tNode: TNode, type: TNodeType.Container): asserts tNode is TContainerNode;
export function assertNodeType(
tNode: TNode, type: TNodeType.Element): asserts tNode is TElementNode;
export function assertNodeType(
tNode: TNode, type: TNodeType.ElementContainer): asserts tNode is TElementContainerNode;
export function assertNodeType(
tNode: TNode, type: TNodeType.IcuContainer): asserts tNode is TIcuContainerNode;
export function assertNodeType(
tNode: TNode, type: TNodeType.Projection): asserts tNode is TProjectionNode;
export function assertNodeType(tNode: TNode, type: TNodeType): asserts tNode is TNode {
export function assertTNodeType(
tNode: TNode|null, expectedTypes: TNodeType, message?: string): void {
assertDefined(tNode, 'should be called with a TNode');
assertEqual(tNode.type, type, `should be a ${typeName(type)}`);
if ((tNode.type & expectedTypes) === 0) {
throwError(
message ||
`Expected [${toTNodeTypeAsString(expectedTypes)}] but got ${
toTNodeTypeAsString(tNode.type)}.`);
}
}
export function assertNodeOfPossibleTypes(
tNode: TNode|null, types: TNodeType[], message?: string): void {
assertDefined(tNode, 'should be called with a TNode');
const found = types.some(type => tNode.type === type);
assertEqual(
found, true,
message ??
`Should be one of ${types.map(typeName).join(', ')} but got ${typeName(tNode.type)}`);
}
export function assertNodeNotOfTypes(tNode: TNode, types: TNodeType[], message?: string): void {
assertDefined(tNode, 'should be called with a TNode');
const found = types.some(type => tNode.type === type);
assertEqual(
found, false,
message ??
`Should not be one of ${types.map(typeName).join(', ')} but got ${typeName(tNode.type)}`);
}
function typeName(type: TNodeType): string {
return TNodeTypeAsString[type] || '<unknown>';
export function assertPureTNodeType(type: TNodeType) {
if (!(type === TNodeType.Element || //
type === TNodeType.Text || //
type === TNodeType.Container || //
type === TNodeType.ElementContainer || //
type === TNodeType.Icu || //
type === TNodeType.Projection || //
type === TNodeType.Placeholder)) {
throwError(`Expected TNodeType to have only a single type selected, but got ${
toTNodeTypeAsString(type)}.`);
}
}

View File

@ -22,10 +22,12 @@ import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'
import {isProceduralRenderer, ProceduralRenderer3, RComment, RElement, Renderer3, RNode, RText, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
import {isLContainer, isLView} from './interfaces/type_checks';
import {CHILD_HEAD, CLEANUP, DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, DestroyHookData, FLAGS, HookData, HookFn, HOST, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView, TViewType, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {assertTNodeType} from './node_assert';
import {getLViewParent} from './util/view_traversal_utils';
import {getNativeByTNode, unwrapRNode, updateTransplantedViewCount} from './util/view_utils';
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
const enum WalkTNodeTreeAction {
@ -539,9 +541,8 @@ export function getClosestRElement(tView: TView, tNode: TNode|null, lView: LView
let parentTNode: TNode|null = tNode;
// Skip over element and ICU containers as those are represented by a comment node and
// can't be used as a render parent.
while (parentTNode != null &&
(parentTNode.type === TNodeType.ElementContainer ||
parentTNode.type === TNodeType.IcuContainer)) {
while (parentTNode !== null &&
(parentTNode.type & (TNodeType.ElementContainer | TNodeType.Icu))) {
tNode = parentTNode;
parentTNode = tNode.parent;
}
@ -553,7 +554,7 @@ export function getClosestRElement(tView: TView, tNode: TNode|null, lView: LView
// it should always be eager.
return lView[HOST];
} else {
// ngDevMode && assertTNodeType(parentTNode, TNodeType.AnyRNode | TNodeType.Container);
ngDevMode && assertTNodeType(parentTNode, TNodeType.AnyRNode | TNodeType.Container);
if (parentTNode.flags & TNodeFlags.isComponentHost) {
ngDevMode && assertTNodeForLView(parentTNode, lView);
const tData = tView.data;
@ -650,8 +651,7 @@ function getInsertInFrontOfRNode(parentTNode: TNode, currentTNode: TNode, lView:
const insertBeforeIndex =
Array.isArray(tNodeInsertBeforeIndex) ? tNodeInsertBeforeIndex[0] : tNodeInsertBeforeIndex;
if (insertBeforeIndex === null) {
if (parentTNode.type === TNodeType.ElementContainer ||
parentTNode.type === TNodeType.IcuContainer) {
if (parentTNode.type & (TNodeType.ElementContainer | TNodeType.Icu)) {
return getNativeByTNode(parentTNode, lView);
}
} else {
@ -716,7 +716,7 @@ function processI18nText(
const isProcedural = isProceduralRenderer(renderer);
let i18nParent: RElement|null = childRNode as RElement;
let anchorRNode: RNode|null = null;
if (childTNode.type !== TNodeType.Element) {
if (!(childTNode.type & TNodeType.AnyRNode)) {
anchorRNode = i18nParent;
i18nParent = parentRElement;
}
@ -738,17 +738,17 @@ function processI18nText(
*/
function getFirstNativeNode(lView: LView, tNode: TNode|null): RNode|null {
if (tNode !== null) {
ngDevMode && assertNodeOfPossibleTypes(tNode, [
TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer, TNodeType.IcuContainer,
TNodeType.Projection
]);
ngDevMode &&
assertTNodeType(
tNode,
TNodeType.AnyRNode | TNodeType.AnyContainer | TNodeType.Icu | TNodeType.Projection);
const tNodeType = tNode.type;
if (tNodeType === TNodeType.Element) {
if (tNodeType & TNodeType.AnyRNode) {
return getNativeByTNode(tNode, lView);
} else if (tNodeType === TNodeType.Container) {
} else if (tNodeType & TNodeType.Container) {
return getBeforeNodeForView(-1, lView[tNode.index]);
} else if (tNodeType === TNodeType.ElementContainer) {
} else if (tNodeType & TNodeType.ElementContainer) {
const elIcuContainerChild = tNode.child;
if (elIcuContainerChild !== null) {
return getFirstNativeNode(lView, elIcuContainerChild);
@ -760,7 +760,7 @@ function getFirstNativeNode(lView: LView, tNode: TNode|null): RNode|null {
return unwrapRNode(rNodeOrLContainer);
}
}
} else if (tNodeType === TNodeType.IcuContainer) {
} else if (tNodeType & TNodeType.Icu) {
let nextRNode = icuContainerIterate(tNode as TIcuContainerNode, lView);
let rNode: RNode|null = nextRNode();
// If the ICU container has no nodes, than we use the ICU anchor as the node.
@ -824,10 +824,10 @@ function applyNodes(
parentRElement: RElement|null, beforeNode: RNode|null, isProjection: boolean) {
while (tNode != null) {
ngDevMode && assertTNodeForLView(tNode, lView);
ngDevMode && assertNodeOfPossibleTypes(tNode, [
TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer, TNodeType.Projection,
TNodeType.IcuContainer
]);
ngDevMode &&
assertTNodeType(
tNode,
TNodeType.AnyRNode | TNodeType.AnyContainer | TNodeType.Projection | TNodeType.Icu);
const rawSlotValue = lView[tNode.index];
const tNodeType = tNode.type;
if (isProjection) {
@ -837,21 +837,21 @@ function applyNodes(
}
}
if ((tNode.flags & TNodeFlags.isDetached) !== TNodeFlags.isDetached) {
if (tNodeType === TNodeType.ElementContainer) {
if (tNodeType & TNodeType.ElementContainer) {
applyNodes(renderer, action, tNode.child, lView, parentRElement, beforeNode, false);
applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
} else if (tNodeType === TNodeType.IcuContainer) {
} else if (tNodeType & TNodeType.Icu) {
const nextRNode = icuContainerIterate(tNode as TIcuContainerNode, lView);
let rNode: RNode|null;
while (rNode = nextRNode()) {
applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode);
}
applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
} else if (tNodeType === TNodeType.Projection) {
} else if (tNodeType & TNodeType.Projection) {
applyProjectionRecursive(
renderer, action, lView, tNode as TProjectionNode, parentRElement, beforeNode);
} else {
ngDevMode && assertNodeOfPossibleTypes(tNode, [TNodeType.Element, TNodeType.Container]);
ngDevMode && assertTNodeType(tNode, TNodeType.AnyRNode | TNodeType.Container);
applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
}
}

View File

@ -27,7 +27,7 @@ import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
import {LQueries, LQuery, TQueries, TQuery, TQueryMetadata, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
import {DECLARATION_LCONTAINER, LView, PARENT, QUERIES, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
import {assertTNodeType} from './node_assert';
import {getCurrentQueryIndex, getCurrentTNode, getLView, getTView, setCurrentQueryIndex} from './state';
import {isCreationMode} from './util/view_utils';
import {createContainerRef, createElementRef, createTemplateRef} from './view_engine_compatibility';
@ -217,7 +217,7 @@ class TQuery_ implements TQuery {
// - <needs-target><ng-container><i #target></i></ng-container></needs-target>: here we need
// to go past `<ng-container>` to determine <i #target> parent node (but we shouldn't traverse
// up past the query's host node!).
while (parent !== null && parent.type === TNodeType.ElementContainer &&
while (parent !== null && (parent.type & TNodeType.ElementContainer) &&
parent.index !== declarationNodeIdx) {
parent = parent.parent;
}
@ -238,7 +238,7 @@ class TQuery_ implements TQuery {
}
} else {
if ((predicate as any) === ViewEngine_TemplateRef) {
if (tNode.type === TNodeType.Container) {
if (tNode.type & TNodeType.Container) {
this.matchTNodeWithReadOption(tView, tNode, -1);
}
} else {
@ -253,7 +253,7 @@ class TQuery_ implements TQuery {
const read = this.metadata.read;
if (read !== null) {
if (read === ViewEngine_ElementRef || read === ViewContainerRef ||
read === ViewEngine_TemplateRef && tNode.type === TNodeType.Container) {
read === ViewEngine_TemplateRef && (tNode.type & TNodeType.Container)) {
this.addMatch(tNode.index, -2);
} else {
const directiveOrProviderIdx =
@ -299,9 +299,9 @@ function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
function createResultByTNodeType(tNode: TNode, currentView: LView): any {
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
if (tNode.type & (TNodeType.AnyRNode | TNodeType.ElementContainer)) {
return createElementRef(ViewEngine_ElementRef, tNode, currentView);
} else if (tNode.type === TNodeType.Container) {
} else if (tNode.type & TNodeType.Container) {
return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, currentView);
}
return null;
@ -327,9 +327,7 @@ function createSpecialToken(lView: LView, tNode: TNode, read: any): any {
} else if (read === ViewEngine_TemplateRef) {
return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, lView);
} else if (read === ViewContainerRef) {
ngDevMode &&
assertNodeOfPossibleTypes(
tNode, [TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer]);
ngDevMode && assertTNodeType(tNode, TNodeType.AnyRNode | TNodeType.AnyContainer);
return createContainerRef(
ViewContainerRef, ViewEngine_ElementRef,
tNode as TElementNode | TContainerNode | TElementContainerNode, lView);

View File

@ -12,6 +12,7 @@ import {DirectiveDef} from './interfaces/definition';
import {TNode, TNodeType} from './interfaces/node';
import {CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState, TData, TVIEW, TView} from './interfaces/view';
import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from './namespaces';
import {assertTNodeType} from './node_assert';
import {getTNode} from './util/view_utils';

View File

@ -27,7 +27,7 @@ import {TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode,
import {isProceduralRenderer, RComment, RElement} from './interfaces/renderer';
import {isComponentHost, isLContainer, isLView, isRootView} from './interfaces/type_checks';
import {DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, LView, LViewFlags, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
import {assertTNodeType} from './node_assert';
import {addViewToContainer, appendChild, destroyLView, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode} from './node_manipulation';
import {getCurrentTNode, getLView} from './state';
import {getParentInjectorIndex, getParentInjectorView, hasParentInjector} from './util/injector_utils';
@ -123,7 +123,7 @@ export function createTemplateRef<T>(
};
}
if (hostTNode.type === TNodeType.Container) {
if (hostTNode.type & TNodeType.Container) {
ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
return new R3TemplateRef(
hostView, hostTNode as TContainerNode,
@ -357,9 +357,7 @@ export function createContainerRef(
};
}
ngDevMode &&
assertNodeOfPossibleTypes(
hostTNode, [TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer]);
ngDevMode && assertTNodeType(hostTNode, TNodeType.AnyContainer | TNodeType.AnyRNode);
let lContainer: LContainer;
const slotValue = hostView[hostTNode.index];
@ -372,7 +370,7 @@ export function createContainerRef(
// comment and we can reuse that comment as anchor element for the new LContainer.
// The comment node in question is already part of the DOM structure so we don't need to append
// it again.
if (hostTNode.type === TNodeType.ElementContainer) {
if (hostTNode.type & TNodeType.ElementContainer) {
commentNode = unwrapRNode(slotValue) as RComment;
} else {
ngDevMode && ngDevMode.rendererCreateComment++;
@ -431,9 +429,7 @@ function createViewRef(tNode: TNode, lView: LView, isPipe: boolean): ViewEngine_
// Instead we want the LView for the component View and so we need to look it up.
const componentView = getComponentLViewByIndex(tNode.index, lView); // look down
return new ViewRef(componentView, componentView);
} else if (
tNode.type === TNodeType.Element || tNode.type === TNodeType.Container ||
tNode.type === TNodeType.ElementContainer || tNode.type === TNodeType.IcuContainer) {
} else if (tNode.type & (TNodeType.AnyRNode | TNodeType.AnyContainer | TNodeType.Icu)) {
// The LView represents the location where the injection is requested from.
// We need to locate the containing LView (in case where the `lView` is an embedded view)
const hostComponentView = lView[DECLARATION_COMPONENT_VIEW]; // look up

View File

@ -19,7 +19,7 @@ import {TElementNode, TIcuContainerNode, TNode, TNodeType} from './interfaces/no
import {RNode} from './interfaces/renderer';
import {isLContainer} from './interfaces/type_checks';
import {CONTEXT, DECLARATION_COMPONENT_VIEW, FLAGS, LView, LViewFlags, T_HOST, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
import {assertTNodeType} from './node_assert';
import {destroyLView, renderDetachView} from './node_manipulation';
import {getLViewParent} from './util/view_traversal_utils';
import {unwrapRNode} from './util/view_utils';
@ -324,10 +324,10 @@ function collectNativeNodes(
tView: TView, lView: LView, tNode: TNode|null, result: any[],
isProjection: boolean = false): any[] {
while (tNode !== null) {
ngDevMode && assertNodeOfPossibleTypes(tNode, [
TNodeType.Element, TNodeType.Container, TNodeType.Projection, TNodeType.ElementContainer,
TNodeType.IcuContainer
]);
ngDevMode &&
assertTNodeType(
tNode,
TNodeType.AnyRNode | TNodeType.AnyContainer | TNodeType.Projection | TNodeType.Icu);
const lNode = lView[tNode.index];
if (lNode !== null) {
@ -349,15 +349,15 @@ function collectNativeNodes(
}
const tNodeType = tNode.type;
if (tNodeType === TNodeType.ElementContainer) {
if (tNodeType & TNodeType.ElementContainer) {
collectNativeNodes(tView, lView, tNode.child, result);
} else if (tNodeType === TNodeType.IcuContainer) {
} else if (tNodeType & TNodeType.Icu) {
const nextRNode = icuContainerIterate(tNode as TIcuContainerNode, lView);
let rNode: RNode|null;
while (rNode = nextRNode()) {
result.push(rNode);
}
} else if (tNodeType === TNodeType.Projection) {
} else if (tNodeType & TNodeType.Projection) {
const componentView = lView[DECLARATION_COMPONENT_VIEW];
const componentHost = componentView[T_HOST] as TElementNode;
const slotIdx = tNode.projection as number;

View File

@ -70,7 +70,7 @@ onlyInIvy('Ivy specific').describe('Debug Representation', () => {
length: 1,
content: [{
index: HEADER_OFFSET + 3,
t: matchTNode({type: TNodeType.Element, value: null}),
t: matchTNode({type: TNodeType.Text, value: '{{?}}'}),
l: matchDomText('Hello World')
}]
});

View File

@ -618,7 +618,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
const exclamation = b.nextSibling!;
const lViewDebug = lView.debug!;
expect(lViewDebug.nodes.map(toTypeContent)).toEqual([
'Element(Hello )', 'Element(<b>)', 'Element(!)'
'Text(Hello )', 'Element(<b>)', 'Text(!)'
]);
expect(lViewDebug.decls).toEqual({
start: HEADER_OFFSET,

View File

@ -668,7 +668,7 @@ describe('Runtime i18n', () => {
i18nRangeOffsetOpcode(0), 'Hello World!', //
]);
const lViewDebug = fixture.lView.debug!;
expect(lViewDebug.template).toEqual('<div>#text</div>');
expect(lViewDebug.template).toEqual('<div>Hello World!</div>');
});
it('should process text with a child node', () => {
@ -685,7 +685,7 @@ describe('Runtime i18n', () => {
insertBeforeIndex: i18nRangeOffset(1),
}));
const lViewDebug = fixture.lView.debug!;
expect(lViewDebug.template).toEqual('<div>#text<Placeholder></Placeholder>#text</div>');
expect(lViewDebug.template).toEqual('<div>Hello <Placeholder></Placeholder>!</div>');
});
it('should process text with a child node that has text', () => {
@ -729,7 +729,7 @@ describe('Runtime i18n', () => {
'if (mask & 0b10) { (lView[51] as Text).textContent = `${lView[i-2]}`; }'
]));
const lViewDebug = fixture.lView.debug!;
expect(lViewDebug.template).toEqual('<div>#text<Placeholder>#text</Placeholder>#text</div>');
expect(lViewDebug.template).toEqual('<div>{{?}}<Placeholder>{{?}}</Placeholder>!</div>');
});
it('should process text with a child template', () => {

View File

@ -6,16 +6,23 @@
* found in the LICENSE file at https://angular.io/license
*/
import {TNodeType, TNodeTypeAsString} from '@angular/core/src/render3/interfaces/node';
import {TNodeType, toTNodeTypeAsString} from '@angular/core/src/render3/interfaces/node';
describe('node interfaces', () => {
describe('TNodeType', () => {
it('should agree with TNodeTypeAsString', () => {
expect(TNodeTypeAsString[TNodeType.Container]).toEqual('Container');
expect(TNodeTypeAsString[TNodeType.Projection]).toEqual('Projection');
expect(TNodeTypeAsString[TNodeType.Element]).toEqual('Element');
expect(TNodeTypeAsString[TNodeType.ElementContainer]).toEqual('ElementContainer');
expect(TNodeTypeAsString[TNodeType.IcuContainer]).toEqual('IcuContainer');
it('should agree with toTNodeTypeAsString', () => {
expect(toTNodeTypeAsString(TNodeType.Element)).toEqual('Element');
expect(toTNodeTypeAsString(TNodeType.Text)).toEqual('Text');
expect(toTNodeTypeAsString(TNodeType.Container)).toEqual('Container');
expect(toTNodeTypeAsString(TNodeType.Projection)).toEqual('Projection');
expect(toTNodeTypeAsString(TNodeType.ElementContainer)).toEqual('ElementContainer');
expect(toTNodeTypeAsString(TNodeType.Icu)).toEqual('IcuContainer');
expect(toTNodeTypeAsString(TNodeType.Placeholder)).toEqual('Placeholder');
expect(toTNodeTypeAsString(
TNodeType.Container | TNodeType.Projection | TNodeType.Element |
TNodeType.ElementContainer | TNodeType.Icu))
.toEqual('Element|Container|ElementContainer|Projection|IcuContainer');
});
});
});