fix(ivy): throw on bindings to unknown properties (#28537)

This commit adds a devMode-only check which will throw if a user
attempts to bind a property that does not match a directive
input or a known HTML property.

Example:
```
<div [unknownProp]="someValue"></div>
```

The above will throw because "unknownProp" is not a known
property of HTMLDivElement.

This check is similar to the check executed in View Engine during
template parsing, but occurs at runtime instead of compile-time.

Note: This change uncovered an existing bug with host binding
inheritance, so some Material tests had to be turned off. They
will be fixed in an upcoming PR.

PR Close #28537
This commit is contained in:
Kara Erickson
2019-02-04 21:42:55 -08:00
committed by Miško Hevery
parent 7660d0d74a
commit 1950e2d9ba
13 changed files with 262 additions and 160 deletions

View File

@ -182,11 +182,12 @@ describe('instructions', () => {
});
it('should not stringify non string values', () => {
const t = new TemplateFixture(createDiv, () => {}, 1);
const t = new TemplateFixture(() => { element(0, 'input'); }, () => {}, 1);
t.update(() => elementProperty(0, 'hidden', false));
// The hidden property would be true if `false` was stringified into `"false"`.
expect((t.hostElement as HTMLElement).querySelector('div') !.hidden).toEqual(false);
// Note: don't use 'hidden' here because IE10 does not support the hidden property
t.update(() => elementProperty(0, 'required', false));
// The required property would be true if `false` was stringified into `"false"`.
expect((t.hostElement as HTMLElement).querySelector('input') !.required).toEqual(false);
expect(ngDevMode).toHaveProperties({
firstTemplatePass: 1,
tNode: 2, // 1 for div, 1 for host element

View File

@ -443,7 +443,8 @@ describe('lifecycles', () => {
factory: () => new Component(), template,
consts: consts,
vars: vars,
directives: directives
directives: directives,
inputs: {val: 'val'}
});
};
}
@ -2665,10 +2666,10 @@ describe('lifecycles', () => {
element(0, 'div');
}
if (rf & RenderFlags.Update) {
elementProperty(0, 'data-a', bind(ctx.a));
elementProperty(0, 'id', bind(ctx.a));
}
},
selectors: [['mycomp']],
selectors: [['my-comp']],
inputs: {
value: 'value',
},
@ -2683,7 +2684,7 @@ describe('lifecycles', () => {
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(0, 'mycomp');
element(0, 'my-comp');
}
if (rf & RenderFlags.Update) {
elementProperty(0, 'value', bind(1));

View File

@ -170,12 +170,12 @@ describe('pipe', () => {
elementEnd();
}
if (rf & RenderFlags.Update) {
elementProperty(0, 'someProp', bind(pipeBind1(1, 1, 'Megatron')));
elementProperty(0, 'id', bind(pipeBind1(1, 1, 'Megatron')));
}
}
renderToHtml(Template, person, 2, 3, null, [IdentityPipe], rendererFactory2);
expect(renderLog.log).toEqual(['someProp=Megatron']);
expect(renderLog.log).toEqual(['id=Megatron']);
renderLog.clear();
renderToHtml(Template, person, 2, 3, null, pipes, rendererFactory2);
@ -255,8 +255,8 @@ describe('pipe', () => {
container(4);
}
if (rf & RenderFlags.Update) {
elementProperty(0, 'someProp', bind(pipeBind1(1, 2, true)));
elementProperty(2, 'someProp', bind(pipeBind1(3, 4, true)));
elementProperty(0, 'id', bind(pipeBind1(1, 2, true)));
elementProperty(2, 'id', bind(pipeBind1(3, 4, true)));
pipeInstances.push(load<CountingImpurePipe>(1), load(3));
containerRefreshStart(4);
{
@ -269,7 +269,7 @@ describe('pipe', () => {
elementEnd();
}
if (rf1 & RenderFlags.Update) {
elementProperty(0, 'someProp', bind(pipeBind1(1, 1, true)));
elementProperty(0, 'id', bind(pipeBind1(1, 1, true)));
pipeInstances.push(load<CountingImpurePipe>(1));
}
}

View File

@ -42,12 +42,14 @@ describe('ViewContainerRef', () => {
factory: () => directiveInstance = new DirectiveWithVCRef(
directiveInject(ViewContainerRef as any), injectComponentFactoryResolver()),
inputs: {tplRef: 'tplRef'}
inputs: {tplRef: 'tplRef', name: 'name'}
});
// TODO(issue/24571): remove '!'.
tplRef !: TemplateRef<{}>;
name: string = '';
// injecting a ViewContainerRef to create a dynamic container in which embedded views will be
// created
constructor(public vcref: ViewContainerRef, public cfr: ComponentFactoryResolver) {}