Periodically updating Observable value in Angular2 - javascript

I'd like to hit and display every 5 seconds from a http link, and it seems to be that using angular2, an observable would be the way to go?
getPhaseVotes(issue: string) {
return this.http.get(this.URL + 'issue/' + issue + '/getPhaseVotes')
.subscribe(data => this.phase_votes = data.json(),
err => console.log(err),
() => this.getStatus(issue));
}
How should I be updating this every 5 seconds?

You could use the interval operator of Observable:
#Injeactable()
export class SomeService {
constructor(private http:Http) {}
getPhaseVotes(issue: string) {
return Observable.interval(5000).flatMap(() => {
return this.http.get(this.URL + 'issue/' + issue + '/getPhaseVotes')
.map(res => res.json());
});
}
}
This way you need to call once the getPhaseVotes method and subscribe on it. Every 5 seconds, an HTTP request will be executed transparently and the result provided within the subscribed callback:
#Component({
(...)
})
export class SomeComponent {
constructor(private service:SomeService) {
this.service.getPhaseVotes('someissue')
.subscribe(data => this.phase_votes = data,
err => console.log(err),
() => this.getStatus(issue));
}
}
This article could give you more hints in its "Polling" section:
https://jaxenter.com/reactive-programming-http-and-angular-2-124560.html

Related

how to fix the unsubscribe in angular

TS
tempThermometer = new BehaviorSubject<any>([]);
subscription: Subscription;
const promises = list.map(
(url: any) =>
new Promise(resolve => {
this.subscription = this.global.getData(url.link).pipe(take(1)).subscribe((res) => {
const urlArr = new Array();
urlArr.push(url);
this.tempThermometer.value.filter((data: any) => {
if (data.spinning) {
return data.spinning = urlArr.findIndex((x: any) => x.sensor === data.sensor) === -1
}
return;
});
resolve(res);
}, (err: Error) => {
return reject(err);
});
})
);
merge(...observables).subscribe((results) => {
console.log(results);
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
What I want to do here is to unsubscribe the promises, because when I click to other page it still running/fetching a data and I want it to stop when I click to other page.
the unsubscribe doesn't work. how to fix it?
The most basic way is to store the Subscription returned from a call to subscribe, and then calling the unsubscribe method on the Subscription when you leave the page (ngOnDestroy life cycle hook in Angular, more about the lifecycle hooks: here).
In your component:
ngOnInit() {
this.sub = this.something.subscribe( ... )
}
ngOnDestroy() {
this.sub.unsubscribe();
}
There are many other ways too:
Using the async pipe in your template where you need the values. It will unsubscribe automatically for you!
take operator that you used in your example will unsubscribe after N values.
takeWhile operator that will unsubscribe based on a predicate.
Here's an article discussing 6 different ways of unsubscribing: https://blog.bitsrc.io/6-ways-to-unsubscribe-from-observables-in-angular-ab912819a78f

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>

What's the pattern to manipulate data in Observables (angular2)

suppose we have this service
#Injectable()
export class CarService {
constructor (private http: Http) {}
getCars() {
return this.http.get('someurl')
.map(res => <Car[]> res.json())
.catch(this.handleError);
}
}
and we subscribe to this in another component. If Car looks like this:
class Car{
Color:string;
Timestamp:any; //this comes as a string in JSON but I want it to be of type Date object
}
and we want to have some logic, ie change date:string to date type, where should this be done?
in a service? and how?
in a class itself? will .map() hit the constructor of Car class?
I would put this process either:
in a map operator that leverages the map method of arrays
return this.http.get('someurl')
.map(res => <Car[]> res.json())
.map(data => {
data.map((d) => {
var date = (...)
return new Car(color, date);
});
return data;
})
.catch(this.handleError);
in the constructor of the Car class
return this.http.get('someurl')
.map(res => <Car[]> res.json())
.map(data => {
data.map((d) => {
return new Car(color, timestampAsString);
});
return data;
})
.catch(this.handleError);

Angular 2 Observables

I'm building an app to get some events from facebook, take a look:
EventComponent:
events: Object[] = [];
constructor(private eventService: EventService) {
this.eventService.getAll()
.subscribe(events => this.events = events)
}
EventService:
getAll() {
const accessToken = 'xxxxxxxxxxx';
const batch = [{...},{...},{...},...];
const body = `access_token=${accessToken}&batch=${JSON.stringify(batch)}`;
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json())
}
AuthenticationService:
getAccessToken() {
return new Promise((resolve: (response: any) => void, reject: (error: any) => void) => {
facebookConnectPlugin.getAccessToken(
token => resolve(token),
error => reject(error)
);
});
}
I have a few questions:
1) How can I set an interval to update the events every 60 seconds?
2) The value of accessToken will actually come from a promise, should I do something like this?
getAll() {
const batch = [{...},{...},{...},...];
this.authenticationService.getAccessToken().then(
accessToken => {
const body = `access_token=${accessToken}&batch=${JSON.stringify(batch)}`;
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json())
},
error => {}
);
}
3) If yes, how can I also handle errors from the getAccessToken() promise since I'm returning just the Observer?
4) The response from the post request will not return an array of objects by default, I'll have to make some manipulation. Should I do something like this?
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json())
.map(response => {
const events: Object[] = [];
// Manipulate response and push to events...
return events;
})
Here are the answers to your questions:
1) You can leverage the interval function of observables:
getAll() {
const accessToken = 'xxxxxxxxxxx';
const batch = [{...},{...},{...},...];
const body = `access_token=${accessToken}&batch=${JSON.stringify(batch)}`;
return Observable.interval(60000).flatMap(() => {
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json());
});
}
2) You could leverage at this level the fromPromise function of observables:
getAll() {
const batch = [{...},{...},{...},...];
return Observable.fromPromise(this.authenticationService.getAccessToken())
.flatMap(accessToken => {
const body = `access_token=${accessToken}&batch=${JSON.stringify(batch)}`;
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json())
});
}
3) You can leverage the catch operator to handle errors:
getAll() {
const batch = [{...},{...},{...},...];
return Observable.fromPromise(this.authenticationService.getAccessToken())
.catch(() => Observable.of({})) // <-----
.flatMap(accessToken => {
const body = `access_token=${accessToken}&batch=${JSON.stringify(batch)}`;
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json())
});
}
In this case, when an error occurs to get the access token, an empty object is provided to build the POST request.
4) Yes sure! The map operator allows you to return what you want...
Put the event inside a timeout block and set the interval of 60s. setTimeout(() => {},60000).
Using Template string is totally fine but you're telling its value comes from a promise. If your whole block of code is inside resolve function of promise this should fine. So it depends on where your code is. And why promises .In A2 it's recommended to use Observables and not promises. Don't mix them.
You're not returning anything in the error function. So if you return error from that block you'll get error data in case of error. error => erroror error => { return error; }.
Exactly you should you map to get the response and manipulate it and return just the array from that function. .map(resp => { return resp.array}). Since respons is in JSON format now you have to get array from it and return it. you can do as much modifications you want before returning it.
Feel free to edit the answer...

Categories