Waiting for subscription to complete setting a public variable - javascript

On my component, I get some translations from my service.
The service makes a call to an MVC API controller.
The code on the component:
private getTranslations(): void {
this._translationService.getTranslations('Foo');
}
The code on the service:
public translations: Translation[] = new Array<Translation>();
public getTranslations(action: string) {
this._http.get<Translation[]>(this.baseUrl + action).subscribe(
(result: Translation[]) => {
result.forEach(element => {
this.translations.push(element);
});
},
(error: any) => this._loggerService.logError(error)
);
}
In the service, it sets a value on the variable this.translations in a subscription.
How can I wait in my component for this to "complete" meaning that the public variable this.translations is set?
I saw a thread to wrap it in a new Promise() but I wasn't able to figure it out in my example. Any method can be used.

You can return an observable from your service:
public translations: Translation[] = new Array<Translation>();
public getTranslations(action: string) {
return new Observable((observer) => { // <-- return an observable
this._http.get<Translation[]>(this.baseUrl + action).subscribe(
(result: Translation[]) => {
result.forEach(element => {
this.translations.push(element);
});
observer.complete(); // <-- indicate success
},
(error: any) => {
this._loggerService.logError(error);
observer.error(error); // <-- indicate error
}
);
});
}
and subscribe in the component:
private getTranslations(): void {
this._translationService.getTranslations('Foo').subscribe({
error: () => { /* handle error */ },
complete: () => { /* handle complete */ },
});
}

Related

How to make sequential service call on success of first service response in Angular

I need to make multiple service call in angular one after other. need to pass the first
service call respose as input to another service.
Here is my component:
Demo(): any {
if (fileToUpload) {
this._voiceboxService.upload(fileToUpload)
.subscribe((res: any) => {
this.text=res.prediction
console.log(res);
});
}
else
console.log("FileToUpload was null or undefined.");
}
}
Here is my Service: i need to call all three service on success of one service and need to
pass first service resposnse as input for next service
upload(fileToUpload: any) {
let input = new FormData();
input.append("file", fileToUpload);
return this.http.post<any>('https://localhost:5001/', input)
language(data: any) {
return this.http.post<any>('https://localhost:5002', data)
}
getDetails(data: any) {
return this.http.post<any>('https://localhost:5003', data)
}
Use mergeMap.
I assume you want to do this in your component:
this._voiceboxService.upload(fileToUpload).pipe(mergeMap(upload =>
this._voiceboxService.language(upload)
.pipe(mergeMap(language => this._voiceboxService.getDetails(language))
))).subscribe((res: any) => {
this.text=res.prediction
console.log(res);
});
You can use map in the end organize your final value result.
You could use any of the RxJS higher order mapping operators like switchMap to map from one observable to another. You could find differences between different mapping operators here.
Service
upload(fileToUpload: any) {
let input = new FormData();
input.append("file", fileToUpload);
return this.http.post<any>('https://localhost:5001/', input).pipe(
switchMap(res => this.language(res)), // <-- `res` = response from previous request
switchMap(res => this.getDetails(res)) // <-- `res` = response from `this.language()`
);
}
language(data: any) {
return this.http.post<any>('https://localhost:5002', data)
}
getDetails(data: any) {
return this.http.post<any>('https://localhost:5003', data)
}
Component
Demo(): any {
if (fileToUpload) {
this._voiceboxService.upload(fileToUpload).subscribe({
next: (res: any) => { // <-- `res` = response from `getDetails()`
this.text = res.prediction
console.log(res);
},
error: (error: any) => {
// handle errors
}
});
} else {
console.log("FileToUpload was null or undefined.");
}
}

How to show spinner only if data are fetched from Http service?

I have to show a spinner only during http service call, and dismiss it when my component receives data.
I wrote a little cache service in order to fetch data from http service only the first time, and load that data from the cache during every other call, avoiding to call another time the http service.
The service is working as expected,but what if I'd like to show the spinner only during the http call and not when data are fetched from cache?
This is my component's code, it works when getReviewsCategory(this.id) method of my service calls http service, but when it fetches from cache the spinner is never dismissed.
Data are loaded in correct way in the background, but the spinner keeps going.
presentLoading() method is in ngOnInit so it's called everytime, what if I want to call it only when data are fetched from cache? How my component could know it?
ngOnInit() {
this.presentLoading();
this.CategoryCtrl();
}
CategoryCtrl() {
this.serverService.getReviewsCategory(this.id)
.subscribe((data) => {
this.category_sources = data['value'];
this.stopLoading();
});
}
async presentLoading() {
const loadingController = this.loadingController;
const loadingElement = await loadingController.create({
spinner: 'crescent',
});
return await loadingElement.present()
}
async stopLoading() {
return await this.loadingController.dismiss();
}
}
EDIT1: this is the CacheService:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class CachingService {
constructor() { }
private _cache = {};
isCashed(url: string) {
return this._cache[url];
}
getData(url: string) {
return this._cache[url];
}
setData(url) {
return (data) => {
if (data && (data instanceof Error) === false) {
this._cache[url] = data;
};
}
}
reset() {
this._cache = {};
}
}
And this is the server service's method:
getReviewsCategory(cat_id) : Observable<any> {
if (this._c.isCashed(url)) {
return of(this._c.getData(url));
}else{
var modeapp = window.sessionStorage.modeapp;
var typemodeapp = typeof(window.sessionStorage.modeapp);
if (modeapp === "online") {
let promise = new Promise ((resolve, reject) => {
this.httpNative.get(url, {}, {}).
then((data) => {
let mydata = JSON.parse(data.data);
console.log("Data from HTTP: ");
console.log(mydata);
resolve(mydata);
}, (error) => {
console.log("error in HTTP");
reject(error.error);
}
);
});
var observable = from(promise);
}
}
return observable
.pipe(
tap(this._c.setData(url))
);
I can see you're returning an observable from the service, you can try the following to see if this helps.
CategoryCtrl() {
this.serverService.getReviewsCategory(this.id)
.subscribe((data) => {
this.category_sources = data['value'];
this.stopLoading();
},
(error) => console.log(error),
() => this.stopLoading(); // This always execute
);}
Docs: http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-subscribe
However, I believe the problem may come from the object you're calling .dismiss()
from. You should be calling dismiss on the instance of the element and not the object itself.
let loadingElement: Loading = null;
async presentLoading() {
const loadingController = this.loadingController;
this.loadingElement = await loadingController.create({
spinner: 'crescent',
});
return await loadingElement.present()
}
async stopLoading() {
return await this.loadingElement.dismiss();
}
You can use an HttpInterceptor class to intercept all http calls, and in the intercept method, you can stop and start a spinner.
Broadly speaking, the structure is:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Start the spinner.
return next.handle(req).pipe(
map((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// Stop the spinner
}
return event;
})
);

return promise every 1 minute?

is there any way to return a promise every 1 minute continuously?
i was trying something like this but it returns promise only once at the beginning:
startWork() {
this.dataService.startPing(details).then((result) => {
this.timeSlotsRefresh();
}, (err) => {
console.log(err);
});
}
and then:
startPing() {
let startingTime = new Date();
return new Promise((resolve, reject) => {
let source = Rx.Observable.timer(startingTime, 60000).timeInterval().pluck('interval');
this.Subscription = source
.subscribe(data => {
this.http.post('http://localhost:63203/api/Ping', JSON.stringify(this.offlinePings[i]))
.map(res => res.json())
.subscribe(data => {
resolve(data);
}, (err) => {
reject(err);
});
});
});
}
it has to basically inform this function every 1 minute to call this.timeSlotsRefresh(); to refresh the data, how can i achieve that?
#Injectable
class Ping {
readonly observable = Rx.Observable.interval(60000);
subscribe(...cbs) {
return this.observable.subscribe(...cbs);
}
}
#Component
class Foo implements OnInit, onDestroy {
private subscription = null;
constructor(private ping: Ping) {}
onPing(count) {}
onPingError(error) {}
onPingFinish() {}
ngOnInit() {
this.subscription = this.ping.subscribe(
(...d) => this.onPing(...d),
(...e) => this.onPingError(...e),
(...f) => this.onPingFinish(...f)
);
}
ngOnDestroy() {
this.subscription.unsubscribe()
}
}
Promises are meant to work only once, you may need for something similar to a streaming and Observables could suit better.
using rx with the interval operator:
var source = Rx
.Observable
.interval(2000 /* ms */)
.map(id => fetch(`https:\/\/jsonplaceholder.typicode.com\/posts\/${id}`).then(res => res.json()))
;
var subscription = source
.subscribe(post => console.log('New Post', post))
;
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.0/Rx.js"></script>

Custom XHR Callbacks Handler

I'm working on a small typescript http library for my friend to simplify the http requests a little bit. I want my friend to be able to send Asynchronous POST requests using method post() from the Http object I've created.
I want to accomplish something similar to subscribe() method in Angular 2. What I mean is I want to create a function, which would be responsible for callbacks (3 types - success, error, complete) and I would use it on my Http's post() method. Here is what I have until now.
Basically here is the written idea:
Http:
import { IHeader } from 'interfaces';
import { SubscribeAble } from 'subscribeAble';
class Http {
http: XMLHttpRequest;
constructor() {
this.http = new XMLHttpRequest;
}
post(url: string, data: Object, headers?: Array<IHeader>) {
this.http.open('POST', url);
if(headers) {
for(let header of headers) {
this.http.setRequestHeader(header.name, header.value);
}
}
this.http.send(JSON.stringify(data));
return new SubscribeAble(this.http);
}
}
SubscribeAble:
export class Subscribe {
http: XMLHttpRequest;
constructor(http) {
this.http = http;
}
subscribe(success: (success) => void, error?: (error) => void, complete?: () => void) {
this.http.onload = success;
if(error) { this.http.onerror = error; }
if(complete) { this.http.onreadystatechange = complete; }
}
}
What I need now is the idea of how to inject the data to functions in subscribe() method... a bit more simple: I want 'success' variable to have this.http.response value in function (success) => {}. Thank you in advance.
I finally figured out how to repair the subscribe method. I used callbacks to achieve what I wanted to. Here is the code:
subscribe(success: (success) => void, error?: (error) => void, complete?: () => void) {
let callback = (cb: (res) => void) {
return callback(this.http.response);
}
this.http.onload = () => {
return callback(success);
}
if(error) {
this.http.onerror = () => {
return callback(error);
}
}
if(complete) { this.http.onloadend = complete; }
}
I think you can do something like this:
subscribe(success: (success) => void, error?: (error) => void, complete?: () => void) {
this.success = success;
this.error = error;
this.complete = complete;
this.http.onload = this.onload;
if(error) { this.http.onerror = this.onerror; }
if(complete) { this.http.onreadystatechange = this.oncomplete; }
}
onload() {
if (this.http.status === 200) {
this.success(this.response);
} else {
if (this.error)
this.error(this.http.statusText);
}
}
}
you set the functions the user send you in subscribe as class variables, and call them with the data you want to send as their parameters.
and you can create the onerror and oncomplete method for the other 2 functions

RxJs: How to "listen" to change on a subscription when it receives a stream?

I'm new with Angular 2 and Observables, but I haven't found a way to "listen" to change on a subscription when it receives a stream, I don't even know if this is possible or if it's the right way.
This is what I used to do with promises:
// Constructor and more code
// ...
ngOnInit(): void {
this.loading.present();
this.getItems()
.then(() => this.loading.dismiss());
}
getItems(): Promise {
return this.itemService
.getItems()
.then(items => this.items = items);
}
refresh(refresher): void {
this.getItems()
.then(() => refresher.complete());
}
I've tried it with subscription/observables but I just don't know how:
// Constructor and more code
// ...
ngOnInit(): void {
this.loading.present();
this.getItems()
.subscribe(() => this.loading.dismiss());
}
getItems(): Subscription {
return this.itemService
.getItems()
.subscribe(items => this.items = items);
}
refresh(refresher): void {
this.getItems()
.subscribe(() => refresher.complete());
}
And of course, I get a compilation error: Property 'subscribe' does not exist on type 'Subscription', any help on how to achieve this with Observables (RxJS)?
A Subscription is a listener, you can't listen to a listener, can you?
Instead, you need to return an Observable to be able to subscribe to. Change your function to be as follow (not tested):
getItems(): Observable<any> {
let obs = this.itemService.getItems().share();
obs.subscribe(items => this.items = items);
return obs;
}

Categories