fix(ivy): not throwing error for unknown properties on container nodes (#29691)

Fixes Ivy not throwing an error if it runs into an invalid property binding on a container node (e.g. `<div *ngFor="let row of rows">` instead of `<div *ngFor="let row if rows">`).

This PR resolves FW-1219.

PR Close #29691
This commit is contained in:
Kristiyan Kostadinov 2019-04-08 19:59:39 +02:00 committed by Igor Minar
parent 66b87cef33
commit d144a3bd91
2 changed files with 34 additions and 2 deletions

View File

@ -161,6 +161,12 @@ function elementPropertyInternal<T>(
(element as RElement).setProperty ? (element as any).setProperty(propName, value) : (element as RElement).setProperty ? (element as any).setProperty(propName, value) :
(element as any)[propName] = value; (element as any)[propName] = value;
} }
} else if (tNode.type === TNodeType.Container) {
// If the node is a container and the property didn't
// match any of the inputs or schemas we should throw.
if (ngDevMode && !matchingSchemas(lView, tNode.tagName)) {
throw createUnknownPropertyError(propName, tNode);
}
} }
} }
@ -209,8 +215,7 @@ function validateAgainstUnknownProperties(
// and isn't a synthetic animation property... // and isn't a synthetic animation property...
propName[0] !== ANIMATION_PROP_PREFIX) { propName[0] !== ANIMATION_PROP_PREFIX) {
// ... it is probably a user error and we should throw. // ... it is probably a user error and we should throw.
throw new Error( throw createUnknownPropertyError(propName, tNode);
`Template error: Can't bind to '${propName}' since it isn't a known property of '${tNode.tagName}'.`);
} }
} }
@ -257,3 +262,13 @@ function savePropertyDebugData(
} }
} }
} }
/**
* Creates an error that should be thrown when encountering an unknown property on an element.
* @param propName Name of the invalid property.
* @param tNode Node on which we encountered the error.
*/
function createUnknownPropertyError(propName: string, tNode: TNode): Error {
return new Error(
`Template error: Can't bind to '${propName}' since it isn't a known property of '${tNode.tagName}'.`);
}

View File

@ -1634,6 +1634,21 @@ function declareTests(config?: {useJit: boolean}) {
} }
}); });
it('should throw on bindings to unknown properties of containers', () => {
TestBed.configureTestingModule({imports: [CommonModule], declarations: [MyComp]});
const template = '<div *ngFor="let item in ctxArrProp">{{item}}</div>';
TestBed.overrideComponent(MyComp, {set: {template}});
try {
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();
throw 'Should throw';
} catch (e) {
expect(e.message).toMatch(
/Can't bind to 'ngForIn' since it isn't a known property of 'div'./);
}
});
it('should not throw for property binding to a non-existing property when there is a matching directive property', it('should not throw for property binding to a non-existing property when there is a matching directive property',
() => { () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir]}); TestBed.configureTestingModule({declarations: [MyComp, MyDir]});
@ -2103,12 +2118,14 @@ class MyComp {
ctxProp: string; ctxProp: string;
ctxNumProp: number; ctxNumProp: number;
ctxBoolProp: boolean; ctxBoolProp: boolean;
ctxArrProp: number[];
toStringThrow = {toString: function() { throw 'boom'; }}; toStringThrow = {toString: function() { throw 'boom'; }};
constructor() { constructor() {
this.ctxProp = 'initial value'; this.ctxProp = 'initial value';
this.ctxNumProp = 0; this.ctxNumProp = 0;
this.ctxBoolProp = false; this.ctxBoolProp = false;
this.ctxArrProp = [0, 1, 2];
} }
throwError() { throw 'boom'; } throwError() { throw 'boom'; }