feat(ivy): support inputs & outputs with aliases in component decorators (#27350)

PR Close #27350
This commit is contained in:
Olivier Combe 2018-11-30 17:45:04 +01:00 committed by Igor Minar
parent 1279a503a1
commit 2bc39860bb
4 changed files with 90 additions and 89 deletions

View File

@ -30,7 +30,7 @@ import {typeWithParameters} from '../util';
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api'; import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api';
import {StylingBuilder, StylingInstruction} from './styling'; import {StylingBuilder, StylingInstruction} from './styling';
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, renderFlagCheckIfStmt} from './template'; import {BindingScope, TemplateDefinitionBuilder, ValueConverter, renderFlagCheckIfStmt} from './template';
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, mapToExpression, temporaryAllocator} from './util'; import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
const EMPTY_ARRAY: any[] = []; const EMPTY_ARRAY: any[] = [];

View File

@ -8,8 +8,8 @@
import {ConstantPool} from '../../constant_pool'; import {ConstantPool} from '../../constant_pool';
import * as o from '../../output/output_ast'; import * as o from '../../output/output_ast';
import {splitAtColon} from '../../util';
import * as t from '../r3_ast'; import * as t from '../r3_ast';
import {R3QueryMetadata} from './api'; import {R3QueryMetadata} from './api';
import {isI18nAttribute} from './i18n/util'; import {isI18nAttribute} from './i18n/util';
@ -75,9 +75,13 @@ export function conditionallyCreateMapObjectLiteral(keys: {[key: string]: string
return null; return null;
} }
export function mapToExpression(map: {[key: string]: any}, quoted = false): o.Expression { function mapToExpression(map: {[key: string]: any}): o.Expression {
return o.literalMap( return o.literalMap(Object.getOwnPropertyNames(map).map(key => {
Object.getOwnPropertyNames(map).map(key => ({key, quoted, value: asLiteral(map[key])}))); // canonical syntax: `dirProp: elProp`
// if there is no `:`, use dirProp = elProp
const parts = splitAtColon(key, [key, map[key]]);
return {key: parts[0], quoted: false, value: asLiteral(parts[1])};
}));
} }
/** /**

View File

@ -958,7 +958,7 @@ export function elementProperty<T>(
if (isComponent(tNode)) markDirtyIfOnPush(lView, index + HEADER_OFFSET); if (isComponent(tNode)) markDirtyIfOnPush(lView, index + HEADER_OFFSET);
if (ngDevMode) { if (ngDevMode) {
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.Container) { if (tNode.type === TNodeType.Element || tNode.type === TNodeType.Container) {
setNgReflectProperties(lView, element, tNode.type, propName, value); setNgReflectProperties(lView, element, tNode.type, dataValue, value);
} }
} }
} else if (tNode.type === TNodeType.Element) { } else if (tNode.type === TNodeType.Element) {
@ -1033,9 +1033,11 @@ function setInputsForProperty(lView: LView, inputs: PropertyAliasValue, value: a
} }
function setNgReflectProperties( function setNgReflectProperties(
lView: LView, element: RElement | RComment, type: TNodeType, propName: string, value: any) { lView: LView, element: RElement | RComment, type: TNodeType, inputs: PropertyAliasValue,
value: any) {
for (let i = 0; i < inputs.length; i += 2) {
const renderer = lView[RENDERER]; const renderer = lView[RENDERER];
const attrName = normalizeDebugBindingName(propName); const attrName = normalizeDebugBindingName(inputs[i + 1] as string);
const debugValue = normalizeDebugBindingValue(value); const debugValue = normalizeDebugBindingValue(value);
if (type === TNodeType.Element) { if (type === TNodeType.Element) {
isProceduralRenderer(renderer) ? isProceduralRenderer(renderer) ?
@ -1050,12 +1052,13 @@ function setNgReflectProperties(
} }
} }
} }
}
/** /**
* Consolidates all inputs or outputs of all directives on this logical node. * Consolidates all inputs or outputs of all directives on this logical node.
* *
* @param number tNodeFlags node flags * @param tNodeFlags node flags
* @param Direction direction whether to consider inputs or outputs * @param direction whether to consider inputs or outputs
* @returns PropertyAliases|null aggregate of all properties if any, `null` otherwise * @returns PropertyAliases|null aggregate of all properties if any, `null` otherwise
*/ */
function generatePropertyAliases(tNode: TNode, direction: BindingDirection): PropertyAliases|null { function generatePropertyAliases(tNode: TNode, direction: BindingDirection): PropertyAliases|null {

View File

@ -238,8 +238,7 @@ function declareTests(config?: {useJit: boolean}) {
expect(getDOM().getProperty(nativeEl, 'htmlFor')).toBe('foo'); expect(getDOM().getProperty(nativeEl, 'htmlFor')).toBe('foo');
}); });
fixmeIvy('FW-587: Inputs with aliases in component decorators don\'t work') it('should consume directive watch expression change.', () => {
.it('should consume directive watch expression change.', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir]}); TestBed.configureTestingModule({declarations: [MyComp, MyDir]});
const template = '<span>' + const template = '<span>' +
'<div my-dir [elprop]="ctxProp"></div>' + '<div my-dir [elprop]="ctxProp"></div>' +
@ -263,8 +262,7 @@ function declareTests(config?: {useJit: boolean}) {
}); });
describe('pipes', () => { describe('pipes', () => {
fixmeIvy('FW-587: Inputs with aliases in component decorators don\'t work') it('should support pipes in bindings', () => {
.it('should support pipes in bindings', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir, DoublePipe]}); TestBed.configureTestingModule({declarations: [MyComp, MyDir, DoublePipe]});
const template = '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>'; const template = '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>';
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
@ -290,8 +288,7 @@ function declareTests(config?: {useJit: boolean}) {
}); });
// GH issue 328 - https://github.com/angular/angular/issues/328 // GH issue 328 - https://github.com/angular/angular/issues/328
fixmeIvy('FW-587: Inputs with aliases in component decorators don\'t work') it('should support different directive types on a single node', () => {
.it('should support different directive types on a single node', () => {
TestBed.configureTestingModule({declarations: [MyComp, ChildComp, MyDir]}); TestBed.configureTestingModule({declarations: [MyComp, ChildComp, MyDir]});
const template = '<child-cmp my-dir [elprop]="ctxProp"></child-cmp>'; const template = '<child-cmp my-dir [elprop]="ctxProp"></child-cmp>';
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
@ -1688,8 +1685,7 @@ function declareTests(config?: {useJit: boolean}) {
}); });
describe('logging property updates', () => { describe('logging property updates', () => {
fixmeIvy('FW-587: Inputs with aliases in component decorators don\'t work') it('should reflect property values as attributes', () => {
.it('should reflect property values as attributes', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir]}); TestBed.configureTestingModule({declarations: [MyComp, MyDir]});
const template = '<div>' + const template = '<div>' +
'<div my-dir [elprop]="ctxProp"></div>' + '<div my-dir [elprop]="ctxProp"></div>' +
@ -1726,9 +1722,7 @@ function declareTests(config?: {useJit: boolean}) {
.toContain('"ng\-reflect\-ng\-if"\: "true"'); .toContain('"ng\-reflect\-ng\-if"\: "true"');
}); });
// also affected by FW-587: Inputs with aliases in component decorators don't work it('should indicate when toString() throws', () => {
fixmeIvy('FW-664: ng-reflect-* is not supported')
.it('should indicate when toString() throws', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir]}); TestBed.configureTestingModule({declarations: [MyComp, MyDir]});
const template = '<div my-dir [elprop]="toStringThrow"></div>'; const template = '<div my-dir [elprop]="toStringThrow"></div>';
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});