Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): produce proper error message for unknown props on <ng-template>s #46068

Closed
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'/);
AndrewKushnir marked this conversation as resolved.
Show resolved Hide resolved
});

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