How to synchronise Angular2 http get? - javascript

I understand using observable I can execute a method when the request is completed, but how can i wait till a http get is completed and return the response using in ng2 http?
getAllUser(): Array<UserDTO> {
this.value = new Array<UserDTO>();
this.http.get("MY_URL")
.map(res => res.json())
.subscribe(
data => this.value = data,
err => console.log(err),
() => console.log("Completed")
);
return this.value;
}
the "value" will is null when its returned because get is async..

your service class: /project/app/services/sampleservice.ts
#Injectable()
export class SampleService {
constructor(private http: Http) {
}
private createAuthorizationHeader() {
return new Headers({'Authorization': 'Basic ZXBossffDFC++=='});
}
getAll(): Observable<any[]> {
const url='';
const active = 'status/active';
const header = { headers: this.createAuthorizationHeader() };
return this.http.get(url + active, header)
.map(
res => {
return res.json();
});
}
}
your component: /project/app/components/samplecomponent.ts
export class SampleComponent implements OnInit {
constructor(private sampleservice: SampleService) {
}
ngOnInit() {
this.dataset();
}
dataset(){
this.sampleservice.getAll().subscribe(
(res) => {
// map Your response with model class
// do Stuff Here or create method
this.create(res);
},
(err) => { }
);
}
create(data){
// do Your Stuff Here
}
}

By looking at the angular source (https://github.com/angular/angular/blob/master/packages/http/src/backends/xhr_backend.ts#L46), it is apparent that the async attribute of the XMLHttpRequest is not getting used. The third parameter of XMLHttpRequest needs to be set to "false" for synchronous requests.

Please find code for your problem
Below is component and service file.And Code is Working fine for synchornize
import { Component, OnInit } from '#angular/core';
import { LoginserviceService } from '../loginservice.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
model:any={};
constructor(private service : LoginserviceService) {
}
ngOnInit() {
}
save() {
this.service.callService(this.model.userName,this.model.passWord).
subscribe(
success => {
if(success) {
console.log("login Successfully done---------------------------- -");
this.model.success = "Login Successfully done";
}},
error => console.log("login did not work!")
);
}
}
Below is service file..
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { UserData } from './UserData';
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/toPromise'
import {Observable} from 'rxjs/Rx'
#Injectable()
export class LoginserviceService {
userData = new UserData('','');
constructor(private http:Http) { }
callService(username:string,passwrod:string):Observable<boolean> {
var flag : boolean;
return (this.http.get('http://localhost:4200/data.json').
map(response => response.json())).
map(data => {
this.userData = data;
return this.loginAuthentication(username,passwrod);
});
}
loginAuthentication(username:string,passwrod:string):boolean{
if(username==this.userData.username && passwrod==this.userData.password){
console.log("Authentication successfully")
return true;
}else{
return false;
}
}
}

Another solution would be to implement a priority queue of sort.
From what I understand http requests do not get executed until you add subscribers. Therefore, you can do something like this:
Observable<Response> observable = http.get("/api/path", new RequestOptions({}));
requestPriorityQueue.add(HttpPriorityQueue.PRIORITY_HIGHEST, observable,
successResponse => { /* Handle code */ },
errorResponse => { /* Handle error */ });
This assumes that requestPriorityQueue is a service injected into your component. The priority queue would store entries in an array in the following format:
Array<{
observable: Observable<Response>,
successCallback: Function,
errorCallback: Function
}>
You would have to decide how the elements are added to your array. Finally, the following will happen in the background:
// HttpPriorityQueue#processQueue() called at a set interval to automatically process queue entries
The processQueue method would do something like this:
protected processQueue() {
if (this.queueIsBusy()) {
return;
}
let entry: {} = getNextEntry();
let observable: Observable<Response> = entry.observable;
this.setQueueToBusy(); // Sets queue to busy and triggers an internal request timeout counter.
observable.subscribe()
.map(response => {
this.setQueueToReady();
entry.successCallback(response);
})
.catch(error => {
this.setQueueToReady();
entry.errorCallback(error);
});
}
If you are able to add new dependencies you could try using the following NPM package: async-priority-queue

I looked and I couldn't find any way to make an HTTP call sync instead of async.
So the only way around this: wrap your call in a while loop with a flag. Don't let the code continue until that flag has "continue" value.
Pseudo code as follows:
let letsContinue = false;
//Call your Async Function
this.myAsyncFunc().subscribe(data => {
letsContinue = true;
};
while (!letsContinue) {
console.log('... log flooding.. while we wait..a setimeout might be better');
}

as you see, first callback waiting for a data from request and
there you can go on with your logic (or use the third one)
example:
.. subscribe( data => {
this.value = data;
doSomeOperation;
},
error => console.log(error),
() => {console.log("Completed");
or do operations here..;
}
});

How about to use $.ajax(of jQuery) or XMLHttpRequest.
It can use as asynchornize.

You should not try to make http calls behave synchronously. Never a good idea.
Coming to your getAllUser implementation it should return an observable from the function and the calling code should subscribe instead of you creating a subscription inside the method itself.
Something like
getAllUser(): Observable<UserDTO> {
return this.http.get("MY_URL")
.map(res => res.json());
}
In you calling code, you should subscribe and do whatever you want.

Related

how to use angular pipe and subscribe correctly in request call

In my angular application I am sending a request to my backend to check credentials, after success the backend sends an token which I read. So far this works, but I had to use an pipe to make it map to a method and then make it work. But my problem now it even though I am getting 200 from the server my page will not navigate to the protected page automatically. If I enter the url manually it works this is what I tried:
authenticateUser(login: LoginModel){
this.http = new HttpClient(this.handler)
return this.http.post<JwtToken>(environment.rootUrl + 'api/authenticate', {
username: login.username,
password: login.password,
}).pipe(map(response => this.authenticateSuccess(response)))
.subscribe({
next: () => {
this.isAuthenticated = true;
this.router.navigate(['/dashboard'])
}, error: (error) => {
this.isAuthenticated = false;
console.log(error)
}
})
}
It does not enter the subscribe part after the pipe. Is there any way to make this work? I still want to have an error handling like if no error then navigate to url if error do not navigate.
EDIT:
AuthenticateSuccess method:
isUserLoggedIn(){
return !! localStorage.getItem('authenticationToken')
}
private authenticateSuccess(response: JwtToken): void {
const jwt = response.id_token;
localStorage.setItem('authenticationToken' , jwt)
this.localStorageService.store('authenticationToken', jwt);
console.log(this.localStorageService.retrieve('authenticationToken'))
this.sessionStorageService.clear('authenticationToken');
}
Authguard:
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private auth: AuthenticationService,
private router: Router
) {
}
canActivate(): Promise<boolean> {
return new Promise(resolve => {
if (this.auth.isUserLoggedIn()) {
resolve(true)
} else {
this.router.navigate(['authenticate'])
resolve(false)
}
})
}
}
SOLUTION:
authenticateUser(login: LoginModel) {
this.http = new HttpClient(this.handler)
return this.http.post<JwtToken>(environment.rootUrl + 'api/authenticate', {
username: login.username,
password: login.password,
}).subscribe({
next: response => {
this.isAuthenticated = true;
this.authenticateSuccess(response)
this.router.navigate(['/dashboard'])
}, error: (err) => {
console.log(err)
}, complete: () => {
console.log("finished without worry")
}
})
}
RxJs map operator is supposed to modify the content of an observable. The map operator however needs to return the same observable or another observable, for the next subscribe operation to be able to function.
In your case your map operator does not return any observable at all and therefore the subscribe method has no reason to be triggered.
You could simple return the response again in your method here
private authenticateSuccess(response: JwtToken): any {
const jwt = response.id_token;
localStorage.setItem('authenticationToken' , jwt)
this.localStorageService.store('authenticationToken', jwt);
console.log(this.localStorageService.retrieve('authenticationToken'))
this.sessionStorageService.clear('authenticationToken');
return response;
}
but I think all the code of the map method matches better directly inside the subscribe method.

Displaying toastr messages sequentially in Angular

I update multiple records using the following update method and receive the updated and failed record count from result. Then I want to display related toastr message sequentially using Angular Material toastr. However, the following approach skip the success in #1 (or it is displayed back of the error) and display the error in #2. So, how can I display them sequentially for this method? Maybe I need to use RxJs for this purpose.
update() {
this.demoService.update(...).toPromise()
.then(result => {
if(result.success.count > 0){
// #1 display success toastr
}
if(result.failed.count > 0) {
// #2 display error toastr
}
}).catch(err => {
// #3 display error toastr related to other errors
});
}
definitelly you need to use Rxjs Observables instead of Promises if you work with Angular :d
So, your code will become:
constructor(private alertService: AlertService) {}
update() {
this.demoService.update(...).subscribe(result => {
if(result.success.count > 0) {
result.success.forEach(item => {
this.alertService.info(item);
await this.delay(2000);
}
}
if(result.failed.count > 0) {
result.failed.forEach(item => {
this.alertService.error(item);
await this.delay(2000);
}
}
}, err => {
this.alertService.error(err.message);
});
}
function delay(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
For each message, to not be override by the next, you need to await a time as the popup to be hidden. For this delay() function is used.
and the service class as an alert provider (and inject into your component):
import { Injectable } from '#angular/core';
import { ToastrService } from 'ngx-toastr';
#Injectable({
providedIn: 'root',
})
export class AlertService {
constructor(private toastr: ToastrService) {}
public success(message: string): void {
this.toastr.success(message, '', {
timeOut: 2000
});
}
public error(message: string): void {
this.toastr.error(message, '', {
timeOut: 2000
});
}
}

TypeScript : Value assigned inside HTTPClient scope is not accessible from outside (within the same function)

On Load, I'm calling the function getData() which will read & map the response to Data[] model and returns back the result. But the function returning undefined object though it's value is assigned.
import { HttpClient} from '#angular/common/http';
import {Data} from "../model";
export class Service {
constructor(private http: HttpClient) {}
getData():Data[]{
var data: Data[];
this.http.get(url,{ responseType: 'blob'}).subscribe(response => {
response.text().then(value => {
data = <Data[]>JSON.parse(value);
console.log(data); ----> {Printing the values as expected i.e., Data Array}
});
});
console.log(data); ----> {Printing Undefined}
return testData;
}
onLoad(){
var data:Data[] = this.getData();
console.log(data) ---------> {Printing Undefined}
}
}
Data Class :
export class Data{
id:number;
name:string;
value:string;
}
Http calls are async calls. Subsequent call will get executed and will not wait for the promise to be resolve.
Your dependent code should be written once it resolved.
I'll just show what I would like to do, here's a little bit reconstruction for your codes:
import {from, Observable} from 'rxjs'; // <= several imports you might care
import {map, switchMap} from 'rxjs/operators'; // <= several imports you might care
export class Service {
constructor(private http: HttpClient) {}
getData(): Observable<Data[]> {
return this.http.get(someurl, {responseType: 'blob'})
.pipe(
switchMap((blob: Blob) => {
return from(blob.text());
}),
map(text => {
return JSON.parse(text) as Data[];
})
);
}
onLoad(): void {
this.getData().subscribe((data: Data[]) => {
console.log(data); // <=== this is where you can log data
});
}
}
The basic idea is you should return a aync(aka Observable) result for your http request, and pipe the first result(type of Blob) to another Observable(which is built from a promise) of string value, then map it to a Data array at last, in onLoad you should subscribe the async result and check the data in subscription. In a word, you should keep the data flow asynchronously until it is been resolved

Late response from API

I am learning TypeScript and currently I am trying to use google API for books. Issue that I have, is that at the time when expect to get get a response I have null. However, I am logging response and data is presented console. Next strange this that, this data is logged and the end of all my code (like the last thing, but not when I am actually doing logging method).
Is there a way to wait for response? My code parts:
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { OrderPage } from '../order/order';
import { BooksApiProvider } from '../../providers/books-api/books-api';
#Component({
selector: 'page-book',
templateUrl: 'book.html'
})
export class BookPage {
books:any;
constructor(public navCtrl: NavController, public booksProvide: BooksApiProvider) {
}
moveToOrder() {
debugger;
this.books = this.booksProvide.getBook();
}
}
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
#Injectable()
export class BooksApiProvider {
constructor() {
}
getBook(){
fetch('https://www.googleapis.com/books/v1/volumes?q=isbn:9780439139601')
.then(res => res.json())
.then(data => console.log(data))
}
}
Your getBook() method doesn't return anything, therefore, this.books will always be null.
As you're using fetch here, you can return the promise in your getBook() method allowing you to perform any action with the data retrieved.
getBook(): Promise<any> {
return fetch('https://www.googleapis.com/books/v1/volumes?q=isbn:9780439139601')
.then(res => res.json())
.then(data => {
console.log(data);
return Promise.resolve(data);
});
}
Then you can call .then on getBook() to do whatever you want with the data.
(Assign the data to this.books for example)
this.booksProvide.getBook()
.then(data => this.books = data);
You're using Angular thought, you should take a look at the HttpClient. It's pretty much the same as fetch unless it deals with RxJS and Observables which are widely used in Angular.

Unsubscribe from Firestore stream - Angular

Recently I implemented Firebase into my Angular project and I have a question about unsubscribing from data stream.
When you use classic HTTP calls, observables from this are finite, but with the firestore, these are infinite, so you have to unsubscribe. But when I do so (after destroying component and log out), I still get an error in console and I can see that requests are still sending (or persisting).
In the network tab I can see this in the request timing:
Caution: request is not finished yet
This error pops up after I log out (Its probably caused because of my rules that I set)
FirebaseError: Missing or insufficient permissions
And also, I still get an error even if I use async pipe in angular.
Here is my current solution:
data.service.ts
items: Observable<any[]>;
constructor(db: AngularFirestore) {
this.items = db.collection("data").valueChanges();
}
getItems() {
return this.items;
}
home.component.ts
req: Subscription;
ngOnInit(): void {
this.req = this.dataService.getItems().subscribe(
res => {
console.log(res);
},
err => console.log(err),
() => console.log("completed")
);
}
ngOnDestroy(): void {
console.log("ya");
this.req.unsubscribe();
}
Thanks for your advices!
home.component.ts
import { takeWhile } from "rxjs/operators";
#Component({...})
export class HomeComponent implements OnInit, OnDestroy {
isAlive: boolean = true;
...
ngOnInit(): void {
this.dataService.getItems()
.pipe(takeWhile(() => this.isAlive))
.subscribe(res => {
console.log(res);
});
}
ngOnDestroy(): void {
this.isAlive = false;
}
}
takeWhile is what u needed
Use one of the rxjs operators, eg take (1) after taking the value, it unsubscribe by itself
this.dataService.getItems().pipe(take(1)).subscribe(
res => {
console.log(res);
},
err => console.log(err),
() => console.log("completed")
);

Categories