Custom mapping function for observable - javascript

I have an angular service that I want to return an object I have defined in an interface.
The tutorial I am following assumes that the response is identical to the interface but in my scenario I want to process the object a bit in order to just return some of the data.
My service method based on the tutorial is:
getResults(): Observable<IPageSpeedResult> {
return this._http.get(this._pageSpeedApiUrl)
.map((response: Response) => <IPageSpeedResult> response.json())
.do(data => console.log('All: ' + JSON.stringify(data)))
.catch(this.handleError);
}
How do I extend the map method to include my logic for building the IPageSpeedResult object from the returns data that I want? I basically need to access an array and then iterate over it to populate the return object.

Well, you can do all of that inside the map operator.
getResults(): Observable<IPageSpeedResult> {
return this._http.get(this._pageSpeedApiUrl)
.map((response: Response) => <IPageSpeedResult> response.json())
.map(data => this.transformData(data))
.do(data => console.log('All: ' + JSON.stringify(data)))
.catch(this.handleError);
}
transformData(data: any) : IPageSpeedResult {
// do your magic here
}

You can add your logic to either service method or when you subscribe the data, based on your business logics
getResults(): Observable<IPageSpeedResult> {
return this._http.get(this._pageSpeedApiUrl)
.map((response: Response) => <IPageSpeedResult> response.json())
.do((data) => {
// your custom logic with the data
})
.catch(this.handleError);
}
When subscribing to data
this.serviceObject.getResults()
.subscribe((data)=>{
// your custom logic with the data
})
Note: It is not good practice to have the business logic in the services as they can be reused and construct a different set of data(some other custom mapping)

Related

How to keep the intermediate data with any High-Order operator?

I'm using the switchMap and mergeMap and need to keep some extra data.
See the code
from(fileList)
.pipe(
mergeMap((file: File) => {
return this.s3Service.checkExists(file);
})
)
.subscribe((hash: any) => {
// I want to access to the `file:File` here as well
console.log(`hash exists`, hash, file);
});
How to achieve this?
Map the result of your s3Service.checkExists method to an object which includes the original file:
from(fileList)
.pipe(
mergeMap((file: File) => {
return this.s3Service.checkExists(file).pipe(map(hash => ({hash, file})));
})
)
.subscribe(data => {
// I want to access to the `file:File` here as well
console.log(`hash exists`, data.hash, data.file);
});

How to combine the results of two observable in angular?

How to combine the results of two observable in angular?
this.http.get(url1)
.map((res: Response) => res.json())
.subscribe((data1: any) => {
this.data1 = data1;
});
this.http.get(url2)
.map((res: Response) => res.json())
.subscribe((data2: any) => {
this.data2 = data2;
});
toDisplay(){
// logic about combining this.data1 and this.data2;
}
The above is wrong, because we couldn't get data1 and data2 immediately.
this.http.get(url1)
.map((res: Response) => res.json())
.subscribe((data1: any) => {
this.http.get(url2)
.map((res: Response) => res.json())
.subscribe((data2: any) => {
this.data2 = data2;
// logic about combining this.data1 and this.data2
// and set to this.data;
this.toDisplay();
});
});
toDisplay(){
// display data
// this.data;
}
I can combine the results in the subscribe method of the second observable.
But I'm not sure if it's a good practice to achieve my requirement.
Update:
Another way I found is using forkJoin to combine the results and return a new observable.
let o1: Observable<any> = this.http.get(url1)
.map((res: Response) => res.json())
let o2: Observable<any> = this.http.get(url2)
.map((res: Response) => res.json());
Observable.forkJoin(o1, o2)
.subscribe(val => { // [data1, data2]
// logic about combining data1 and data2;
toDisplay(); // display data
});
toDisplay(){
//
}
A great way to do this is to use the rxjs forkjoin operator (which is included with Angular btw), this keeps you away from nested async function hell where you have to nest function after function using the callbacks.
Here's a great tutorial on how to use forkjoin (and more):
https://coryrylan.com/blog/angular-multiple-http-requests-with-rxjs
In the example you make two http requests and then in the subscribe fat arrow function the response is an array of the results that you can then bring together as you see fit:
let character = this.http.get('https://swapi.co/api/people/1').map(res => res.json());
let characterHomeworld = this.http.get('http://swapi.co/api/planets/1').map(res => res.json());
Observable.forkJoin([character, characterHomeworld]).subscribe(results => {
// results[0] is our character
// results[1] is our character homeworld
results[0].homeworld = results[1];
this.loadedCharacter = results[0];
});
The first element in the array always corresponds to the first http request you pass in, and so on. I used this successfully a few days ago with four simultaneous requests and it worked perfectly.
TRY with forkJoin if it's not working then give this a try combineLatest()
What it do - it combine the last emitted value from your stream array into one before completion of your stream array.
Observable.combineLatest(
this.filesServiceOberserval,
this.filesServiceOberserval,
this.processesServiceOberserval,
).subscribe(
data => {
this.inputs = data[0];
this.outputs = data[1];
this.processes = data[2];
},
err => console.error(err)
);
We can combine observables in different ways based on our need. I had two problems:
The response of first is the input for the second one: flatMap() is
suitable in this case.
Both must finish before proceeding further: forkJoin()/megre()/concat() can be used depending on how you want
your output.
You can find details of all the above functions here.
You can find even more operations that can be performed to combine observables here.
You can merge multiple observables into a single observable and then reduce the values from the source observable into a single value.
const cats = this.http.get<Pet[]>('https://example.com/cats.json');
const dogs = this.http.get<Pet[]>('https://example.com/dogs.json');
const catsAndDogs = merge(cats, dogs).pipe(reduce((a, b) => a.concat(b)));
You can also use mergemap which merges two obserables
Merge map documentation

Passing params to mapped observable

I'm fairly new to Observables, Promises, Angular 2, and Javascript.
My question is how do I get a reference to the "item" object here:
getItemTransactions (item: Item): Observable<any> {
// Do some stuff ...
return this.http.post(this.url, body, options)
.map(this.extractData)
.catch(this.handleError);
}
In the mapped extractData helper?
private extractData(res: Response) {
let json = res.json().body
/// How do I assign back to item object here?
item.some_property = json["some_property"]
}
Code come from here:
https://angular.io/docs/ts/latest/guide/server-communication.html#!#extract-data
Map should be used to convert a type to another type. When using the http service you should convert your expected json result to some expected and known type. This can be done using .json() method on the response.
Use subscribe to then do something with the expected result from .json().
All of your actions can be done in the expression body, you do not need separate methods for them. This is a mater of preference so whatever you choose is fine.
See code below.
getItemTransactions (item: Item): Observable<any> {
// Do some stuff ...
return this.http.post(this.url, body, options)
.map((res) => res.json()) // get the data from the json result. This is equivalent to writing {return res.json()}
.subscribe((data) => {
this.doSomethingWithData(data, item); // pass that deserialized json result to something or do something with it in your expression
})
.catch(this.handleError);
}
private doSomethingWithData(data: any, item: Item) {
// Do some stuff ...
item.some_property = data["some_property"];
}
Why would you want to re-assign a method parameter in the first place? You probably want to assign a class property instead (this.item vs item).
But if for some reason you really want to reassign the item param, you could always inline the extractData helper, i.e.:
getItemTransactions(item: Item): Observable<any> {
return this.http.post(this.url, body, options)
.map((res: Response) => {
item = res.json(); // Re-assign some value to `item`
})
.catch(this.handleError);
}
It's probably NOT what you want to do. The usual pattern is to have a function return an observable and subscribe() to that observable somewhere else in your code. You can do the assignment inside the subscribe callback.
This would translate to the following code:
getItemTransactions(item: Item): Observable<any> {
return this.http.post(this.url, body, options)
.map((res: Response) => res.json())
.catch(this.handleError);
}
// Somewhere else in your code
this.getItemTransactions(item: Item).subscribe(data => {
item = data; // for instance
}

Angular 2 http - filter the observable before returning it

In service I have this defined:
getAllHTTP(): Observable<IUser[]> {
return this.http.get(`${this.url}/users`)
.map(res => res.json())
.catch(err => Observable.throw(err));
}
and in the component:
ngOnInit() {
const self = this;
this._uService.getAllHTTP().subscribe(
function (data) {
console.log(data);
self.listOfUsers = data
},
(error) => console.error(error)
);
}
So now I wanted to filter the data before passing the observable to component and I did this:
getAllHTTP(): Observable<IUser[]> {
return this.http.get(`${this.url}/users`)
.map(res => res.json())
.filter(<IUser>(x) => x.id > 2)
.catch(err => Observable.throw(err));
}
and it doesn't work. The x in filter is an actual array, not an item in the array. This is really odd and I think it has nothing to do with RXjs, because it works like it's suppose to in this example: http://jsbin.com/nimejuduso/1/edit?html,js,console,output
So I did some digging and apparently I should be using flatMap. OK, I did that, replaced map with flatMap but now I get this error:
Cannot find a differ supporting object '[object Object]'
So apparently when I subscribe, I used to get an array and now I get one object at a time... Why is Angular2 behaving like that? Or is this RXjs after all?
You are applying a filter on the observable stream, thus filtering events within the stream and not elements within the array which in this case is a single event in the stream.
What you need to do is:
getAllHTTP(): Observable<IUser[]> {
return this.http.get(`${this.url}/users`)
.map(res => res.json()
.filter(<IUser>(x) => x.id > 2))
.catch(err => Observable.throw(err));
}
The example you linked is working like that because Observable.from is creating a sequence of values for the observable, rather than a singe value being the whole array. This is why the Observable.filter is working as it does, because the array elements are passing by one at a time. See more details in the docs.
It seems like you are asking for a list of user, hence the array. If I got you correctly, you want all users with id > 2:
return this.http.get(`${this.url}/users`)
.map(res => res.json())
.map((users: Array<IUser>) => users.filter(user => user.id > 2)
.catch(err => Observable.throw(err));

Convert http request / add a function in case of success. ::Typescript & Angular2

I have a http request:
private getValues() {
this._watchlistElements.map(v =>
this.http.get('http://localhost/getValue/' + v.xid)
.subscribe(res => {
this._values.push(res.json());
}));
};
And in case of success, I want to make one line code:
this.vars.map((v,i) => v.push(this._values[i].value));
My question is in normal ajax it would be like .success: function(){}
How to convert my code into something like this?
Thank you in advance.
EDIT
private getValues() {
this._watchlistElements.map(v =>
this.http.get('http://localhost/getValue/' + v.xid)
.subscribe(res => {
this._values.push(res.json());
})).then(console.log());
};
Angular2 is unable to resolve the then variable. What have I to import into component to make it work?
The http function uses observables.
You can do something like this:
private getValues() {
this._watchlistElements.map(v =>
this.http.get('http://localhost/getValue/' + v.xid).map(res=>res.json())
.subscribe(res => {
//success
},
error=>{
//error logic
});
}
If you want to use only promises,
private getValues() {
this._watchlistElements.map(v =>
this.http.get('http://localhost/getValue/' + v.xid)
.toPromise()
.then(res=>{res.json()}).catch(errorFunction);
};

Categories

Resources