angular2 work with Observable(Http) without reloading - javascript

I have a PartnersService that in constructor load list of partners array from server.
this.partners = this._http.get('/app/partners/partners.data.json')
.map(this.extractData)
.catch(this.handleError);
When I get a list of partners, everything OK.
But I need to get partner by ID.
In this case, I use function with callback
getPartner(id:number, callback:(partner:Partner)=>void):void {
this.partners.subscribe(partners=> {
for (let partner of partners) {
if (partner.id == id) {
callback(partner);
}
}
});
}
In browser console, I see that any time when I call subscribe, angular2 send request to server. Can I do something, to not send request any time? And maybe exist better way to create getPartner() function(without callback)?

You could try something like that:
getPartners() {
if (this.partners) {
return Observable.of(this.partners);
} else {
return this._http.get('/app/partners/partners.data.json')
.map(this.extractData)
.catch(this.handleError)
.do(partners => {
this.partners = partners;
});
}
}
getPartner(id:number):Observable<partner> {
this.getPartners().map(partners => {
return partners.find(partner => (partner.id == id));
});
}
And execute the method this way:
this.service.getPartner('some id').subscribe(partner => {
console.log(partner);
});

You could use publishLast() as well to capture the event.
constructor() {
this.partners = this._http.get('/app/partners/partners.data.json')
.map(this.extractData)
.catch(this.handleError)
//shares the subscription with all observers so you don't get repeated
//calls and caches the result.
.publishLast();
//Initiates the http call
this.connection = this.partners.connect();
}
getPartner(id:number):Observable<partner> {
return this.partners
//Convert Observable<partner[]> to Observable<partner>
.flatMap(x => x)
.filter(p => p.id === id);
}

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 define if #mentioned is subscriber? TMI.js

Trying to make little bot for my friend's twitch and want to make it define if "#mentioned" is a sub.
I decided to test if it define mods first but it always says "2"
client.on("chat", function (channel, user, message, self) {
if (message.toLowerCase().startsWith("!love")) {
var loved = message.split(' ')[1];
if (loved.mod || loved['user-type'] === 'mod'){
client.action(channel, "1 ");  //says sth
      }
else {
client.action(channel, "2 ");//says sth different
      }
}
user.mod
Is only available for user objects that are returned by the message handler. If you want to check if a mentioned user is a mod you have to request the chat list
function getAllChatters(channel, _attempts) {
return rp({
uri: `https://tmi.twitch.tv/group/user/${channel.replace('#','')}/chatters`,
json: true
})
.then(data => {
return Object.entries(data.chatters)
.reduce((p, [ type, list ]) => p.concat(list.map(name => {
return {name, type};
})), []);
})
.catch(err => {
if(_attempts < 3) {
return getChatters(channel, _attempts + 1);
}
throw err;
})
}
Then filter it for only mods
function getModsOnline(channel) {
return getAllChatters(channel)
.then(data => {
var mods = data.filter(chatter => chatter.type == 'moderators' || chatter.type == 'broadcaster').map(chatter => chatter.name);
return mods;
})
}
And then see if the user is in the list
function isModOnline(channel, username) {
// This will only return true if the mod is online in chat aka in the chat list
return getMods(channel)
.then(mods => {
return mods.includes(username);
})
}
Keep in mind that
This is for getting mods, not subscribers
It will only work for mods that are currently in chat, that list tends to update rather slowly
You need to work with promises here so
const rp = require('request-promise');
and to use the value
```
isModOnline(channel, username)
.then(value => {
// Code to work with the value here
console.log(`User is mod: ${value}`);
})

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;
})
);

make search in Observable sync

I want to check, if given clientName is present in Observable Collection, but in cause of async run, i don't get "false" return at all. How to transform my function to sync - i would like not to use callbacks - just return true/false
checkIfClientNameIsUnique(clientName: string): boolean {
var isUnique = true;
this.getAll()
.subscribe(clients => {
clients.forEach(client => {
if (clientName == client.name) {
isUnique = false
}
})
});
return isUnique
}
I see three options:
make checkIfClientNameIsUnique to return Promise then you
can use it like checkIfClientNameIsUnique(name).then(isUnique =>
{...})
Load all clients to array on initial state. I suppose you have ClientsService there you can put clients array, then your
checkIfClientNameIsUnique method can be sync and use already loaded
clients array.
3.If you emit to ES6 you can use async await keywords and it will look like this.
checkIfClientNameIsUnique(clientName: string): Promise<boolean> {
return new Promise((resolve, reject) => {
this.getAll()
.subscribe(clients => {
for (let client of clients) {
if (clientName == client.name) {
resolve(false);
break;
}
}
resolve(true);
});
});
}
// ...
async main() {
let isUnique = await checkIfClientNameIsUnique(name);
}

Add listener to Object prototype

I'm running a WebSocket server with ws, and am trying to split incoming messages based on whether they are JSON or not.
function determineJSON(m) {
try { return ['json', JSON.parse(m)] }
catch (err) { return ['not-json', m] }
}
wss.on('connection', ws => {
ws.on('message', m => {
// Emit to 'json' and 'not-json' as appropriate
if (ws.listenerCount('json') + ws.listenerCount('not-json') > 0) {
ws.emit(...determineJSON(m))
}
})
.on('json', j => { ... })
.on('not-json', m => { ... })
})
The code works great, but I was wondering how I could add the .on('message', ...) listener to the WS class, so that all new WS objects would have it. I tried WS.prototype.on('message', ...), but that didn't seem to do anything.
So actually you want creating instances of WS with predefined state?
For that purpose I would suggest you just to create a factory which would handle it for you.
WsFactory.create = function () {
var ws = new WS(); //or whatever you use for creating
ws.on(...);
return ws;
}
You would avoid mutating prototypes, and would get what you want to have.
Well I'm not a big fan of modifying the prototype, so you can do something like :
function determineJSON(m) {
try { return ['json', JSON.parse(m)] }
catch (err) { return ['not-json', m] }
}
function decorateWS(ws) {
return ws.on('message', m => {
// Emit to 'json' and 'not-json' as appropriate
if (ws.listenerCount('json') + ws.listenerCount('not-json') > 0) {
ws.emit(...determineJSON(m))
}
})
}
wss.on('connection', ws => {
decorateWS(ws).on('json', j => { ... })
.on('not-json', m => { ... })
})
Anyway if you want to modify the class itself you will probably need to do something like :
let origConstructor = ws.prototype.constructor;
WS.prototype.constructor = () => {
origConstructor.apply( this, arguments );
const ws = this;
this.on('message', m => {
// Emit to 'json' and 'not-json' as appropriate
if (ws.listenerCount('json') + ws.listenerCount('not-json') > 0) {
ws.emit(...determineJSON(m))
}
})
}
In any case I think this might have side effects. So the decorate approach looks far better and maintainable.

Categories