Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions packages/core/src/render3/instructions/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1034,7 +1034,7 @@ export function elementPropertyInternal<T>(
validateAgainstEventProperties(propName);
if (!validateProperty(element, tNode.value, propName, tView.schemas)) {
// Return here since we only log warnings for unknown properties.
handleUnknownPropertyError(propName, tNode.value);
handleUnknownPropertyError(propName, tNode);
return;
}
ngDevMode.rendererSetProperty++;
Expand All @@ -1053,7 +1053,7 @@ export function elementPropertyInternal<T>(
// If the node is a container and the property didn't
// match any of the inputs or schemas we should throw.
if (ngDevMode && !matchingSchemas(tView.schemas, tNode.value)) {
handleUnknownPropertyError(propName, tNode.value);
handleUnknownPropertyError(propName, tNode);
}
}
}
Expand Down Expand Up @@ -1170,7 +1170,18 @@ export function matchingSchemas(schemas: SchemaMetadata[]|null, tagName: string|
* @param propName Name of the invalid property.
* @param tagName Name of the node on which we encountered the property.
*/
function handleUnknownPropertyError(propName: string, tagName: string): void {
function handleUnknownPropertyError(propName: string, tNode: TNode): void {
let tagName = tNode.value;

// Special-case a situation when a structural directive is applied to
// an `<ng-template>` element, for example: `<ng-template *ngIf="true">`.
// In this case the compiler generates the `ɵɵtemplate` instruction with
// the `null` as the tagName. The directive matching logic at runtime relies
// on this effect (see `isInlineTemplate`), thus using the 'ng-template' as
// a default value of the `tNode.value` is not feasible at this moment.
if (!tagName && tNode.type === TNodeType.Container) {
tagName = 'ng-template';
}
const message = `Can't bind to '${propName}' since it isn't a known property of '${tagName}'.`;
if (shouldThrowErrorOnUnknownProperty) {
throw new RuntimeError(RuntimeErrorCode.UNKNOWN_BINDING, message);
Expand Down
84 changes: 84 additions & 0 deletions packages/core/test/acceptance/ng_module_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,90 @@ describe('NgModule', () => {
expect(spy.calls.mostRecent().args[0])
.toMatch(/Can't bind to 'unknown-prop' since it isn't a known property of 'div'/);
});

it('should log an error on unknown props of `ng-template` if NO_ERRORS_SCHEMA is absent',
() => {
@Component({
selector: 'my-comp',
template: `
<ng-template *ngIf="condition"></ng-template>
`,
})
class MyComp {
condition = true;
}

@NgModule({
declarations: [MyComp],
})
class MyModule {
}

TestBed.configureTestingModule({imports: [MyModule]});

const spy = spyOn(console, 'error');
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();

expect(spy.calls.mostRecent().args[0])
.toMatch(/Can't bind to 'ngIf' since it isn't a known property of 'ng-template'/);
});

it('should log an error on unknown props of `ng-container` if NO_ERRORS_SCHEMA is absent',
() => {
@Component({
selector: 'my-comp',
template: `
<ng-container *ngIf="condition"></ng-container>
`,
})
class MyComp {
condition = true;
}

@NgModule({
declarations: [MyComp],
})
class MyModule {
}

TestBed.configureTestingModule({imports: [MyModule]});

const spy = spyOn(console, 'error');
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();

expect(spy.calls.mostRecent().args[0])
.toMatch(/Can't bind to 'ngIf' since it isn't a known property of 'ng-container'/);
});

it('should log an error on unknown props of `ng-content` if NO_ERRORS_SCHEMA is absent', () => {
@Component({
selector: 'my-comp',
template: `
<ng-content *ngIf="condition"></ng-content>
`,
})
class MyComp {
condition = true;
}

@NgModule({
declarations: [MyComp],
})
class MyModule {
}

TestBed.configureTestingModule({imports: [MyModule]});

const spy = spyOn(console, 'error');
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();

expect(spy.calls.mostRecent().args[0])
.toMatch(/Can't bind to 'ngIf' since it isn't a known property of 'ng-content'/);
});

it('should throw an error with errorOnUnknownProperties on unknown props if NO_ERRORS_SCHEMA is absent',
() => {
@Component({
Expand Down