How do I stop duplicate api calls in angular - javascript

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.

Related

Sending calculated data from one component to another without Services

I want to send the value from one component to another, they are not related so all solutions are saying that I must use shared service to do that. But these services are using templates (if I'm right). Is there a way to do this sharing without services?
I want to send the BMI value from homepage.component.ts to result.component.ts.
homepage.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
constructor() { }
myHeight!:number;
myWeight!:number;
bmi!:number;
ngOnInit(): void {
}
onGenerate( height:string,width:string){
this.myHeight = +height;
this.myHeight=Math.pow(this.myHeight/100,2);
this.myWeight = +width;
this.bmi=this.myWeight/this.myHeight
console.log(this.bmi); //this is the calculated value to send
}
}
result.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-result',
templateUrl: './result.component.html',
styleUrls: ['./result.component.css']
})
export class ResultComponent implements OnInit {
constructor() { }
//I want to get the bmi here
ngOnInit(): void {
}
}
There are Two ways to communicate between unrelated components in angular:
1 - Through services, you have to understand where to inject it, in your case I think it should be injected in root, so try this with your service ( follow this tutorial to implement your service, just add my code instead of theirs )
#Injectable({
providedIn: 'root',
})
2 - Through a store ( a lot of boilerplate coding, to use if you have complexe states to keep synchronized through the whole app, by the way the store is basically a service )
If your components are not related then you can create a shared service between them. Then, you need to use dependency injection to communicate between these components. So, there is a great Angular tutorial which describes how to do it.
The service code would look like this:
#Injectable()
export class FooService {
constructor( ) { }
private yourData;
setData(data){
this.yourData = data;
}
getData(){
let temp = this.yourData;
this.clearData();
return temp;
}
}
and sender component:
import { Router } from '#angular/router';
import { FooService} from './services/foo.service';
export class SenderComponent implements OnInit {
constructor(
private fooService: FooService,
private router:Router) {}
somefunction(data){
this.fooService.setData(data);
this.router.navigateByUrl('/reciever');//as per router
}
}
and subscriber:
import { Router } from '#angular/router';
import { TransfereService } from './services/transfer.service';
export class RecieverComponent implements OnInit {
data;
constructor(
private fooService: FooService){
}
ngOnInit() {
data = this.transfereService.getData();
console.log(`data: `, data)
}
}
Solution: To pass the data from one component to another we can store it in a session storage or a local storage and then access it in other components from that storage. Here I have provided a sample code using local storage for your reference.
homepage.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
constructor() { }
myHeight!:number;
myWeight!:number;
data:string='';
bmi!:number;
ngOnInit(): void {
}
onGenerate( height:string,width:string){
this.myHeight = +height;
this.myHeight=Math.pow(this.myHeight/100,2);
this.myWeight = +width;
this.bmi=this.myWeight/this.myHeight;
this.data=localStorage.setItem('bmi',this.bmi);
console.log(this.bmi); //this is the calculated value to send
}
}
resultcomponent.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-result',
templateUrl: './result.component.html',
styleUrls: ['./result.component.css']
})
export class ResultComponent implements OnInit {
data:any;
constructor() { this.data=localstorage.getItem('bmi')}
//Access the bmi using the data variable here
ngOnInit(): void {
}
}

How do I push data from a Subject observable to an array in the component?

I am trying to push a message into an array that is already declared as a variable in the component. I am using a service and have created a subject observable to take data from one component and inject it into another component. When I try to push the data onto the array after subscribing to the variable, it's updated temporarily but when I open that component, the data is not pushed. The array updates when I console log from inside the subscribe method but it's reset once I open that component. I don't know what is the problem. This is the code:
Service.ts
import { Injectable } from '#angular/core';
import { User } from './user';
import { Subject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class SerService {
private message = new Subject<string>();
sourceMessage$ = this.message.asObservable();
constructor() { }
sendMessage(message: string) {
this.message.next(message);
}
}
Receiver component
import { Component, OnInit } from '#angular/core';
import { SerService } from '../ser.service';
import { User } from "../user";
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
public messages = ['hi', 'hello', 'bye'];
constructor(private _service: Service) { }
ngOnInit() {
this._service.message$
.subscribe(
message => {
this.messages.push(message);
}
);
}
}
Sender Component
import { Component, OnInit } from '#angular/core';
import { SerService } from '../ser.service';
import { User } from '../user';
#Component({
selector: 'app-sign-up',
templateUrl: './sign-up.component.html',
styleUrls: ['./sign-up.component.css']
})
export class SignUpComponent {
userModel = new User('', '', '', '', false);
constructor (private _service : SerService) {}
onSubmit(){
this._service.sendMessage(this.userModel.message);
}
}
I can't update the message array. How do I do this with minimal changes?
You can create a service to send data from one component to another by using BehaviourSubject
Service:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable({
providedIn: 'root'
})
export class DataService {
private userDetails = new BehaviorSubject<any>('');
currentUserDetails = this.userDetails.asObservable();
constructor() { }
sendUserDetails(message){
this.userDetails.next(message)
}
}
Sender Component:
import { DataService } from '/services/data.service';
export class SignupComponent implements OnInit {
public userDetails;
constructor(private _dataService: DataService) {}
ngOnInit(){
userDetails = new User('', '', '', '', false);
this._dataService.sendUserDetails(this.userDetails);
}
}
Receiver Component
import { DataService } from '/services/data.service';
export class LoginComponent implements OnInit {
public userDetails;
constructor(private _dataService: DataService) {}
ngOnInit(): void {
this._dataService.currentUserDetails.subscribe(userDetails => this.userDetails = userDetails);
}
Blockquote

event after asyn function finished in typescript

hi i made a simple ionic app to connect to an api as the instruction at here :
https://www.freecodecamp.org/news/how-to-build-your-first-ionic-4-app-with-api-calls-f6ea747dc17a/
u have a service like this:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class MovieService {
url = 'http://www.omdbapi.com/';
apiKey = '';
constructor(private http: HttpClient) { }
searchData(title: string): Observable<any> {
return this.http.get(`${this.url}?s=${encodeURI(title)}&apikey=${this.apiKey}`).pipe(
map(results => results['Search'])
);
}
}
also I have another page.ts which works like this:
import { MovieService, SearchType } from './../../services/movie.service';
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs';
#Component({
selector: 'app-movies',
templateUrl: './movies.page.html',
styleUrls: ['./movies.page.scss'],
})
export class MoviesPage implements OnInit {
results: Observable<any>;
searchTerm: string = '';
constructor(private movieService: MovieService) { }
ngOnInit() { }
doSearch() {
this.results = this.movieService.searchData(this.searchTerm);
}
}
how can I launch an event like for example navigate to another page when doSearch finished it's job?
because if i use like this:
doSearch() {
this.results = this.movieService.searchData(this.searchTerm);
alert("fin");
}
the alert show up although the result job is still working
You just need to make desirable changes inside subscribe method.
doSearch() {
this.movieService.searchData(this.searchTerm).subscribe(result
=> {
alert("fin");
});
}

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';

Angular 5 - HttpClient Service - Component not getting data

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

Categories