Which @angular/* package(s) are the source of the bug?
compiler-cli
Is this a regression?
No
Description
If I forget to call a function in a template event binding, Angular warns me (NG8111). If I make the exact same mistake in the component's host metadata, I get no warning at all.
@Component({
selector: 'app-repro',
standalone: true,
template: 'x',
host: { '(click)': 'myFn' }, // same mistake: myFn is never called
})
export class Repro {
myFn(): void {}
}
Clicking does nothing, and the compiler says nothing either. The NG8111 check's own doc says it exists to catch this:
"Ensures that function in event bindings are called. For example, <button (click)="myFunc"></button> will not call myFunc when the button is clicked. Instead, it should be <button (click)="myFunc()"></button>."
Comparison — same class, only the place where I write the binding differs:
| # |
How the listener is written |
What I get |
| A |
<button (click)="myFn">x</button> |
NG8111 reported ✅ |
| B |
host: { '(click)': 'myFn' } |
nothing at all ❌ |
| C |
host: { '(click)': 'myFn()' } |
nothing (correct — the function is called) |
Case B is the problem. And unlike some other similar reports, TypeScript doesn't catch it either — I checked with a plain tsc run (exit 0, no errors) — so the user gets total silence.
Evidence
I debugged the compiler (@angular/compiler-cli@22.1.3) and took two screenshots at the same breakpoint in the same source file — the only difference is which component is being checked.
1. Entry A (template). Inside extended_template_checker.ts, getDiagnosticsForComponent: template = ["button"]. The template contains the element with (click)="myFn", so the check sees it and reports NG8111.

2. Entry B (host). Same breakpoint, same line, but this is the component with host: { '(click)': 'myFn' }: template = ["Text"]. The host listener isn't in there at all, so the check has nothing to look at.

Why it can't be caught today
getDiagnosticsForComponent() only ever hands the check the template:
const template = this.partialCtx.templateTypeChecker.getTemplate(component);
...
diagnostics.push(...check.run(ctx, component, template));
getTemplate() returns TmplAstNode[] | null — template AST only. Host listener metadata lives on the decorator, so it never enters that tree. On top of that, the check starts with if (!(node instanceof TmplAstBoundEvent)) return [];.
Not the same as #70680
That one was about a signal without () in @defer (when ...), where an extra TypeScript error ("This condition will always return true since this function is always defined…") already reached the user, so adding NG8109 there was redundant. Here there is no TypeScript fallback for case B — the user sees nothing.
Question
Is host: metadata meant to be out of scope for extended diagnostics? I couldn't find this stated either way in the NG8111 docs or the extended-diagnostics overview. If it's intentional, feel free to close this. If not, I'd like to help — but I'd rather ask first than send a patch in the wrong direction.
Please provide a link to a minimal reproduction of the bug
The two screenshots above are the reproduction. Minimal source:
import { Component } from '@angular/core';
@Component({
selector: 'app-repro',
standalone: true,
template: 'x',
host: { '(click)': 'myFn' }, // nothing reported
})
export class Repro {
myFn(): void {}
}
@Component({
selector: 'app-control',
standalone: true,
template: `<button (click)="myFn">x</button>`, // NG8111 reported
})
export class Control {
myFn(): void {}
}
Compile both with ngc (strictTemplates: true, compilationMode: "full"): Control reports NG8111, Repro stays silent.
Please tell us about your environment
- Angular version:
@angular/compiler-cli@22.1.3 (the check is unchanged on main as of writing)
- Node version: v26.8.1
- Package Manager: npm
- OS: macOS (darwin arm64)
- Language: TypeScript 6.0.2
Which @angular/* package(s) are the source of the bug?
compiler-cli
Is this a regression?
No
Description
If I forget to call a function in a template event binding, Angular warns me (NG8111). If I make the exact same mistake in the component's
hostmetadata, I get no warning at all.Clicking does nothing, and the compiler says nothing either. The NG8111 check's own doc says it exists to catch this:
Comparison — same class, only the place where I write the binding differs:
<button (click)="myFn">x</button>host: { '(click)': 'myFn' }host: { '(click)': 'myFn()' }Case B is the problem. And unlike some other similar reports, TypeScript doesn't catch it either — I checked with a plain
tscrun (exit 0, no errors) — so the user gets total silence.Evidence
I debugged the compiler (
@angular/compiler-cli@22.1.3) and took two screenshots at the same breakpoint in the same source file — the only difference is which component is being checked.1. Entry A (template). Inside
extended_template_checker.ts,getDiagnosticsForComponent:template=["button"]. The template contains the element with(click)="myFn", so the check sees it and reports NG8111.2. Entry B (host). Same breakpoint, same line, but this is the component with
host: { '(click)': 'myFn' }:template=["Text"]. The host listener isn't in there at all, so the check has nothing to look at.Why it can't be caught today
getDiagnosticsForComponent()only ever hands the check the template:getTemplate()returnsTmplAstNode[] | null— template AST only. Host listener metadata lives on the decorator, so it never enters that tree. On top of that, the check starts withif (!(node instanceof TmplAstBoundEvent)) return [];.Not the same as #70680
That one was about a signal without
()in@defer (when ...), where an extra TypeScript error ("This condition will always return true since this function is always defined…") already reached the user, so adding NG8109 there was redundant. Here there is no TypeScript fallback for case B — the user sees nothing.Question
Is
host:metadata meant to be out of scope for extended diagnostics? I couldn't find this stated either way in the NG8111 docs or the extended-diagnostics overview. If it's intentional, feel free to close this. If not, I'd like to help — but I'd rather ask first than send a patch in the wrong direction.Please provide a link to a minimal reproduction of the bug
The two screenshots above are the reproduction. Minimal source:
Compile both with
ngc(strictTemplates: true,compilationMode: "full"):Controlreports NG8111,Reprostays silent.Please tell us about your environment
@angular/compiler-cli@22.1.3(the check is unchanged onmainas of writing)