refactor(ivy): delete ɵɵelementHostAttrs
instruction (#34717)
PR Close #34717
This commit is contained in:
parent
2227d471a4
commit
da7e362bce
@ -17,13 +17,15 @@ import {assertComponentType} from './assert';
|
|||||||
import {getComponentDef} from './definition';
|
import {getComponentDef} from './definition';
|
||||||
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
|
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
|
||||||
import {registerPostOrderHooks} from './hooks';
|
import {registerPostOrderHooks} from './hooks';
|
||||||
import {CLEAN_PROMISE, addHostBindingsToExpandoInstructions, addToViewTree, createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, growHostVarsSpace, initNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshView, renderView} from './instructions/shared';
|
import {CLEAN_PROMISE, addHostBindingsToExpandoInstructions, addToViewTree, createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, growHostVarsSpace, initTNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshView, renderInitialStyling, renderView} from './instructions/shared';
|
||||||
|
import {registerInitialStylingOnTNode} from './instructions/styling';
|
||||||
import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition';
|
import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition';
|
||||||
import {TElementNode, TNode, TNodeType} from './interfaces/node';
|
import {TElementNode, TNode, TNodeType} from './interfaces/node';
|
||||||
import {PlayerHandler} from './interfaces/player';
|
import {PlayerHandler} from './interfaces/player';
|
||||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||||
import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW, TViewType} from './interfaces/view';
|
import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW, TViewType} from './interfaces/view';
|
||||||
import {enterView, getPreviousOrParentTNode, incrementActiveDirectiveId, leaveView, setActiveHostElement} from './state';
|
import {enterView, getPreviousOrParentTNode, incrementActiveDirectiveId, leaveView, setActiveHostElement} from './state';
|
||||||
|
import {setUpAttributes} from './util/attrs_utils';
|
||||||
import {publishDefaultGlobalUtils} from './util/global_utils';
|
import {publishDefaultGlobalUtils} from './util/global_utils';
|
||||||
import {defaultScheduler, stringifyForError} from './util/misc_utils';
|
import {defaultScheduler, stringifyForError} from './util/misc_utils';
|
||||||
import {getRootContext} from './util/view_traversal_utils';
|
import {getRootContext} from './util/view_traversal_utils';
|
||||||
@ -171,6 +173,14 @@ export function createRootComponentView(
|
|||||||
ngDevMode && assertDataInRange(rootView, 0 + HEADER_OFFSET);
|
ngDevMode && assertDataInRange(rootView, 0 + HEADER_OFFSET);
|
||||||
rootView[0 + HEADER_OFFSET] = rNode;
|
rootView[0 + HEADER_OFFSET] = rNode;
|
||||||
const tNode: TElementNode = getOrCreateTNode(tView, null, 0, TNodeType.Element, null, null);
|
const tNode: TElementNode = getOrCreateTNode(tView, null, 0, TNodeType.Element, null, null);
|
||||||
|
const mergedAttrs = tNode.mergedAttrs = def.hostAttrs;
|
||||||
|
if (mergedAttrs !== null) {
|
||||||
|
registerInitialStylingOnTNode(tNode, mergedAttrs, 0);
|
||||||
|
if (rNode !== null) {
|
||||||
|
setUpAttributes(renderer, rNode, mergedAttrs);
|
||||||
|
renderInitialStyling(renderer, rNode, tNode, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
const componentView = createLView(
|
const componentView = createLView(
|
||||||
rootView, getOrCreateTComponentView(def), null,
|
rootView, getOrCreateTComponentView(def), null,
|
||||||
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rootView[HEADER_OFFSET], tNode,
|
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rootView[HEADER_OFFSET], tNode,
|
||||||
@ -179,7 +189,7 @@ export function createRootComponentView(
|
|||||||
if (tView.firstCreatePass) {
|
if (tView.firstCreatePass) {
|
||||||
diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type);
|
diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type);
|
||||||
markAsComponentHost(tView, tNode);
|
markAsComponentHost(tView, tNode);
|
||||||
initNodeFlags(tNode, rootView.length, 1);
|
initTNodeFlags(tNode, rootView.length, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
addToViewTree(rootView, componentView);
|
addToViewTree(rootView, componentView);
|
||||||
@ -216,7 +226,8 @@ export function createRootComponent<T>(
|
|||||||
// part of the `hostAttrs`.
|
// part of the `hostAttrs`.
|
||||||
// The check for componentDef.hostBindings is wrong since now some directives may not
|
// The check for componentDef.hostBindings is wrong since now some directives may not
|
||||||
// have componentDef.hostBindings but they still need to process hostVars and hostAttrs
|
// have componentDef.hostBindings but they still need to process hostVars and hostAttrs
|
||||||
if (tView.firstCreatePass && (componentDef.hostBindings || componentDef.hostAttrs !== null)) {
|
if (tView.firstCreatePass &&
|
||||||
|
(componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) {
|
||||||
const elementIndex = rootTNode.index - HEADER_OFFSET;
|
const elementIndex = rootTNode.index - HEADER_OFFSET;
|
||||||
setActiveHostElement(elementIndex);
|
setActiveHostElement(elementIndex);
|
||||||
incrementActiveDirectiveId();
|
incrementActiveDirectiveId();
|
||||||
@ -225,9 +236,7 @@ export function createRootComponent<T>(
|
|||||||
addHostBindingsToExpandoInstructions(rootTView, componentDef);
|
addHostBindingsToExpandoInstructions(rootTView, componentDef);
|
||||||
growHostVarsSpace(rootTView, rootLView, componentDef.hostVars);
|
growHostVarsSpace(rootTView, rootLView, componentDef.hostVars);
|
||||||
|
|
||||||
const expando = tView.expandoInstructions !;
|
invokeHostBindingsInCreationMode(componentDef, component, rootTNode);
|
||||||
invokeHostBindingsInCreationMode(
|
|
||||||
componentDef, expando, component, rootTNode, tView.firstCreatePass);
|
|
||||||
|
|
||||||
setActiveHostElement(null);
|
setActiveHostElement(null);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {assertDataInRange, assertDefined, assertEqual} from '../../util/assert';
|
import {assertDataInRange, assertDefined, assertEqual} from '../../util/assert';
|
||||||
import {assertHasParent} from '../assert';
|
import {assertFirstCreatePass, assertHasParent} from '../assert';
|
||||||
import {attachPatchData} from '../context_discovery';
|
import {attachPatchData} from '../context_discovery';
|
||||||
import {registerPostOrderHooks} from '../hooks';
|
import {registerPostOrderHooks} from '../hooks';
|
||||||
import {TAttributes, TElementNode, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
|
import {TAttributes, TElementNode, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
|
||||||
@ -28,20 +28,21 @@ import {registerInitialStylingOnTNode} from './styling';
|
|||||||
function elementStartFirstCreatePass(
|
function elementStartFirstCreatePass(
|
||||||
index: number, tView: TView, lView: LView, native: RElement, name: string,
|
index: number, tView: TView, lView: LView, native: RElement, name: string,
|
||||||
attrsIndex?: number | null, localRefsIndex?: number): TElementNode {
|
attrsIndex?: number | null, localRefsIndex?: number): TElementNode {
|
||||||
|
ngDevMode && assertFirstCreatePass(tView);
|
||||||
ngDevMode && ngDevMode.firstCreatePass++;
|
ngDevMode && ngDevMode.firstCreatePass++;
|
||||||
|
|
||||||
const tViewConsts = tView.consts;
|
const tViewConsts = tView.consts;
|
||||||
const attrs = getConstant<TAttributes>(tViewConsts, attrsIndex);
|
const attrs = getConstant<TAttributes>(tViewConsts, attrsIndex);
|
||||||
const tNode = getOrCreateTNode(tView, lView[T_HOST], index, TNodeType.Element, name, attrs);
|
const tNode = getOrCreateTNode(tView, lView[T_HOST], index, TNodeType.Element, name, attrs);
|
||||||
|
|
||||||
if (attrs !== null) {
|
|
||||||
registerInitialStylingOnTNode(tNode, attrs, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasDirectives =
|
const hasDirectives =
|
||||||
resolveDirectives(tView, lView, tNode, getConstant<string[]>(tViewConsts, localRefsIndex));
|
resolveDirectives(tView, lView, tNode, getConstant<string[]>(tViewConsts, localRefsIndex));
|
||||||
ngDevMode && warnAboutUnknownElement(lView, native, tNode, hasDirectives);
|
ngDevMode && warnAboutUnknownElement(lView, native, tNode, hasDirectives);
|
||||||
|
|
||||||
|
if (tNode.mergedAttrs !== null) {
|
||||||
|
registerInitialStylingOnTNode(tNode, tNode.mergedAttrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (tView.queries !== null) {
|
if (tView.queries !== null) {
|
||||||
tView.queries.elementStart(tView, tNode);
|
tView.queries.elementStart(tView, tNode);
|
||||||
}
|
}
|
||||||
@ -83,9 +84,9 @@ export function ɵɵelementStart(
|
|||||||
tView.data[adjustedIndex] as TElementNode;
|
tView.data[adjustedIndex] as TElementNode;
|
||||||
setPreviousOrParentTNode(tNode, true);
|
setPreviousOrParentTNode(tNode, true);
|
||||||
|
|
||||||
const attrs = tNode.attrs;
|
const mergedAttrs = tNode.mergedAttrs;
|
||||||
if (attrs != null) {
|
if (mergedAttrs !== null) {
|
||||||
setUpAttributes(renderer, native, attrs);
|
setUpAttributes(renderer, native, mergedAttrs);
|
||||||
}
|
}
|
||||||
if ((tNode.flags & TNodeFlags.hasInitialStyling) === TNodeFlags.hasInitialStyling) {
|
if ((tNode.flags & TNodeFlags.hasInitialStyling) === TNodeFlags.hasInitialStyling) {
|
||||||
renderInitialStyling(renderer, native, tNode, false);
|
renderInitialStyling(renderer, native, tNode, false);
|
||||||
@ -106,7 +107,7 @@ export function ɵɵelementStart(
|
|||||||
createDirectivesInstances(tView, lView, tNode);
|
createDirectivesInstances(tView, lView, tNode);
|
||||||
executeContentQueries(tView, tNode, lView);
|
executeContentQueries(tView, tNode, lView);
|
||||||
}
|
}
|
||||||
if (localRefsIndex != null) {
|
if (localRefsIndex !== null) {
|
||||||
saveResolvedLocalsInData(lView, tNode);
|
saveResolvedLocalsInData(lView, tNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,78 +170,6 @@ export function ɵɵelement(
|
|||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign static attribute values to a host element.
|
|
||||||
*
|
|
||||||
* This instruction will assign static attribute values as well as class and style
|
|
||||||
* values to an element within the host bindings function. Since attribute values
|
|
||||||
* can consist of different types of values, the `attrs` array must include the values in
|
|
||||||
* the following format:
|
|
||||||
*
|
|
||||||
* attrs = [
|
|
||||||
* // static attributes (like `title`, `name`, `id`...)
|
|
||||||
* attr1, value1, attr2, value,
|
|
||||||
*
|
|
||||||
* // a single namespace value (like `x:id`)
|
|
||||||
* NAMESPACE_MARKER, namespaceUri1, name1, value1,
|
|
||||||
*
|
|
||||||
* // another single namespace value (like `x:name`)
|
|
||||||
* NAMESPACE_MARKER, namespaceUri2, name2, value2,
|
|
||||||
*
|
|
||||||
* // a series of CSS classes that will be applied to the element (no spaces)
|
|
||||||
* CLASSES_MARKER, class1, class2, class3,
|
|
||||||
*
|
|
||||||
* // a series of CSS styles (property + value) that will be applied to the element
|
|
||||||
* STYLES_MARKER, prop1, value1, prop2, value2
|
|
||||||
* ]
|
|
||||||
*
|
|
||||||
* All non-class and non-style attributes must be defined at the start of the list
|
|
||||||
* first before all class and style values are set. When there is a change in value
|
|
||||||
* type (like when classes and styles are introduced) a marker must be used to separate
|
|
||||||
* the entries. The marker values themselves are set via entries found in the
|
|
||||||
* [AttributeMarker] enum.
|
|
||||||
*
|
|
||||||
* NOTE: This instruction is meant to used from `hostBindings` function only.
|
|
||||||
*
|
|
||||||
* @param directive A directive instance the styling is associated with.
|
|
||||||
* @param attrs An array of static values (attributes, classes and styles) with the correct marker
|
|
||||||
* values.
|
|
||||||
*
|
|
||||||
* @codeGenApi
|
|
||||||
*/
|
|
||||||
export function ɵɵelementHostAttrs(attrs: TAttributes) {
|
|
||||||
const hostElementIndex = getSelectedIndex();
|
|
||||||
const lView = getLView();
|
|
||||||
const tView = lView[TVIEW];
|
|
||||||
const tNode = getTNode(hostElementIndex, lView);
|
|
||||||
|
|
||||||
// non-element nodes (e.g. `<ng-container>`) are not rendered as actual
|
|
||||||
// element nodes and adding styles/classes on to them will cause runtime
|
|
||||||
// errors...
|
|
||||||
if (tNode.type === TNodeType.Element) {
|
|
||||||
const native = getNativeByTNode(tNode, lView) as RElement;
|
|
||||||
// TODO(misko-next): setup attributes need to be moved out of `ɵɵelementHostAttrs`
|
|
||||||
const lastAttrIndex = setUpAttributes(lView[RENDERER], native, attrs);
|
|
||||||
if (tView.firstCreatePass) {
|
|
||||||
const stylingNeedsToBeRendered = registerInitialStylingOnTNode(tNode, attrs, lastAttrIndex);
|
|
||||||
|
|
||||||
// this is only called during the first template pass in the
|
|
||||||
// event that this current directive assigned initial style/class
|
|
||||||
// host attribute values to the element. Because initial styling
|
|
||||||
// values are applied before directives are first rendered (within
|
|
||||||
// `createElement`) this means that initial styling for any directives
|
|
||||||
// still needs to be applied. Note that this will only happen during
|
|
||||||
// the first template pass and not each time a directive applies its
|
|
||||||
// attribute values to the element.
|
|
||||||
if (stylingNeedsToBeRendered) {
|
|
||||||
const renderer = lView[RENDERER];
|
|
||||||
// TODO(misko-next): Styling initialization should move out of `ɵɵelementHostAttrs`
|
|
||||||
renderInitialStyling(renderer, native, tNode, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setDirectiveStylingInput(
|
function setDirectiveStylingInput(
|
||||||
context: TStylingContext | StylingMapArray | null, lView: LView,
|
context: TStylingContext | StylingMapArray | null, lView: LView,
|
||||||
stylingInputs: (string | number)[], propName: string) {
|
stylingInputs: (string | number)[], propName: string) {
|
||||||
|
@ -154,31 +154,32 @@ export const TViewConstructor = class TView implements ITView {
|
|||||||
|
|
||||||
export const TNodeConstructor = class TNode implements ITNode {
|
export const TNodeConstructor = class TNode implements ITNode {
|
||||||
constructor(
|
constructor(
|
||||||
public tView_: TView, //
|
public tView_: TView, //
|
||||||
public type: TNodeType, //
|
public type: TNodeType, //
|
||||||
public index: number, //
|
public index: number, //
|
||||||
public injectorIndex: number, //
|
public injectorIndex: number, //
|
||||||
public directiveStart: number, //
|
public directiveStart: number, //
|
||||||
public directiveEnd: number, //
|
public directiveEnd: number, //
|
||||||
public propertyBindings: number[]|null, //
|
public propertyBindings: number[]|null, //
|
||||||
public flags: TNodeFlags, //
|
public flags: TNodeFlags, //
|
||||||
public providerIndexes: TNodeProviderIndexes, //
|
public providerIndexes: TNodeProviderIndexes, //
|
||||||
public tagName: string|null, //
|
public tagName: string|null, //
|
||||||
public attrs: (string|AttributeMarker|(string|SelectorFlags)[])[]|null, //
|
public attrs: (string|AttributeMarker|(string|SelectorFlags)[])[]|null, //
|
||||||
public localNames: (string|number)[]|null, //
|
public mergedAttrs: (string|AttributeMarker|(string|SelectorFlags)[])[]|null, //
|
||||||
public initialInputs: (string[]|null)[]|null|undefined, //
|
public localNames: (string|number)[]|null, //
|
||||||
public inputs: PropertyAliases|null, //
|
public initialInputs: (string[]|null)[]|null|undefined, //
|
||||||
public outputs: PropertyAliases|null, //
|
public inputs: PropertyAliases|null, //
|
||||||
public tViews: ITView|ITView[]|null, //
|
public outputs: PropertyAliases|null, //
|
||||||
public next: ITNode|null, //
|
public tViews: ITView|ITView[]|null, //
|
||||||
public projectionNext: ITNode|null, //
|
public next: ITNode|null, //
|
||||||
public child: ITNode|null, //
|
public projectionNext: ITNode|null, //
|
||||||
public parent: TElementNode|TContainerNode|null, //
|
public child: ITNode|null, //
|
||||||
public projection: number|(ITNode|RNode[])[]|null, //
|
public parent: TElementNode|TContainerNode|null, //
|
||||||
public styles: TStylingContext|null, //
|
public projection: number|(ITNode|RNode[])[]|null, //
|
||||||
public classes: TStylingContext|null, //
|
public styles: TStylingContext|null, //
|
||||||
public classBindings: TStylingRange, //
|
public classes: TStylingContext|null, //
|
||||||
public styleBindings: TStylingRange, //
|
public classBindings: TStylingRange, //
|
||||||
|
public styleBindings: TStylingRange, //
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get type_(): string {
|
get type_(): string {
|
||||||
|
@ -20,7 +20,7 @@ import {attachPatchData} from '../context_discovery';
|
|||||||
import {getFactoryDef} from '../definition';
|
import {getFactoryDef} from '../definition';
|
||||||
import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from '../di';
|
import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from '../di';
|
||||||
import {throwMultipleComponentError} from '../errors';
|
import {throwMultipleComponentError} from '../errors';
|
||||||
import {executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags, registerPreOrderHooks} from '../hooks';
|
import {executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags} from '../hooks';
|
||||||
import {ACTIVE_INDEX, ActiveIndexFlag, CONTAINER_HEADER_OFFSET, LContainer, MOVED_VIEWS} from '../interfaces/container';
|
import {ACTIVE_INDEX, ActiveIndexFlag, CONTAINER_HEADER_OFFSET, LContainer, MOVED_VIEWS} from '../interfaces/container';
|
||||||
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
|
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
|
||||||
import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector';
|
import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector';
|
||||||
@ -28,20 +28,18 @@ import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, Pro
|
|||||||
import {RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer';
|
import {RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer';
|
||||||
import {SanitizerFn} from '../interfaces/sanitization';
|
import {SanitizerFn} from '../interfaces/sanitization';
|
||||||
import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks';
|
import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks';
|
||||||
import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, TViewType, T_HOST} from '../interfaces/view';
|
import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, TViewType, T_HOST} from '../interfaces/view';
|
||||||
import {assertNodeOfPossibleTypes} from '../node_assert';
|
import {assertNodeOfPossibleTypes} from '../node_assert';
|
||||||
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
|
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
|
||||||
import {ActiveElementFlags, enterView, executeElementExitFn, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, hasActiveElementFlag, incrementActiveDirectiveId, leaveView, leaveViewProcessExit, setActiveHostElement, setBindingIndex, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state';
|
import {ActiveElementFlags, enterView, executeElementExitFn, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, hasActiveElementFlag, incrementActiveDirectiveId, leaveView, leaveViewProcessExit, setActiveHostElement, setBindingIndex, setBindingRoot, setCheckNoChangesMode, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state';
|
||||||
import {renderStylingMap, writeStylingValueDirectly} from '../styling/bindings';
|
import {renderStylingMap, writeStylingValueDirectly} from '../styling/bindings';
|
||||||
import {NO_CHANGE} from '../tokens';
|
import {NO_CHANGE} from '../tokens';
|
||||||
import {isAnimationProp} from '../util/attrs_utils';
|
import {isAnimationProp, mergeHostAttrs} from '../util/attrs_utils';
|
||||||
import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils';
|
import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils';
|
||||||
import {getInitialStylingValue} from '../util/styling_utils';
|
import {getInitialStylingValue} from '../util/styling_utils';
|
||||||
import {getLViewParent} from '../util/view_traversal_utils';
|
import {getLViewParent} from '../util/view_traversal_utils';
|
||||||
import {getComponentLViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isCreationMode, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils';
|
import {getComponentLViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isCreationMode, readPatchedLView, resetPreOrderHookFlags, viewAttachedToChangeDetector} from '../util/view_utils';
|
||||||
|
|
||||||
import {selectIndexInternal} from './advance';
|
import {selectIndexInternal} from './advance';
|
||||||
import {ɵɵelementHostAttrs} from './element';
|
|
||||||
import {LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeConstructor, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor, attachLContainerDebug, attachLViewDebug, cloneToLViewFromTViewBlueprint, cloneToTViewData} from './lview_debug';
|
import {LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeConstructor, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor, attachLContainerDebug, attachLViewDebug, cloneToLViewFromTViewBlueprint, cloneToTViewData} from './lview_debug';
|
||||||
|
|
||||||
|
|
||||||
@ -828,6 +826,7 @@ export function createTNode(
|
|||||||
0, // providerIndexes: TNodeProviderIndexes
|
0, // providerIndexes: TNodeProviderIndexes
|
||||||
tagName, // tagName: string|null
|
tagName, // tagName: string|null
|
||||||
attrs, // attrs: (string|AttributeMarker|(string|SelectorFlags)[])[]|null
|
attrs, // attrs: (string|AttributeMarker|(string|SelectorFlags)[])[]|null
|
||||||
|
null, // mergedAttrs
|
||||||
null, // localNames: (string|number)[]|null
|
null, // localNames: (string|number)[]|null
|
||||||
undefined, // initialInputs: (string[]|null)[]|null|undefined
|
undefined, // initialInputs: (string[]|null)[]|null|undefined
|
||||||
null, // inputs: PropertyAliases|null
|
null, // inputs: PropertyAliases|null
|
||||||
@ -854,6 +853,7 @@ export function createTNode(
|
|||||||
providerIndexes: 0,
|
providerIndexes: 0,
|
||||||
tagName: tagName,
|
tagName: tagName,
|
||||||
attrs: attrs,
|
attrs: attrs,
|
||||||
|
mergedAttrs: null,
|
||||||
localNames: null,
|
localNames: null,
|
||||||
initialInputs: undefined,
|
initialInputs: undefined,
|
||||||
inputs: null,
|
inputs: null,
|
||||||
@ -1115,64 +1115,69 @@ export function resolveDirectives(
|
|||||||
// tsickle.
|
// tsickle.
|
||||||
ngDevMode && assertFirstCreatePass(tView);
|
ngDevMode && assertFirstCreatePass(tView);
|
||||||
|
|
||||||
if (!getBindingsEnabled()) return false;
|
|
||||||
|
|
||||||
const directives: DirectiveDef<any>[]|null = findDirectiveMatches(tView, lView, tNode);
|
|
||||||
const exportsMap: ({[key: string]: number} | null) = localRefs === null ? null : {'': -1};
|
|
||||||
let hasDirectives = false;
|
let hasDirectives = false;
|
||||||
|
if (getBindingsEnabled()) {
|
||||||
|
const directiveDefs: DirectiveDef<any>[]|null = findDirectiveDefMatches(tView, lView, tNode);
|
||||||
|
const exportsMap: ({[key: string]: number} | null) = localRefs === null ? null : {'': -1};
|
||||||
|
|
||||||
if (directives !== null) {
|
if (directiveDefs !== null) {
|
||||||
let totalDirectiveHostVars = 0;
|
let totalDirectiveHostVars = 0;
|
||||||
hasDirectives = true;
|
hasDirectives = true;
|
||||||
initNodeFlags(tNode, tView.data.length, directives.length);
|
initTNodeFlags(tNode, tView.data.length, directiveDefs.length);
|
||||||
// When the same token is provided by several directives on the same node, some rules apply in
|
// When the same token is provided by several directives on the same node, some rules apply in
|
||||||
// the viewEngine:
|
// the viewEngine:
|
||||||
// - viewProviders have priority over providers
|
// - viewProviders have priority over providers
|
||||||
// - the last directive in NgModule.declarations has priority over the previous one
|
// - the last directive in NgModule.declarations has priority over the previous one
|
||||||
// So to match these rules, the order in which providers are added in the arrays is very
|
// So to match these rules, the order in which providers are added in the arrays is very
|
||||||
// important.
|
// important.
|
||||||
for (let i = 0; i < directives.length; i++) {
|
for (let i = 0; i < directiveDefs.length; i++) {
|
||||||
const def = directives[i];
|
const def = directiveDefs[i];
|
||||||
if (def.providersResolver) def.providersResolver(def);
|
if (def.providersResolver) def.providersResolver(def);
|
||||||
}
|
}
|
||||||
generateExpandoInstructionBlock(tView, tNode, directives.length);
|
generateExpandoInstructionBlock(tView, tNode, directiveDefs.length);
|
||||||
let preOrderHooksFound = false;
|
let preOrderHooksFound = false;
|
||||||
let preOrderCheckHooksFound = false;
|
let preOrderCheckHooksFound = false;
|
||||||
for (let i = 0; i < directives.length; i++) {
|
for (let i = 0; i < directiveDefs.length; i++) {
|
||||||
const def = directives[i];
|
const def = directiveDefs[i];
|
||||||
|
// Merge the attrs in the order of matches. This assumes that the first directive is the
|
||||||
|
// component itself, so that the component has the least priority.
|
||||||
|
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
|
||||||
|
|
||||||
baseResolveDirective(tView, lView, def);
|
baseResolveDirective(tView, lView, def);
|
||||||
|
|
||||||
saveNameToExportMap(tView.data !.length - 1, def, exportsMap);
|
saveNameToExportMap(tView.data !.length - 1, def, exportsMap);
|
||||||
|
|
||||||
if (def.contentQueries !== null) tNode.flags |= TNodeFlags.hasContentQuery;
|
if (def.contentQueries !== null) tNode.flags |= TNodeFlags.hasContentQuery;
|
||||||
if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
|
if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
|
||||||
tNode.flags |= TNodeFlags.hasHostBindings;
|
tNode.flags |= TNodeFlags.hasHostBindings;
|
||||||
|
|
||||||
// Only push a node index into the preOrderHooks array if this is the first
|
// Only push a node index into the preOrderHooks array if this is the first
|
||||||
// pre-order hook found on this node.
|
// pre-order hook found on this node.
|
||||||
if (!preOrderHooksFound && (def.onChanges || def.onInit || def.doCheck)) {
|
if (!preOrderHooksFound && (def.onChanges || def.onInit || def.doCheck)) {
|
||||||
// We will push the actual hook function into this array later during dir instantiation.
|
// We will push the actual hook function into this array later during dir instantiation.
|
||||||
// We cannot do it now because we must ensure hooks are registered in the same
|
// We cannot do it now because we must ensure hooks are registered in the same
|
||||||
// order that directives are created (i.e. injection order).
|
// order that directives are created (i.e. injection order).
|
||||||
(tView.preOrderHooks || (tView.preOrderHooks = [])).push(tNode.index - HEADER_OFFSET);
|
(tView.preOrderHooks || (tView.preOrderHooks = [])).push(tNode.index - HEADER_OFFSET);
|
||||||
preOrderHooksFound = true;
|
preOrderHooksFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preOrderCheckHooksFound && (def.onChanges || def.doCheck)) {
|
||||||
|
(tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [
|
||||||
|
])).push(tNode.index - HEADER_OFFSET);
|
||||||
|
preOrderCheckHooksFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
addHostBindingsToExpandoInstructions(tView, def);
|
||||||
|
totalDirectiveHostVars += def.hostVars;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preOrderCheckHooksFound && (def.onChanges || def.doCheck)) {
|
initializeInputAndOutputAliases(tView, tNode);
|
||||||
(tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [
|
growHostVarsSpace(tView, lView, totalDirectiveHostVars);
|
||||||
])).push(tNode.index - HEADER_OFFSET);
|
|
||||||
preOrderCheckHooksFound = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
addHostBindingsToExpandoInstructions(tView, def);
|
|
||||||
totalDirectiveHostVars += def.hostVars;
|
|
||||||
}
|
}
|
||||||
|
if (exportsMap) cacheMatchingLocalNames(tNode, localRefs, exportsMap);
|
||||||
initializeInputAndOutputAliases(tView, tNode);
|
|
||||||
growHostVarsSpace(tView, lView, totalDirectiveHostVars);
|
|
||||||
}
|
}
|
||||||
if (exportsMap) cacheMatchingLocalNames(tNode, localRefs, exportsMap);
|
// Merge the template attrs last so that they have the highest priority.
|
||||||
|
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
|
||||||
return hasDirectives;
|
return hasDirectives;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1187,9 +1192,7 @@ export function addHostBindingsToExpandoInstructions(
|
|||||||
ngDevMode && assertFirstCreatePass(tView);
|
ngDevMode && assertFirstCreatePass(tView);
|
||||||
const expando = tView.expandoInstructions !;
|
const expando = tView.expandoInstructions !;
|
||||||
// TODO(misko): PERF we are adding `hostBindings` even if there is nothing to add! This is
|
// TODO(misko): PERF we are adding `hostBindings` even if there is nothing to add! This is
|
||||||
// suboptimal for performance. See `currentDirectiveIndex` comment in
|
// suboptimal for performance. `def.hostBindings` may be null,
|
||||||
// `setHostBindingsByExecutingExpandoInstructions` for details.
|
|
||||||
// TODO(misko): PERF `def.hostBindings` may be null,
|
|
||||||
// but we still need to push null to the array as a placeholder
|
// but we still need to push null to the array as a placeholder
|
||||||
// to ensure the directive counter is incremented (so host
|
// to ensure the directive counter is incremented (so host
|
||||||
// binding functions always line up with the corrective directive).
|
// binding functions always line up with the corrective directive).
|
||||||
@ -1277,7 +1280,7 @@ function invokeDirectivesHostBindings(tView: TView, viewData: LView, tNode: TNod
|
|||||||
// It is important that this be called first before the actual instructions
|
// It is important that this be called first before the actual instructions
|
||||||
// are run because this way the first directive ID value is not zero.
|
// are run because this way the first directive ID value is not zero.
|
||||||
incrementActiveDirectiveId();
|
incrementActiveDirectiveId();
|
||||||
invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstCreatePass);
|
invokeHostBindingsInCreationMode(def, directive, tNode);
|
||||||
} else if (firstCreatePass) {
|
} else if (firstCreatePass) {
|
||||||
expando.push(null);
|
expando.push(null);
|
||||||
}
|
}
|
||||||
@ -1287,22 +1290,13 @@ function invokeDirectivesHostBindings(tView: TView, viewData: LView, tNode: TNod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(COMMIT): jsdoc
|
||||||
export function invokeHostBindingsInCreationMode(
|
export function invokeHostBindingsInCreationMode(
|
||||||
def: DirectiveDef<any>, expando: ExpandoInstructions, directive: any, tNode: TNode,
|
def: DirectiveDef<any>, directive: any, tNode: TNode) {
|
||||||
firstCreatePass: boolean) {
|
|
||||||
const previousExpandoLength = expando.length;
|
|
||||||
setCurrentDirectiveDef(def);
|
|
||||||
const elementIndex = tNode.index - HEADER_OFFSET;
|
|
||||||
// TODO(misko-next): This is a temporary work around for the fact that we moved the information
|
|
||||||
// from instruction to declaration. The workaround is to just call the instruction as if it was
|
|
||||||
// part of the `hostAttrs`.
|
|
||||||
if (def.hostAttrs !== null) {
|
|
||||||
ɵɵelementHostAttrs(def.hostAttrs);
|
|
||||||
}
|
|
||||||
if (def.hostBindings !== null) {
|
if (def.hostBindings !== null) {
|
||||||
|
const elementIndex = tNode.index - HEADER_OFFSET;
|
||||||
def.hostBindings !(RenderFlags.Create, directive, elementIndex);
|
def.hostBindings !(RenderFlags.Create, directive, elementIndex);
|
||||||
}
|
}
|
||||||
setCurrentDirectiveDef(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1331,7 +1325,7 @@ export function generateExpandoInstructionBlock(
|
|||||||
* Matches the current node against all available selectors.
|
* Matches the current node against all available selectors.
|
||||||
* If a component is matched (at most one), it is returned in first position in the array.
|
* If a component is matched (at most one), it is returned in first position in the array.
|
||||||
*/
|
*/
|
||||||
function findDirectiveMatches(
|
function findDirectiveDefMatches(
|
||||||
tView: TView, viewData: LView,
|
tView: TView, viewData: LView,
|
||||||
tNode: TElementNode | TContainerNode | TElementContainerNode): DirectiveDef<any>[]|null {
|
tNode: TElementNode | TContainerNode | TElementContainerNode): DirectiveDef<any>[]|null {
|
||||||
ngDevMode && assertFirstCreatePass(tView);
|
ngDevMode && assertFirstCreatePass(tView);
|
||||||
@ -1413,7 +1407,7 @@ function saveNameToExportMap(
|
|||||||
* the directive count to 0, and adding the isComponent flag.
|
* the directive count to 0, and adding the isComponent flag.
|
||||||
* @param index the initial index
|
* @param index the initial index
|
||||||
*/
|
*/
|
||||||
export function initNodeFlags(tNode: TNode, index: number, numberOfDirectives: number) {
|
export function initTNodeFlags(tNode: TNode, index: number, numberOfDirectives: number) {
|
||||||
ngDevMode && assertNotEqual(
|
ngDevMode && assertNotEqual(
|
||||||
numberOfDirectives, tNode.directiveEnd - tNode.directiveStart,
|
numberOfDirectives, tNode.directiveEnd - tNode.directiveStart,
|
||||||
'Reached the max number of directives');
|
'Reached the max number of directives');
|
||||||
|
@ -434,6 +434,19 @@ export interface TNode {
|
|||||||
*/
|
*/
|
||||||
attrs: TAttributes|null;
|
attrs: TAttributes|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as `TNode.attrs` but contains merged data across all directive host bindings.
|
||||||
|
*
|
||||||
|
* We need to keep `attrs` as unmerged so that it can be used for attribute selectors.
|
||||||
|
* We merge attrs here so that it can be used in a performant way for initial rendering.
|
||||||
|
*
|
||||||
|
* The `attrs` are merged in first pass in following order:
|
||||||
|
* - Component's `hostAttrs`
|
||||||
|
* - Directives' `hostAttrs`
|
||||||
|
* - Template `TNode.attrs` associated with the current `TNode`.
|
||||||
|
*/
|
||||||
|
mergedAttrs: TAttributes|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of local names under which a given element is exported in a template and
|
* A set of local names under which a given element is exported in a template and
|
||||||
* visible to queries. An entry in this array can be created for different reasons:
|
* visible to queries. An entry in this array can be created for different reasons:
|
||||||
|
@ -8,32 +8,52 @@
|
|||||||
|
|
||||||
import '../util/ng_dev_mode';
|
import '../util/ng_dev_mode';
|
||||||
|
|
||||||
import {assertDefined, assertNotEqual} from '../util/assert';
|
import {assertDefined, assertEqual, assertNotEqual} from '../util/assert';
|
||||||
|
|
||||||
import {AttributeMarker, TAttributes, TNode, TNodeType, unusedValueExportToPlacateAjd as unused1} from './interfaces/node';
|
import {AttributeMarker, TAttributes, TNode, TNodeType, unusedValueExportToPlacateAjd as unused1} from './interfaces/node';
|
||||||
import {CssSelector, CssSelectorList, SelectorFlags, unusedValueExportToPlacateAjd as unused2} from './interfaces/projection';
|
import {CssSelector, CssSelectorList, SelectorFlags, unusedValueExportToPlacateAjd as unused2} from './interfaces/projection';
|
||||||
|
import {classIndexOf} from './styling/class_differ';
|
||||||
import {isNameOnlyAttributeMarker} from './util/attrs_utils';
|
import {isNameOnlyAttributeMarker} from './util/attrs_utils';
|
||||||
import {getInitialStylingValue} from './util/styling_utils';
|
|
||||||
|
|
||||||
const unusedValueToPlacateAjd = unused1 + unused2;
|
const unusedValueToPlacateAjd = unused1 + unused2;
|
||||||
|
|
||||||
const NG_TEMPLATE_SELECTOR = 'ng-template';
|
const NG_TEMPLATE_SELECTOR = 'ng-template';
|
||||||
|
|
||||||
function isCssClassMatching(nodeClassAttrVal: string, cssClassToMatch: string): boolean {
|
/**
|
||||||
const nodeClassesLen = nodeClassAttrVal.length;
|
* Search the `TAttributes` to see if it contains `cssClassToMatch` (case insensitive)
|
||||||
// we lowercase the class attribute value to be able to match
|
*
|
||||||
// selectors without case-sensitivity
|
* @param attrs `TAttributes` to search through.
|
||||||
// (selectors are already in lowercase when generated)
|
* @param cssClassToMatch class to match (lowercase)
|
||||||
const matchIndex = nodeClassAttrVal.toLowerCase().indexOf(cssClassToMatch);
|
* @param isProjectionMode Whether or not class matching should look into the attribute `class` in
|
||||||
const matchEndIdx = matchIndex + cssClassToMatch.length;
|
* addition to the `AttributeMarker.Classes`.
|
||||||
if (matchIndex === -1 // no match
|
*/
|
||||||
|| (matchIndex > 0 && nodeClassAttrVal ![matchIndex - 1] !== ' ') // no space before
|
function isCssClassMatching(
|
||||||
||
|
attrs: TAttributes, cssClassToMatch: string, isProjectionMode: boolean): boolean {
|
||||||
(matchEndIdx < nodeClassesLen && nodeClassAttrVal ![matchEndIdx] !== ' ')) // no space after
|
// TODO(misko): The fact that this function needs to know about `isProjectionMode` seems suspect.
|
||||||
{
|
// It is strange to me that sometimes the class information comes in form of `class` attribute
|
||||||
return false;
|
// and sometimes in form of `AttributeMarker.Classes`. Some investigation is needed to determine
|
||||||
|
// if that is the right behavior.
|
||||||
|
ngDevMode &&
|
||||||
|
assertEqual(
|
||||||
|
cssClassToMatch, cssClassToMatch.toLowerCase(), 'Class name expected to be lowercase.');
|
||||||
|
let i = 0;
|
||||||
|
while (i < attrs.length) {
|
||||||
|
let item = attrs[i++];
|
||||||
|
if (isProjectionMode && item === 'class') {
|
||||||
|
item = attrs[i] as string;
|
||||||
|
if (classIndexOf(item.toLowerCase(), cssClassToMatch, 0) !== -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (item === AttributeMarker.Classes) {
|
||||||
|
// We found the classes section. Start searching for the class.
|
||||||
|
while (i < attrs.length && typeof(item = attrs[i++]) == 'string') {
|
||||||
|
// while we have strings
|
||||||
|
if (item.toLowerCase() === cssClassToMatch) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,9 +126,8 @@ export function isNodeMatchingSelector(
|
|||||||
|
|
||||||
// special case for matching against classes when a tNode has been instantiated with
|
// special case for matching against classes when a tNode has been instantiated with
|
||||||
// class and style values as separate attribute values (e.g. ['title', CLASS, 'foo'])
|
// class and style values as separate attribute values (e.g. ['title', CLASS, 'foo'])
|
||||||
if ((mode & SelectorFlags.CLASS) && tNode.classes) {
|
if ((mode & SelectorFlags.CLASS) && tNode.attrs !== null) {
|
||||||
if (!isCssClassMatching(
|
if (!isCssClassMatching(tNode.attrs, selectorAttrValue as string, isProjectionMode)) {
|
||||||
getInitialStylingValue(tNode.classes), selectorAttrValue as string)) {
|
|
||||||
if (isPositive(mode)) return false;
|
if (isPositive(mode)) return false;
|
||||||
skipToNextSelector = true;
|
skipToNextSelector = true;
|
||||||
}
|
}
|
||||||
@ -143,7 +162,7 @@ export function isNodeMatchingSelector(
|
|||||||
|
|
||||||
const compareAgainstClassName = mode & SelectorFlags.CLASS ? nodeAttrValue : null;
|
const compareAgainstClassName = mode & SelectorFlags.CLASS ? nodeAttrValue : null;
|
||||||
if (compareAgainstClassName &&
|
if (compareAgainstClassName &&
|
||||||
!isCssClassMatching(compareAgainstClassName, selectorAttrValue as string) ||
|
classIndexOf(compareAgainstClassName, selectorAttrValue as string, 0) !== -1 ||
|
||||||
mode & SelectorFlags.ATTRIBUTE && selectorAttrValue !== nodeAttrValue) {
|
mode & SelectorFlags.ATTRIBUTE && selectorAttrValue !== nodeAttrValue) {
|
||||||
if (isPositive(mode)) return false;
|
if (isPositive(mode)) return false;
|
||||||
skipToNextSelector = true;
|
skipToNextSelector = true;
|
||||||
|
@ -194,14 +194,6 @@ export function decreaseElementDepthCount() {
|
|||||||
instructionState.lFrame.elementDepthCount--;
|
instructionState.lFrame.elementDepthCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCurrentDirectiveDef(): DirectiveDef<any>|ComponentDef<any>|null {
|
|
||||||
return instructionState.lFrame.currentDirectiveDef;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setCurrentDirectiveDef(def: DirectiveDef<any>| ComponentDef<any>| null): void {
|
|
||||||
instructionState.lFrame.currentDirectiveDef = def;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getBindingsEnabled(): boolean {
|
export function getBindingsEnabled(): boolean {
|
||||||
return instructionState.bindingsEnabled;
|
return instructionState.bindingsEnabled;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ export function removeClass(className: string, classToRemove: string): string {
|
|||||||
*
|
*
|
||||||
* @param className A string containing classes (whitespace separated)
|
* @param className A string containing classes (whitespace separated)
|
||||||
* @param classToToggle A class name to remove or add to the `className`
|
* @param classToToggle A class name to remove or add to the `className`
|
||||||
* @param toggle Weather the resulting `className` should contain or not the `classToToggle`
|
* @param toggle Whether the resulting `className` should contain or not the `classToToggle`
|
||||||
* @returns a new class-list which does not have `classToRemove`
|
* @returns a new class-list which does not have `classToRemove`
|
||||||
*/
|
*/
|
||||||
export function toggleClass(className: string, classToToggle: string, toggle: boolean): string {
|
export function toggleClass(className: string, classToToggle: string, toggle: boolean): string {
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {assertEqual} from '../../util/assert';
|
|
||||||
import {CharCode} from '../../util/char_code';
|
import {CharCode} from '../../util/char_code';
|
||||||
import {AttributeMarker, TAttributes} from '../interfaces/node';
|
import {AttributeMarker, TAttributes} from '../interfaces/node';
|
||||||
import {CssSelector} from '../interfaces/projection';
|
import {CssSelector} from '../interfaces/projection';
|
||||||
|
@ -830,6 +830,30 @@ describe('host bindings', () => {
|
|||||||
expect(hostElement.title).toBe('other title');
|
expect(hostElement.title).toBe('other title');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should merge attributes on host and template', () => {
|
||||||
|
@Directive({selector: '[dir1]', host: {id: 'dir1'}})
|
||||||
|
class MyDir1 {
|
||||||
|
}
|
||||||
|
@Directive({selector: '[dir2]', host: {id: 'dir2'}})
|
||||||
|
class MyDir2 {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: `<div dir1 dir2 id="tmpl"></div>`})
|
||||||
|
class MyComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [MyComp, MyDir1, MyDir2]});
|
||||||
|
const fixture = TestBed.createComponent(MyComp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const div: HTMLElement = fixture.debugElement.nativeElement.firstChild;
|
||||||
|
expect(div.id).toEqual(
|
||||||
|
ivyEnabled ?
|
||||||
|
// In ivy the correct result is `tmpl` because template has the highest priority.
|
||||||
|
'tmpl' :
|
||||||
|
// In VE the order was simply that of execution and so dir2 would win.
|
||||||
|
'dir2');
|
||||||
|
});
|
||||||
|
|
||||||
onlyInIvy('Host bindings do not get merged in ViewEngine')
|
onlyInIvy('Host bindings do not get merged in ViewEngine')
|
||||||
.it('should work correctly with inherited directives with hostBindings', () => {
|
.it('should work correctly with inherited directives with hostBindings', () => {
|
||||||
@Directive({selector: '[superDir]', host: {'[id]': 'id'}})
|
@Directive({selector: '[superDir]', host: {'[id]': 'id'}})
|
||||||
|
@ -191,6 +191,9 @@
|
|||||||
{
|
{
|
||||||
"name": "callHooks"
|
"name": "callHooks"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "classIndexOf"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "concatString"
|
"name": "concatString"
|
||||||
},
|
},
|
||||||
@ -279,7 +282,7 @@
|
|||||||
"name": "findAttrIndexInNode"
|
"name": "findAttrIndexInNode"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "findDirectiveMatches"
|
"name": "findDirectiveDefMatches"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "forceStylesAsString"
|
"name": "forceStylesAsString"
|
||||||
@ -407,9 +410,6 @@
|
|||||||
{
|
{
|
||||||
"name": "getStylingMapArray"
|
"name": "getStylingMapArray"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "getTNode"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "growHostVarsSpace"
|
"name": "growHostVarsSpace"
|
||||||
},
|
},
|
||||||
@ -444,7 +444,7 @@
|
|||||||
"name": "incrementInitPhaseFlags"
|
"name": "incrementInitPhaseFlags"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "initNodeFlags"
|
"name": "initTNodeFlags"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "initializeInputAndOutputAliases"
|
"name": "initializeInputAndOutputAliases"
|
||||||
@ -533,6 +533,12 @@
|
|||||||
{
|
{
|
||||||
"name": "matchTemplateAttribute"
|
"name": "matchTemplateAttribute"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "mergeHostAttribute"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mergeHostAttrs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "nativeAppendChild"
|
"name": "nativeAppendChild"
|
||||||
},
|
},
|
||||||
@ -638,9 +644,6 @@
|
|||||||
{
|
{
|
||||||
"name": "setClassName"
|
"name": "setClassName"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "setCurrentDirectiveDef"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "setCurrentQueryIndex"
|
"name": "setCurrentQueryIndex"
|
||||||
},
|
},
|
||||||
@ -719,9 +722,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ɵɵelementEnd"
|
"name": "ɵɵelementEnd"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "ɵɵelementHostAttrs"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "ɵɵelementStart"
|
"name": "ɵɵelementStart"
|
||||||
},
|
},
|
||||||
|
@ -347,9 +347,6 @@
|
|||||||
{
|
{
|
||||||
"name": "getStylingMapArray"
|
"name": "getStylingMapArray"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "getTNode"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "growHostVarsSpace"
|
"name": "growHostVarsSpace"
|
||||||
},
|
},
|
||||||
@ -372,7 +369,7 @@
|
|||||||
"name": "incrementInitPhaseFlags"
|
"name": "incrementInitPhaseFlags"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "initNodeFlags"
|
"name": "initTNodeFlags"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "insertBloom"
|
"name": "insertBloom"
|
||||||
@ -518,9 +515,6 @@
|
|||||||
{
|
{
|
||||||
"name": "setClassName"
|
"name": "setClassName"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "setCurrentDirectiveDef"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "setCurrentQueryIndex"
|
"name": "setCurrentQueryIndex"
|
||||||
},
|
},
|
||||||
@ -572,9 +566,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ɵɵdefineComponent"
|
"name": "ɵɵdefineComponent"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "ɵɵelementHostAttrs"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "ɵɵtext"
|
"name": "ɵɵtext"
|
||||||
}
|
}
|
||||||
|
@ -437,6 +437,9 @@
|
|||||||
{
|
{
|
||||||
"name": "checkNoChangesInternal"
|
"name": "checkNoChangesInternal"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "classIndexOf"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "cleanUpView"
|
"name": "cleanUpView"
|
||||||
},
|
},
|
||||||
@ -579,7 +582,7 @@
|
|||||||
"name": "findAttrIndexInNode"
|
"name": "findAttrIndexInNode"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "findDirectiveMatches"
|
"name": "findDirectiveDefMatches"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "findExistingListener"
|
"name": "findExistingListener"
|
||||||
@ -867,7 +870,7 @@
|
|||||||
"name": "incrementInitPhaseFlags"
|
"name": "incrementInitPhaseFlags"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "initNodeFlags"
|
"name": "initTNodeFlags"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "initializeInputAndOutputAliases"
|
"name": "initializeInputAndOutputAliases"
|
||||||
@ -1040,6 +1043,12 @@
|
|||||||
{
|
{
|
||||||
"name": "matchTemplateAttribute"
|
"name": "matchTemplateAttribute"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "mergeHostAttribute"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mergeHostAttrs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "nativeAppendChild"
|
"name": "nativeAppendChild"
|
||||||
},
|
},
|
||||||
@ -1217,9 +1226,6 @@
|
|||||||
{
|
{
|
||||||
"name": "setClassName"
|
"name": "setClassName"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "setCurrentDirectiveDef"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "setCurrentQueryIndex"
|
"name": "setCurrentQueryIndex"
|
||||||
},
|
},
|
||||||
@ -1382,9 +1388,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ɵɵelementEnd"
|
"name": "ɵɵelementEnd"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "ɵɵelementHostAttrs"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "ɵɵelementStart"
|
"name": "ɵɵelementStart"
|
||||||
},
|
},
|
||||||
|
@ -22,7 +22,7 @@ describe('css selector matching', () => {
|
|||||||
const tNode = (!attrsOrTNode || Array.isArray(attrsOrTNode)) ?
|
const tNode = (!attrsOrTNode || Array.isArray(attrsOrTNode)) ?
|
||||||
createTNode(null !, null, TNodeType.Element, 0, tagName, attrsOrTNode as TAttributes) :
|
createTNode(null !, null, TNodeType.Element, 0, tagName, attrsOrTNode as TAttributes) :
|
||||||
(attrsOrTNode as TNode);
|
(attrsOrTNode as TNode);
|
||||||
return isNodeMatchingSelector(tNode, selector, false);
|
return isNodeMatchingSelector(tNode, selector, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('isNodeMatchingSimpleSelector', () => {
|
describe('isNodeMatchingSimpleSelector', () => {
|
||||||
@ -322,26 +322,6 @@ describe('css selector matching', () => {
|
|||||||
// <div class="foo">
|
// <div class="foo">
|
||||||
expect(isMatching('div', ['class', 'foo'], selector)).toBeFalsy();
|
expect(isMatching('div', ['class', 'foo'], selector)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should match against a class value before and after the styling context is created',
|
|
||||||
() => {
|
|
||||||
// selector: 'div.abc'
|
|
||||||
const selector = ['div', SelectorFlags.CLASS, 'abc'];
|
|
||||||
const tNode = createTNode(null !, null, TNodeType.Element, 0, 'div', []);
|
|
||||||
|
|
||||||
// <div> (without attrs or styling context)
|
|
||||||
expect(isMatching('div', tNode, selector)).toBeFalsy();
|
|
||||||
|
|
||||||
// <div class="abc"> (with attrs but without styling context)
|
|
||||||
tNode.attrs = ['class', 'abc'];
|
|
||||||
tNode.classes = null;
|
|
||||||
expect(isMatching('div', tNode, selector)).toBeTruthy();
|
|
||||||
|
|
||||||
// <div class="abc"> (with styling context but without attrs)
|
|
||||||
tNode.classes = ['abc', 'abc', true];
|
|
||||||
tNode.attrs = null;
|
|
||||||
expect(isMatching('div', tNode, selector)).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user