fix(core): do not use unbound attributes as inputs to structural directives (#36441)

Prior to this commit unbound attributes were treated as possible inputs to structural directives. Since structural directives can only accepts inputs defined using microsyntax expression (e.g. `<div *dir="exp">`), such unbound attributes should not be considered as inputs. This commit aligns Ivy and View Engine behavior and avoids using unbound attributes as inputs to structural directives.

PR Close #36441
This commit is contained in:
Andrew Kushnir
2020-04-05 18:35:37 -07:00
committed by Matias Niemelä
parent 0bd50e2e50
commit c0ed57db76
5 changed files with 112 additions and 6 deletions

View File

@ -30,7 +30,7 @@ import {SanitizerFn} from '../interfaces/sanitization';
import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, 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, TVIEW, TView, TViewType} from '../interfaces/view';
import {assertNodeOfPossibleTypes} from '../node_assert';
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
import {isInlineTemplate, isNodeMatchingSelectorList} from '../node_selector_matcher';
import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, getTView, leaveView, setBindingIndex, setBindingRootForHostBindings, setCheckNoChangesMode, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state';
import {NO_CHANGE} from '../tokens';
import {isAnimationProp, mergeHostAttrs} from '../util/attrs_utils';
@ -900,8 +900,14 @@ function initializeInputAndOutputAliases(tView: TView, tNode: TNode): void {
for (let i = start; i < end; i++) {
const directiveDef = defs[i] as DirectiveDef<any>;
const directiveInputs = directiveDef.inputs;
inputsFromAttrs.push(
tNodeAttrs !== null ? generateInitialInputs(directiveInputs, tNodeAttrs) : null);
// Do not use unbound attributes as inputs to structural directives, since structural
// directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`).
// TODO(FW-1930): microsyntax expressions may also contain unbound/static attributes, which
// should be set for inline templates.
const initialInputs = (tNodeAttrs !== null && !isInlineTemplate(tNode)) ?
generateInitialInputs(directiveInputs, tNodeAttrs) :
null;
inputsFromAttrs.push(initialInputs);
inputsStore = generatePropertyAliases(directiveInputs, i, inputsStore);
outputsStore = generatePropertyAliases(directiveDef.outputs, i, outputsStore);
}

View File

@ -56,6 +56,15 @@ function isCssClassMatching(
return false;
}
/**
* Checks whether the `tNode` represents an inline template (e.g. `*ngFor`).
*
* @param tNode current TNode
*/
export function isInlineTemplate(tNode: TNode): boolean {
return tNode.type === TNodeType.Container && tNode.tagName !== NG_TEMPLATE_SELECTOR;
}
/**
* Function that checks whether a given tNode matches tag-based selector and has a valid type.
*
@ -134,11 +143,9 @@ export function isNodeMatchingSelector(
continue;
}
const isInlineTemplate =
tNode.type == TNodeType.Container && tNode.tagName !== NG_TEMPLATE_SELECTOR;
const attrName = (mode & SelectorFlags.CLASS) ? 'class' : current;
const attrIndexInNode =
findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate, isProjectionMode);
findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate(tNode), isProjectionMode);
if (attrIndexInNode === -1) {
if (isPositive(mode)) return false;