Common Angular Errors And How To Fix Them
Angular is such an awesome framework, but sometimes the errors leave you guessing. Have you ever wished that you had a cheat-sheet for fixing errors in Angular? Well, now you do. This article covers the most common Angular errors, their most common causes, and how to fix them.
This article will be divided into sections for each error. Search the text of your error, or settle in for some light error reading. 😉
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked
Here is the most infamous of Angular errors. There are a variety of causes and fixes, but here’s the silver bullet fix: Use the OnPush
change detection strategy. In a root component, AppComponent
if you’d like, specify your change detection strategy:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
Every child component will inherit this change detection strategy. It will completely eliminate the error but there’s a caveat: every binding must be immutable. That means you can’t mutate the same property, you have to set it to something entirely new in order to trigger change detection.
If you don’t want to implement On-push, here are common causes of the error and how to fix it:
- A property you are binding to was
undefined
ornull
and then had a value set at runtime. Solution: set default value. - Sometimes this error occurs because you are using the async pipe with synchronous code, such as calling
next()
on aBehaviorSubject
. Here are several ways to make your code asynchronous: Use asetTimeout
, or pipedelay(0)
on your observable:
setTimeout(() => this.mySubject$.next(true));
// or...
this.mySubject$.pipe(delay(0));
...
this.mySubject$.next(true);
Error: Template parse errors:
Can’t bind to ‘formControl’ since it isn’t a known property of ‘input’.
- You need to add
ReactiveFormsModule
to the imports of the module your component belongs to.
Error: Template parse errors:
Unexpected character “EOF” (Do you have an unescaped “{“ in your template?
- Your template is likely missing a double-quote (“) or perhaps has an extra double-quote.
- Your template has invalid interpolation — Where you should have {{ }} you might be missing a curly bracket.
Error: No value accessor for form control with name: ‘foo’
- You added a
formControl
orformControlName
binding to an element that isn’t an input, select or textarea. Reactive form controls can only be bound to valid form controls.
Error: Template parse errors: Can’t bind to ‘foo’ since it isn’t a known property of ‘element’
- If it’s a directive selector, omit brackets unless it’s holding a value
- If you’re seeing this error on a component, skip down to the next error
Otherwise, you might be trying to bind to a native HTML element attribute. Maybe you tried something like this:
<a href=”test” [alt]=”myProp”>Hi</a>
The proper way to do this is to prefix native attributes with “attr” like so:
<a href=”test” [attr.alt]=”myProp”>Hi</a>
Error: Template parse errors: Can’t bind to ‘foo’ since it isn’t a known property of ‘foo-component’…
This could be because you simply forgot to add the @Input
to FooComponent
:
@Input() someProp: any;
Or it could be caused by the same issue that would cause this error:
Error: Template parse errors: ‘foo-component’ is not a known element
If FooComponent
isn’t declared in any module yet, declare it in the module you are using it in:
@NgModule({
declarations: [ FooComponent ]
...
If FooComponent
comes from another module, say ModuleB
, and you are trying to use it in a component that belongs to ModuleA
, simply import ModuleB
in ModuleA
:
@NgModule({
imports: [ ModuleB ]
...
})
export class ModuleA {}
And make sure ModuleB
both declares and exports FooComponent
:
@NgModule({
declarations: [ FooComponent ],
exports: [ FooComponent ]
...
})
export class ModuleB {}
If you are trying to use FooComponent
in a lazy-loaded module you might have already imported its module, ModuleB
in AppModule
. That’s ok, you can import ModuleB
again in the lazy-loaded module.
But if any modules included in ModuleB
can only be in included once, you might see this error:
ModuleB is already loaded. Import it in the AppModule only.
In this case, find the module imported in ModuleB
which exports FooComponent
. Let’s call it ModuleC
. Then just import ModuleC
in your lazy-loaded module.
NullInjectorError: No provider for FooService!
This error can also happen for pipes. For both services and pipes, this error can be easily fixed by amending the decorator of your service, or including this additional decorator on your pipe:
@Injectable({
providedIn: 'root'
})
This will include the service or pipe in your root bundle. If the service isn’t required on the initial page load, you can avoid bloating your bundle by omitting the providedIn
value, and instead just providing the service or pipe in the modules that need it by including it in the providers array:
@NgModule({
...
providers: [ FooService, FooPipe ]
...
})
RouterModule.forRoot() called twice. Lazy loaded modules should use RouterModule.forChild() instead.
If this isn’t caused by actually calling RouterModule.forRoot()
twice (once in the AppModule
, and once in another module), then this is almost always caused by importing the AppModule
. You never import the AppModule
. It’s the root module that bootstraps your application.
Everything included in AppModule
is already available to other modules. However, in a lazy-loaded module, you will need to re-import things that are already imported in AppModule
. This is totally fine.
Error: Template parse errors: No provider for ControlContainer (“[ERROR ->]<form>
If you’re using a form tag in a template, and you are not using a FormGroup
, it will cause this error. Import the FormsModule
to make it go away, or alternatively, use a FormGroup
binding on your form tag, and import the ReactiveFormsModule
.
Error: ngIfElse must be a TemplateRef, but received ‘[object HTMLDivElement]’.
You may have tried this:
<div #showLoading><app-spinner></app-spinner></div>
But you need to use ng-template
like so:
<ng-template #showLoading><app-spinner></app-spinner></ng-template>
Error: Uncaught (in promise): EmptyError: no elements in sequence
This is an RxJs error. It can occur if you used first()
in an Observable
that had a takeUntil()
emit before first()
.
I hope this has helped you discover the cause of your error and how to fix it. If so, please give some claps! If I left anything out, feel free to respond in the comments, and I’ll add it on.