Angular 6+ providedIn: 'root' raising a StaticInjectorError - dependency-injection

I have googled extensively and I can't seem to find anyone else with this issue, so I must be missing something. I am converting all of my AppModule services to use the providedIn: 'root' method, but it doesn't seem to be working.
import { Injectable } from '#angular/core';
Injectable({
providedIn: 'root'
})
export class CommonService{
UserName : string = 'Guest';
Roles : Array<any> = [];
Theme: string = 'standard';
constructor(){}
}
Here is one of the components that uses the service:
import { CommonService } from './Services/common.service';
#Component({
selector: 'navBar',
templateUrl: './navbar.html'
})
export class NavBar {
constructor(private session: CommonService) {}
At runtime, this is the error in the console:
StaticInjectorError(AppModule)[NavBar -> CommonService]:
StaticInjectorError(Platform: core)[NavBar -> CommonService]:
NullInjectorError: No provider for CommonService!
I've checked the documentation, and I don't see where I am going wrong. What am I missing?
Forgot to mention, NavBar is a component declared in SharedModule, SharedModule is imported in AppModule.

You may be missing the leading "#" on Injectable:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class CommonService{
UserName : string = 'Guest';
Roles : Array<any> = [];
Theme: string = 'standard';
constructor(){}
}
There may not be an error in the IDE, but that looks like it might be the issue

Related

Angular upgrade from 8 to 9, Can't resolve all parameters for any injectable

I recently upgrade form Angular 8 to Angular 9 and it appears that nothing can be dependency injected.
I've commented out so much code in my project to something as basic as the following:
// configuration.state.ts
#State<ConfigurationStateModel>({
name: 'ConfigurationStateModel',
defaults: defaultConfiguration,
})
export class ConfigurationState {
constructor(
//private configurationService: ConfigurationService,
private configurationService: HeroService,
) { }
// .. irrelevant code
}
//hero.service.ts
// generated by ng g s heroservice
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class HeroService {
constructor() { }
}
Errors with
Error: Can't resolve all parameters for ConfigurationState: (?).
at getUndecoratedInjectableFactory (core.js:17311)
at injectableDefOrInjectorDefFactory (core.js:17295)
at providerToFactory (core.js:17363)
at providerToRecord (core.js:17345)
at R3Injector.processProvider (core.js:17161)
at core.js:17122
at core.js:1400
at Array.forEach ()
at deepForEach (core.js:1400)
at R3Injector.processInjectorType (core.js:17118)
Apparently with NGXS, all States must be now decorated with the #Injectable().
This does not work:
#Injectable()
#State<ConfigurationStateModel>({
name: 'ConfigurationStateModel',
defaults: defaultConfiguration,
})
export class ConfigurationState {
but adding it after #State does:
#State<ConfigurationStateModel>({
name: 'ConfigurationStateModel',
defaults: defaultConfiguration,
})
#Injectable()
export class ConfigurationState {

Angular MatSnackBar not working from custom class

I am trying to create a toast using Material Snackbar from a custom class.
I am getting an error in my custom class ( Unable to find open from undefined. ) but working fine in user.service.ts
If ngZone is used, then i am getting an error, (unable to find run from undefined)
Note: In ErrorHandler Class
console.log(this.snackBar) // gives undefined
app.module.ts
providers: [ErrorHandler],
bootstrap: [AppComponent]
})
export class AppModule { }
User Service
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { catchError } from 'rxjs/operators';
import { environment } from '../../environments/environment';
import { ErrorHandler } from '../classes/error-handler';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class UserService {
private url = environment.api+'/login';
constructor(private http: HttpClient, private eh:ErrorHandler, private sb: MatSnackBar) { }
login(credentials){
this.sb.open("hello world"); // Working Fine
return this.http.post(this.url, {})
.pipe(
catchError(this.eh.handleError)
);
}
}
Error Handler Class
import {Component, Injectable, NgZone} from '#angular/core';
import { HttpErrorResponse } from '#angular/common/http';
import { throwError } from 'rxjs';
import * as _ from 'lodash';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class ErrorHandler {
constructor (private snackBar: MatSnackBar) {}
public handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error("Error code working")
console.log(this.snackBar) // gives undefined
this.snackBar.open("Hello world"); // Throwing Error
}
// return an observable with a user-facing error message
return throwError('Something bad happened; please try again later.');
};
}
Thanks to all. I fixed it. Unfortunately i missed the stackoverflow link.
Change in
catchError((res) => this.eh.handleError(res))
did the trick
UserService
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { catchError } from 'rxjs/operators';
import { environment } from '../../environments/environment';
import { ErrorHandler } from '../classes/error-handler';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class UserService {
private url = environment.api+'/login';
constructor(private http: HttpClient, private eh:ErrorHandler, private sb: MatSnackBar) { }
login(credentials){
this.sb.open("hello world"); // Working Fine
return this.http.post(this.url, {})
.pipe(
catchError((res) => this.eh.handleError(res)) // change in this line
);
}
}
I stumbled upon the same issue in my application a few days ago. The reason is the context of this.
In
This.sb.open("hello world"); // Working Fine
the context of this is the UserService class. While in
this.snackBar.open("Hello world"); // Throwing Error
the context of this changed. Probably to CatchSubscriber.
You already mentioned that:
catchError((res) => this.eh.handleError(res)) // change in this line
resolves the error. Because it changed the context of this back to the UserService class. An alternative solution is to use the arrow syntax for the whole login():
login = (credentials) => {
// your code goes here
}

Angular 2 - No provider for Service

I've been trying to troubleshoot a strange problem with angular 2 where it isn't detecting my provider declaration, but nothing is working. I can't even replicate it in plunkr.
I'm using angular 2 rc 3 with router 3.0 alpha.8.
Error message is: ORIGINAL EXCEPTION: No provider for TestService!
app.routes.ts:
import { provideRouter, RouterConfig } from '#angular/router';
import { HomeComponent } from './app/home/home.component';
import { LogInComponent } from './app/log-in/log-in.component';
import { SignUpComponent } from './app/sign-up/sign-up.component';
export const routes: RouterConfig = [
{ path: '', component: HomeComponent },
{ path: 'log-in', component: LogInComponent },
{ path: 'sign-up', component: SignUpComponent }
];
export const APP_ROUTER_PROVIDERS = [
provideRouter(routes)
];
main.ts:
import { bootstrap } from '#angular/platform-browser-dynamic';
import { enableProdMode } from "#angular/core";
import { AppComponent } from './app/app.component';
import { APP_ROUTER_PROVIDERS } from './app.routes';
// enableProdMode();
bootstrap(AppComponent, [
APP_ROUTER_PROVIDERS
])
.catch(error => console.log(error));
app/app.component.ts:
import { Component } from '#angular/core';
import { ROUTER_DIRECTIVES } from '#angular/router';
import { TestService } from './shared/test.service';
#Component({
selector: 'my-app',
template: `
<div id="menu">
<a [routerLink]="['/sign-up']"><button>Sign Up</button></a>
</div>
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES],
providers: [TestService]
})
export class AppComponent {
constructor() { }
}
app/sign-up/sign-up.component.ts:
import { Component } from '#angular/core';
import { ROUTER_DIRECTIVES } from '#angular/router';
import { TestService } from '../shared/test.service';
#Component({
selector: 'sign-up',
template: `<h1>Sign up!</h1>`,
directives: [ROUTER_DIRECTIVES]
})
export class SignUpComponent {
constructor(private testService: TestService) {
this.testService.test('works?');
}
}
app/shared/test.service.ts:
import { Injectable } from '#angular/core';
#Injectable()
export class TestService {
constructor() { }
test(message: string) {
console.log(message);
}
}
So, I'm providing the testservice in the base component (app.component.ts) because I want all my components to access the same instance. However, when I navigate to sign-up, I get the no provider for testservice error. If I provide the TestService within the sign-up component, this then works:
import { Component, OnInit } from '#angular/core';
import { ROUTER_DIRECTIVES } from '#angular/router';
import { TestService } from '../shared/test.service';
#Component({
selector: 'sign-up',
template: `<h1>Sign up!</h1>`,
directives: [ROUTER_DIRECTIVES],
providers: [TestService]
})
export class SignUpComponent implements OnInit {
constructor(private testService: TestService) { }
ngOnInit() { }
}
However, I need the same instance accessible throughout my app, so how can I inject this at the main component level?
I even tried replicating this app-level service providing with plunkr with the same version of everything, but it doesn't seem to give me the same error...
http://plnkr.co/edit/5bpeHs72NrlyUITCAJim?p=preview
Injecting something on the app level is done in bootstrap:
main.ts:
import { TestService } from '../shared/test.service';
bootstrap(AppComponent, [
APP_ROUTER_PROVIDERS, TestService
])
For me, some references to the "services" folder were "Services". When I made them all "services" (lower case), it worked.
For example:
import {ApiService} from "./Services/api.service";
didn't work, but this worked:
import {ApiService} from "./services/api.service";
To All future readers - and this is correct for angular 2.0.0 rc-4:
make sure that you follow the below folder structure:
root:
index.html
package.json
systemjs.config.js
tsconfig.json (if using TypeScript)
typings.json
app (folder):
- main.js (the root script for your app)
- app.component.js (the root component for the entire app)
This is crucial for the hierarchical injection to properly scope and identify providers.
Also, and this is very important - if still encountering problems and you are using TypeScript or any other transpiled language- delete any artifacts which your transpiler produces for every associated class in the problematic object graph - this, and the OP's answer eventually helped in my case (*.map.js and *.js files deleted and re-transpiled).
It was a configuration issue after all, and a completely elusive one at that.
Per the ang2 style guide, I had my main.ts one folder up from my main app folder, and in systemjs.config I had to declare the main for app as '../main.js'. When I moved the main file to the root app folder and changed the package declaration in systemjs to 'main.js' it worked.
The odd thing is everything else worked, right up until I try to utilize hierarchical dependency injection.

How to instantiate/inject class declared in main component to another component

My AppComponent sample:
///other imports here
import { ApplyColor } from './../../shared/directives/applycolor';
import { SomeComponent} from './../../components/somecomponent';
#Component({
selector: 'my-app',
directives: [ApplyColor, ROUTER_DIRECTIVES],
providers: [ROUTER_PROVIDERS],
templateUrl: 'myurl.html'
})
#RouteConfig([
//routes here
{ path: '/main', name: 'Main', component: SomeComponent, useAsDefault: true },
])
export class AppComponent {
}
In order to instantiate ApplyColor in SomeComponent
Import ApplyColor
Add to directives: [ApplyColor]
Instantiate with a new keyword
Which is:
import {Component, AfterViewInit, ViewChild} from 'angular2/core';
import { ApplyColor } from './../../shared/directives/applycolor';
#Component({
selector: 'my-selector',
directives: [ApplyColor],
templateUrl: 'app/components/mycomponenturl.html'
})
export class MyComponent implements AfterViewInit {
constructor() { }
ngAfterViewInit() {
var color = new ApplyColor();
color.apply(2);
}
}
How can I instantiate/inject ApplyColor without there 3 steps above?
Directive instances are managed by Angular2. This means that you only need to specify it into the directives attribute. So if ApplyColor is a directive just add it into the directives attribute.
If ApplyColor isn't a directive, you can explicitly instantiate into the provide to the child component using #Input.
In your case, it's a bit particular since you leverage routing. In this case, you need to rely on a shared service. Such service needs to be defined when bootstrapping the application to be able to share a single instance for all components. You can set your instance of ApplyColor into a field of this service. So both component (AppComponent and SomeComponent) can access it.
Define the service
export class SharedService {
color:ApplyColor;
}
Bootstrapping the service
bootstrap(AppComponent, [ SharedService ]);
Set color from AppComponent
#Component({
(...)
})
export class AppComponent {
constructor(private service:SharedService) {
var color = new ApplyColor();
this.service.color = color;
}
}
Get color from SomeComponent
#Component({
(...)
})
export class AppComponent {
constructor(private service:SharedService) {
this.service.color.apply(2);
}
}
I have just started working angular2 but as I can understand:
import ApplyColor => you can't remove that, it required by the compiler to know which class you are referenced to
directives : [ApplyColor] => that means you will use the selector (the one you have defined in applycolor.ts) in the template (app/components/mycomponenturl.html). it is only to know where the component will be in the view.
new ApplyColor => you are creating the object yourself, it is not injected.
To inject your component,
export class MyComponent implements AfterViewInit {
constructor(private color:ApplyColor) { }
ngAfterViewInit() {
this.color.apply(2);
}
}
I hope it helped you ?

Angular2 simple DI not working

I have the following angular2 app with a simple dependency injected and it doesn't work. What am I missing?
Here's the error:
EXCEPTION: Cannot resolve all parameters for 'AppComponent'(?). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'AppComponent' is decorated with Injectable.
and the code:
import {Component} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
class DataService {
items: Array<any>;
constructor() {
this.items = [
{ name: 'Christoph Burgdorf' },
{ name: 'Pascal Precht' },
{ name: 'thoughtram' }
];
}
getItems() {
return this.items;
}
}
#Component({
selector: 'app',
providers: [DataService],
template: `
<ul>
<li *ngFor="#item of items">{{item.name}}</li>
</ul>
`
})
class AppComponent {
items: Array<any>;
constructor(dataService: DataService) {
this.items = dataService.getItems();
}
}
bootstrap(AppComponent, []);
Can't reproduce. I added your code to a Plunker
https://plnkr.co/edit/0DTjG5?p=preview
and it seems to work fine with or without #Injectable().
It is suggested to always add #Injectable() to services but it is only required when the service has constructor parameters.
.

Resources