From d144a3bd91bd75d2d0a2fd230df9d6fc5392ee7f Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 8 Apr 2019 19:59:39 +0200 Subject: [PATCH] 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. `
` instead of `
`). This PR resolves FW-1219. PR Close #29691 --- .../core/src/render3/instructions/property.ts | 19 +++++++++++++++++-- packages/core/test/linker/integration_spec.ts | 17 +++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/core/src/render3/instructions/property.ts b/packages/core/src/render3/instructions/property.ts index 768b07d8c7..e0718ddf6a 100644 --- a/packages/core/src/render3/instructions/property.ts +++ b/packages/core/src/render3/instructions/property.ts @@ -161,6 +161,12 @@ function elementPropertyInternal( (element as RElement).setProperty ? (element as any).setProperty(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... propName[0] !== ANIMATION_PROP_PREFIX) { // ... it is probably a user error and we should throw. - throw new Error( - `Template error: Can't bind to '${propName}' since it isn't a known property of '${tNode.tagName}'.`); + throw createUnknownPropertyError(propName, tNode); } } @@ -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}'.`); +} diff --git a/packages/core/test/linker/integration_spec.ts b/packages/core/test/linker/integration_spec.ts index 140dd9f776..2bc52e3b2c 100644 --- a/packages/core/test/linker/integration_spec.ts +++ b/packages/core/test/linker/integration_spec.ts @@ -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 = '
{{item}}
'; + 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', () => { TestBed.configureTestingModule({declarations: [MyComp, MyDir]}); @@ -2103,12 +2118,14 @@ class MyComp { ctxProp: string; ctxNumProp: number; ctxBoolProp: boolean; + ctxArrProp: number[]; toStringThrow = {toString: function() { throw 'boom'; }}; constructor() { this.ctxProp = 'initial value'; this.ctxNumProp = 0; this.ctxBoolProp = false; + this.ctxArrProp = [0, 1, 2]; } throwError() { throw 'boom'; }