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:
parent
d50df92568
commit
70f1e2e04a
|
@ -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];
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.`);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)}.`);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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')
|
||||
}]
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue