I am using Angular 5 and trying to get some data from JsonPlaceholder.
First I created the service, then added:
import { HttpClientModule } from '#angular/common/http';
This is the service code:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class DataService {
private ROOT_URL = 'http://jsonplaceholder.typicode.com';
constructor(private http: HttpClient) {}
getPosts() {
this.http.get(`${this.ROOT_URL}/posts`).subscribe(data => {
return data;
});
}
}
And finally, on my app.component.ts:
import { Component, OnInit } from '#angular/core';
import { DataService } from '../../services/data.service';
#Component({
selector: 'app-root',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css']
})
export class AppComponent implements OnInit {
data;
constructor(private dataService: DataService) {
this.data = dataService.getPosts();
console.log(this.data;
}
ngOnInit() {
}
}
On the console it's just returning 'Undefined'
What I'm I doing wrong?
Don't subscribe on the service, return the observable and subscribe to it on the component. Because it is asynchronous your data variable on the component will be undefined because you assigned before the http request could resolve a value.
On the service:
getPosts() {
return this.http.get(`${this.ROOT_URL}/posts`);
}
On the coponent:
ngOnInit() {
this.dataService.getPosts().subscribe(posts => this.posts = posts);
}
Try following code snippet.
You are getting undefined because you assigning the data before the http request could resolve a value. Remove the subscription from service and move it to component.
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class DataService {
private ROOT_URL = 'https://jsonplaceholder.typicode.com';
constructor(private http: HttpClient) {}
getPosts() {
return this.http.get(`${this.ROOT_URL}/posts`);
}
}
AppComponent.ts
import { Component } from '#angular/core';
import {DataService} from './data.service';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
constructor(public data:DataService){
this.data.getPosts().subscribe(data=>{
console.log(data);
})
}
}
See the Demo here
import { HttpClientModule } from '#angular/common/http';
//This is the service code:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class DataService {
private ROOT_URL = 'http://jsonplaceholder.typicode.com';
constructor(private http: HttpClient) {}
getPosts() {
//if you want to use the observable which returns from .get()
//.. you need to do "return"
return this.http.get(`${this.ROOT_URL}/posts`);
}
}
//app.component.ts:
import { Component, OnInit } from '#angular/core';
import { DataService } from '../../services/data.service';
#Component({
selector: 'app-root',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css']
})
export class AppComponent implements OnInit {
data;
constructor(private dataService: DataService) {
this.data = dataService.getPosts();
//so if you want to use the value of your http call outside..
//..of the service here is a good place where to do subscribe()
this.data.subscribe(data => {
console.log(this.data;
});
}
ngOnInit() {
}
}
You expect a return value and returns nothing. Your return statement is placed inside a nested-lambda function, thus using "return" in the place you used it causes the inner function to return a value, and not the outer one as you needed.
I suggest you to read about asynchronous programming, and particularly about Observable (which works on the same concept of Promise) in Angular.
I basically agree to #Eduardo Vargas answer, but it might be also a good idea to do it in resolver, which will call api, and put data into route snapshot. Thanks to this it won't wait on empty page for loading the data on subscribe in constructor. More info here:
https://blog.thoughtram.io/angular/2016/10/10/resolving-route-data-in-angular-2.html
Related
Is it possible to trigger a function in another component from the current component with EventEmitter, as sort of a callback? For example, after I finish the API request a success function occurs, like so:
#Output() afterAPIRequest = new EventEmitter();
handleSuccess() {
this.afterAPIRequest.emit();
}
Now, can I catch that somehow in another component and trigger another function, something like this?
// when emitted, run this
refreshListIfEmitted() {
this.refreshMyList();
}
use a service
import { Injectable } from '#angular/core';
import { Observable, Subject } from 'rxjs';
#Injectable()
export class MessageService {
private _message: Subject<any>;
constructor() {
this._message = new Subject();
}
get changes(): Observable<any> {
return this._message.asObservable();
}
set message(message: any) {
this._message.next(message);
}
}
component one
import { Component } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-one',
templateUrl: './one.component.html',
styleUrls: ['./one.component.scss'],
})
export class OneComponent {
constructor(private _http: HttpClient, private _message: MessageService) { }
apiRequest(): void {
this._http.get('end-point').subscribe(value => this._message.message = value);
}
}
component two
import { Component } from '#angular/core';
#Component({
selector: 'app-two',
templateUrl: './two.component.html',
styleUrls: ['./two.component.scss'],
})
export class TwoComponent {
constructor(private _message: MessageService) {
this._message.changes.subscribe(value => console.log(value));
}
}
I have a very simple API call when clicking on minister router link. It displays some data when minister page is open. But I see whenever I came back to that page either from the homepage or any other page the API keeps loading again.
minister.component.ts
import { Component, OnInit } from '#angular/core';
import { ApiReadService } from "../apiReadService.service";
interface mydata{
allMinisters: Object
}
#Component({
selector: 'app-ministers',
templateUrl: './ministers.component.html',
styleUrls: ['./ministers.component.scss']
})
export class MinistersComponent implements OnInit {
allData:Object = [];
constructor(private apiRead: ApiReadService) {
}
ngOnInit(){
this.apiRead.getData().subscribe(data=>{
this.allData = data.allMinisters;
});
}
}
apiReadSerivce.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/Http';
#Injectable({
providedIn: 'root'
})
export class ApiReadService {
constructor(private http: HttpClient) { }
getData(){
return this.http.get('http://localhost:8081/allMinisters')
}
}
The simplest way - if you only ever want the data to load once is something like this:
import { shareReplay } from 'rxjs/operators';
export class ApiReadService {
constructor(private http: HttpClient) { }
ministers$ = this.http.get('http://localhost:8081/allMinisters')
.pipe(shareReplay(1))
getData() {
return this.ministers$;
}
}
Once the results have been returned once it will get 'cached' by the shareReplay(1) and that same value returned each time.
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';
I am brand new to Angular and I am attempting to make a simple http request for data. How do I access this data in my component? I am getting the error 'Cannot read property 'get' of undefined'
data.service.ts
import { Injectable } from '#angular/core'
import { HttpClient } from '#angular/common/http'
#Injectable()
export class FetchData {
private url: string = 'https://jsonplaceholder.typicode.com/users'
constructor(private http: HttpClient){}
get(){
return this.http.get(this.url).subscribe(data => {
console.log(data)
})
}
}
table.component.ts
import { FetchData } from './datatable.service';
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-datatable',
templateUrl: './datatable.component.html',
styleUrls: ['./datatable.component.css']
})
export class DatatableComponent implements OnInit {
Data: FetchData
constructor() { }
ngOnInit() {
this.Data.get()
}
}
You need to "Inject" the service in your component and also make the subscription in your component.
In your service you should "map" your response.
import { Injectable } from '#angular/core'
import { HttpClient } from '#angular/common/http'
import 'rxjs/add/operator/map';
#Injectable()
export class FetchData {
private url: string = 'https://jsonplaceholder.typicode.com/users'
constructor(private http: HttpClient){}
get(){
return this.http.get(this.url).map(data => {
return data.json();
})
}
}
Your component:
import { FetchData } from './datatable.service';
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-datatable',
templateUrl: './datatable.component.html',
styleUrls: ['./datatable.component.css']
})
export class DatatableComponent implements OnInit {
constructor(private fetchDataService: FetchData) { }
ngOnInit() {
this.fetchDataService.get().subscribe(res => {
console.log(response);
});
}
}
try this:
export class DatatableComponent implements OnInit {
constructor(private dataService: FetchData) { }
ngOnInit() {
this.dataService.get()
}}
I recently ran into a problem and can't really figure out what's wrong with my code at this point, hopefully someone of you can help me.
All I am trying to do is changing the value of my BehaviorSubject with a function but it isn't working out.
chat.service.ts
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class ChatService {
chatId = new BehaviorSubject<number>(0);
constructor() {
this.chatId.next(1);
}
changeChatId(chatId: number) {
console.log(chatId);
this.chatId.next(chatId);
}
}
So the subscribers get the default as well as the changed chatId from the constructor. But as soon as I try to change it with the changeChatId function nothing happens at all. The right id's get passed into the function I already debugged that but the line this.chatId.next(chatId) doesn't seem to do anything.
ADD
These are the other components the service is currently used in.
chat-message-list
import { Component, OnInit, Input} from '#angular/core';
import { ChatService } from "../../../shared/services/chat.service";
#Component({
selector: 'app-chat-message-list',
templateUrl: './chat-message-list.component.html',
styleUrls: ['./chat-message-list.component.css'],
providers: [ChatService]
})
export class ChatMessageListComponent implements OnInit {
chatId: number;
constructor(private chat: ChatService) { }
ngOnInit() {
this.chat.chatId.subscribe(
chatId => this.updateMessageList(chatId)
);
}
}
chat-item
import { Component, OnInit, Input} from '#angular/core';
import { User } from '../../../shared/models/user.model';
import { ChatService } from '../../../shared/services/chat.service';
#Component({
selector: 'app-chat-list-item',
templateUrl: './chat-list-item.component.html',
styleUrls: ['./chat-list-item.component.css'],
providers: [ChatService]
})
export class ChatListItemComponent implements OnInit {
#Input()
user: User;
constructor(private chat: ChatService) { }
ngOnInit() {
}
onChatItemSelected(){
this.chat.changeChatId(this.user.id);
}
}
You need to make your ChatService a singleton (shared) service. Add it to the providers of your ngModule. This allows all the components that use the ChatService to share the same service instance.
#NgModule({
providers: [ChatService]
})
And remove it from your components providers. When you are adding it to your components providers, that component gets its own instance of ChatService which can not be used by other components.