NullInjectorError: StaticInjectorError[JobcounterComponent -> FetchJobDataService] - javascript

I am getting this error as shown in the screenshot below.
I have studied several similiar questions here. Most common suggestion is 'Add your service to your app module's providers array'. As I am writing web components with Angular elements, I do not actively use the default app component. However, I have added die Injectable property providedIn: root to the services decorator. IMO, this should be equivalent to adding the service to app module's providers array.
Have got no ideas on how to fix this.
Best, Dropbear.
My fetch-job-data.service.ts file:
import { Injectable } from '#angular/core';
import {HttpClient, HttpClientModule} from '#angular/common/http';
import {count} from 'rxjs/operators';
import {Observable} from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class FetchJobDataService {
constructor(private _http: HttpClient) { }
getJobCount(jobCountUrl: string): Observable<{'count': number}> {
return this._http.get<{'count': number}>(jobCountUrl);
}
}
My jobcounter.component.js file:
import {Component, OnDestroy, OnInit} from '#angular/core';
import {FetchJobDataService} from '../../services/fetch-job-data.service';
import {takeUntil} from 'rxjs/operators';
import {Subject} from 'rxjs';
#Component({
templateUrl: './jobcounter.component.html',
styleUrls: ['./jobcounter.component.scss']
})
export class JobcounterComponent implements OnInit, OnDestroy {
public jobCount: {'count': number};
public jobDataUrl = 'assets/data/jobCount.json';
private complete$ = new Subject<void>();
constructor(private _fetchDataService: FetchJobDataService) {
console.log('Job counter initialized...');
}
ngOnInit() {
this._fetchDataService.getJobCount(this.jobDataUrl)
.pipe(
takeUntil(this.complete$)
)
.subscribe(
(jobCount) => {
this.jobCount = jobCount;
console.log('Job count: ' + jobCount);
}
);
}
ngOnDestroy() {
this.complete$.next();
this.complete$.complete();
}
}
Browser Console error message

Import HttpClientModule in app.module
Like this:
import { HttpClientModule } from '#angular/common/http';
#NgModule({
imports: [
...
HttpClientModule,
],
declarations: [
]
})

Related

Angular 7 shared service is not shared

I am new to angular and I am trying to pass data from one component(HomeComponent) to another component(ProfileComponent) after navigation.
I created a shared service(DataService).
I injected the service in both the HomeComponent and ProfileComponent but when I set the value of the message property in HomeComponent and try to retrieve it in the ProfileComponent the value is undefined because the DataService is not the same instance.
The DataService was registered in the AppModule in the providers array so it should be a shared service and always the same instance right?
Thanks in advance
DataService.ts
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class DataService {
message:string;
constructor() { }
}
HomeComponent.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../services/data/data.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor(private data:DataService) { }
ngOnInit() {
this.data.message = "hello world";
}
}
ProfileComponent.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../services/data/data.service';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
private message : string;//undefined
constructor(private data:DataService) { }
ngOnInit() {
this.message = this.data.message;
}
}
AppModule.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DataService } from './services/data/data.service';
import { HomeComponent } from './home/home.component';
import { ProfileComponent } from './profile/profile.component';
#NgModule({
declarations: [
AppComponent,
HomeComponent,
ProfileComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [DataService],
bootstrap: [AppComponent]
})
export class AppModule { }
I know it's a 2 year question, but Google puts it at the top of search results
Now, Angular docs are clearer about this (or just we can find out easier), it's called "Singleton Services"
The section that explains this "bug" is The ForRoot Pattern and it says:
"If a module defines both providers and declarations (components, directives, pipes), then loading the module in multiple feature modules would duplicate the registration of the service. This could result in multiple service instances and the service would no longer behave as a singleton."
To sum up, if you define this in your services (DataService.ts) the providedIn: root as follows
#Injectable({ providedIn: 'root' })
you need to avoid define the service as a provider on your components or modules.
AppModule.ts
...
imports: [
BrowserModule,
AppRoutingModule
],
providers: [DataService], // This line is the problem
bootstrap: [AppComponent]
....
Hope that helps to somebody and if need more documentation refer to Singleton Services' link
Each time you inject the service to your component, new instance is generated. However in this case i would recommend you to use BehaviorSubject as follows,
#Injectable()
export class SharedService {
private messageSource = new BehaviorSubject<string>("default message");
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
STACKBLITZ DEMO

Getting empty json data

I'm trying to get json data from the url, but i'm not able to get any.
It is just showing an empty array. What do you think that I'm missing
here?
service.ts
this is the service.ts. Im trying to get data from 'https://jsonplaceholder.typicode.com/posts'.
import { Injectable } from '#angular/core';
import{UserCreation} from '../../models/user-creation.model';
import{Observable} from 'rxjs/Observable';
import{of} from 'rxjs/observable/of';
import{catchError,map,tap} from 'rxjs/operators';
import{HttpClient,HttpHeaders} from '#angular/common/http';
const httpOptions={
headers:new HttpHeaders({'Content-Type':'application/json'})
};
#Injectable({
providedIn: 'root'
})
export class UserCreationService{
//Create constructor to get Http instance
constructor(private http:HttpClient) { }
private usersUrl:'https://jsonplaceholder.typicode.com/posts';
getUsers():Observable<UserCreation[]>{
return this.http.get<UserCreation[]>(this.usersUrl).pipe(
tap(receivedUsers
=>console.log(`receivedUsers=${JSON.stringify(receivedUsers)}`)),
catchError(error=>of([]))
);
}
app.component.ts
this is the component.ts file
import { Component, OnInit } from '#angular/core';
import { Http, Response } from '#angular/http';
import { UserCreationService } from '../../common/services/user-
creation.service';
#Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app-component.css']
})
export class AppComponent implements OnInit {
allUsers: UserCreation[];
constructor(private userService: UserCreationService) { }
getUsersFromServices():void{
this.userService.getUsers().subscribe(
(Users)=>{
this.allUsers=Users;
console.log(`this.allUsers = ${JSON.stringify(this.allUsers)}`);
}
)
}
ngOnInit(): void {
this.getUsersFromServices();
}
There is typo error here - private usersUrl:'https://jsonplaceholder.typicode.com/posts';.
It should be = instead of : like this - private usersUrl='https://jsonplaceholder.typicode.com/posts';.
Or better way private usersUrl:string = 'https://jsonplaceholder.typicode.com/posts';

http service hit not working in Angular

I am using JSONPlaceholder to get data in service, but I am unable to get data at all. Please, help me out.
user.component.html
<p (click)="getUsers()">Click Me!</p>
<ul *ngFor="let x of users">
<li>{{x.name}}, {{x.age}}</li>
</ul>
user.component.ts
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { DataService } from '../../services/data.service';
import { Http } from '#angular/http';
#Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
constructor(private http:Http) { }
ngOnInit() {
}
getUsers(){
console.log(this.http.get("https://jsonplaceholder.typicode.com/posts"));
}
}
app.module.ts
//Basic File Inclusions
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
//Additional files inclusion
import { AppComponent } from './app.component';
import { UserComponent } from './components/user/user.component';
import { DataService } from './services/data.service';
import { Http } from '#angular/http';
#NgModule({
declarations: [
AppComponent,
UserComponent
],
imports: [
BrowserModule
],
providers: [ Http ],
bootstrap: [AppComponent]
})
export class AppModule { }
Please, can someone help me out making a successfull service call via http and get data on console.
Import
import { HttpModule } from '#angular/http';
in app.module.ts
imports: [HttpModule]
Rest of code will be same as you posted.
calling http like
this.http.get(`https://jsonplaceholder.typicode.com/posts`).subscribe(
data => {
console.log(data)
});
user.component.ts
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { DataService } from '../../services/data.service';
import { Http } from '#angular/http';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
constructor(private http: HttpClient) { }
ngOnInit() {
}
this.http.get(`https://jsonplaceholder.typicode.com/posts`).subscribe(
data => {
console.log(data)
});
}
app.module.ts
//Basic File Inclusions
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
//Additional files inclusion
import { AppComponent } from './app.component';
import { UserComponent } from './components/user/user.component';
import { DataService } from './services/data.service';
import { Http } from '#angular/http';
import { HttpModule } from "#angular/http";
#NgModule({
declarations: [
AppComponent,
UserComponent
],
imports: [
BrowserModule,
HttpModule
],
providers: [ Http ],
bootstrap: [AppComponent]
})
export class AppModule { }
Hope this may help you..
You are just calling http.get(url) and expecting something in return is like calling ajax method without success and error callback methods.
Kindly check Http documentation and usage of get and post methods
Mistake/Wrong Assumptions:
this.http.get(https://jsonplaceholder.typicode.com/posts) will not return http response which are expecting
Reality/Correct Approach:
You can use either pipe(can be used in the service) or subscribe(can be used in Component) method on http's get method whose return type is Observable.
Based on your requirement, you can use either of them
http.get('https://jsonplaceholder.typicode.com/posts')
// Call map on the response observable to get the parsed people object
.pipe(map(res => res.json()))
// Subscribe to the observable to get the parsed people object and attach it to the
// component
.subscribe(posts => this.posts = posts)
Hence your component code becomes:
user.component.ts
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { DataService } from '../../services/data.service';
import { Http } from '#angular/http';
#Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
constructor(private http:Http) { }
ngOnInit() {
}
getUsers(){
this.http.get("https://jsonplaceholder.typicode.com/posts")
.subscribe(posts=> console.log(posts))
}
}

Cyclic Dependency in Angular 4 while Opening one modal component from another

In my app, I want to open a modal popup for user to upload a file. So I used below code for it (Used angular material to open the popup):
Actual service call happens after I upload document and if uploaded wrong document then service respond with error message.
What I want to achieve is If user select incorrect document I want to show another popup (Error Modal popup).
However when I import dialog.service.ts in uploaddoc.component.ts gives me below error
Can't resolve all parameters for UploaddocComponent
also throws warning in console saying :
WARNING in Circular dependency detected:
src\app\dialog.service.ts ->
src\app\uploaddoc\uploaddoc.component.ts ->
src\app\dialog-service.service.ts
WARNING in Circular dependency detected:
src\app\uploaddoc\uploaddoc.component.ts ->
src\app\dialog.service.ts ->
src\app\uploaddoc\uploaddoc.component.ts
Note : UploaddocComponent and ErrorModalComponents are both added in entryComponents array in app.module.ts as both are dynamic components.
Below is my code (and reproduced in stackblitz)
Main Component(to open upload popup ):
HTML
<button type="button" (click)="openUpload()">Open Upload Popup</button>
Component.ts
import { Component } from '#angular/core';
import { MatDialog } from '#angular/material';
import { DialogService } from './dialog.service';
import { ErrorModalComponent } from './error-modal/error-modal.component';
import { UploaddocComponent } from './uploaddoc/uploaddoc.component';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular 5';
constructor(public dialog: MatDialog,private dialogsService: DialogService){
}
public openUpload(){
this.dialogsService.openUploadDialog(UploaddocComponent);
}
}
My dialog.service.ts
import { Injectable } from '#angular/core';
import { ErrorModalComponent } from './error-modal/error-modal.component';
import { UploaddocComponent } from './uploaddoc/uploaddoc.component';
import { MatDialogRef, MatDialog, MatDialogConfig } from '#angular/material';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class DialogService {
constructor(private dialog: MatDialog) { }
public infoPopup(): Observable<boolean> {
let dialogRef: MatDialogRef<ErrorModalComponent>;
dialogRef = this.dialog.open(ErrorModalComponent);
dialogRef.componentInstance.data = "error";
return dialogRef.afterClosed();
}
public openUploadDialog(data: Object): Observable<boolean> {
let dialogRef: MatDialogRef<UploaddocComponent>;
dialogRef = this.dialog.open(UploaddocComponent);
dialogRef.componentInstance.data = data;
return dialogRef.afterClosed();
}
}
upload.component.ts
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { delay } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';
import { DialogService } from '../dialog.service';
import { ErrorModalComponent } from '../error-modal/error-modal.component';
#Component({
selector: 'app-uploaddoc',
templateUrl: './uploaddoc.component.html',
styleUrls: ['./uploaddoc.component.css']
})
export class UploaddocComponent implements OnInit {
constructor(public dialogService: DialogService) { }
data: any;
ngOnInit() {
}
public uploadDoc() {
//in this method actual service call happens and check if correct document is uploaded or not.
// Service side sends error if wrong document is uploaded.
// If wrong doc is uploaded then I want to display Error component here
// I will simulate service call here with delay and will open ErrorModal
of(['some data']).pipe(
delay(2000)
).subscribe((res)=>{
console.log(res);
// suppose error occured here then I want to open error modal So I added `dialog.service.ts` here in this component
this.dialogService.infoPopup();
})
}
}
upload.component.html
<p>
Upload popup works
<button type="button" (click)="uploadDoc()">Do upload</button>
</p>
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule } from '#angular/forms';
import { Components } from './materialComponents';
import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import { DialogService } from './dialog.service';
import { UploaddocComponent } from './uploaddoc/uploaddoc.component';
import { ErrorModalComponent } from './error-modal/error-modal.component';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
#NgModule({
imports: [BrowserModule, FormsModule, ...Components,BrowserAnimationsModule],
declarations: [AppComponent, HelloComponent, UploaddocComponent, ErrorModalComponent],
bootstrap: [AppComponent],
entryComponents: [UploaddocComponent, ErrorModalComponent],
providers: [DialogService]
})
export class AppModule { }
I am not sure How should I handle circular dependency.
I may not have understood ngModule completely but guessing; Not able to inject service in components added in entryComponents array in app.module.ts.
What I am doing wrong here?
Well I tried below approach:
Modified uploaddoc.component.ts to :
import { Component, OnInit, Injector } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { delay } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';
import { DialogService } from '../dialog.service';
import { ErrorModalComponent } from '../error-modal/error-modal.component';
#Component({
selector: 'app-uploaddoc',
templateUrl: './uploaddoc.component.html',
styleUrls: ['./uploaddoc.component.css']
})
export class UploaddocComponent implements OnInit {
constructor(private injector: Injector) {
this.dialogsService = this.injector.get(DialogsService);
}
data: any;
ngOnInit() {
}
public uploadDoc() {
of(['some data']).pipe(
delay(2000)
).subscribe((res)=>{
console.log(res);
// suppose error occured here then I want to open error modal So I added `dialog.service.ts` here in this component
this.dialogService.infoPopup();
})
}
}
I have used injector from #angular/core to explicitly get the service instance and no more error now.
however I can still see warnings. To remove warning I have added following in .angular-cli.json
"defaults": {
....
"build": {
"showCircularDependencies": false
}
}

Creating a angular2 service that displays a processing overlay

I am trying to create a reusable component that serves as a processing overlay when making asynchronous calls across my site. I have a service in place but the OverlayComponent doesn't seem to get invoked when showOverlay is invoked:
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { HashLocationStrategy, LocationStrategy } from '#angular/common';
import { HttpModule } from '#angular/http';
import { AppRoutingModule } from './app-routing.module';
import { MainComponent } from './app.mysite.component';
import { OverlayComponent } from './app.mysite.overlay.component';
import { TrackerComponent } from './pages/tracker/mysite.tracker.component';
import { OverlayService } from "./overlay.service";
#NgModule({
imports: [ BrowserModule, AppRoutingModule, HttpModule ],
declarations: [
MainComponent,
OverlayComponent,
NavbarComponent,
TrackerComponent,
],
providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}, OverlayService],
bootstrap: [ MainComponent ]
})
export class AppModule { }
TrackerComponent.ts
import { Component, OnInit } from '#angular/core';
import { OverlayService } from '../../overlay.service.js';
#Component({
moduleId: module.id,
selector: 'tracker-component',
templateUrl: '/public/app/templates/pages/tracker/mysite.tracker.component.html',
providers: [ OverlayService]
})
export class TrackerComponent implements OnInit{
constructor(private http: Http, private overlayService: OverlayService) {
}
ngOnInit(): void {
this.overlayService.showOverlay('Processing...'); //This kicks everything off but doesn't show the alert or overlay
this.overlayService.test(); //does exactly what i'd expect
}
}
overlay.service.ts
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class OverlayService {
private message: string;
private subject: Subject<any> = new Subject<any>();
showOverlay(msg: string) : void { //When this gets invoked, shouldn't it be invoking a change to this.subject and therefore invoking getMessage()
this.message = msg;
this.subject.next(msg);
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
test() {
return 'test good'; //if I call this function, it works
}
}
app.mysite.overlay.component
import { Component, OnInit } from '#angular/core';
import { OverlayService } from './overlay.service';
#Component({
selector: 'overlay-component',
templateUrl: '/public/app/templates/mysite.overlay.component.html',
styleUrls: ['public/app/scss/overlay.css'],
providers: [OverlayService]
})
export class OverlayComponent implements OnInit {
private processingMessage: string;
constructor(private overlayService: OverlayService) {}
ngOnInit() {
this.overlayService.getMessage().subscribe((message: string) => { //since i'm subscribed to this, i'm expecting this to get called. It doesn't
this.processingMessage = message;
alert(this.processingMessage); //never gets hit
$('.overlay-component-container').show(); // never gets hit
},
error => {
alert('error');
})
}
}
Specifying providers in the Component metadata actually creates a new injectable, scoped to that component tree.
If you want to share the overlay service across the app, you'll need to declare the overlay provider in the NgModule, and not in the components. Alternatively, you can declare it only as a provider on the top-level entry component (eg. AppComponent), though it may cause confusion when used in other entry components/lazy-loaded modules.
See https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html for a better explanation

Categories