Reputation: 39148
In my page, I have two lists of ngFor'ed objects. One list is the assigned members, the other is assignable members. Both are shown by the following pattern.
<ng-container *ngIf="available$ | async as available">
<div *ngFor="let item of available">
<span (click)="assign(item)"</span>{{item.name}}
</div>
</ng-container>
this.available$ = this.service.getMembers(false)
.pipe(
map(__ => ...), ... );
When I click on the assignable member, I want it to disappear from the list and appear in the other. I wonder how to go about achieving that without running subscribe(...) explicitly and assigning to the lists.
One way, would be saving the stuff to the server, then reloading the data but it seems like a huge overkill. another option is to have local, non-observable lists and managed them. However, I kind of like the async pipe and would prefer to keep the component code to the minimum. (Also, I have the whole loading/failure GUI set up based on the pipe in the page so it would be quite some work to rewrite.)
Upvotes: 2
Views: 1741
Reputation: 433
As far as I understand, what you want is to combine your service observable with the output from your assign function without having to use subscribe. I would suggest that you create a subject $newMember
in your class that would emit the value of the new member you have as a result of your assign function. Thereafter you can do something like
this.available$ = combineLatest(this.service.getMembers(false), newMember$).pipe(map(...));
In the demo you can find the following:
this.repos = combineLatest(
this.http.get(this.path),
this.itemObs.asObservable()
).pipe(
map(([req, action]) => {
switch (action.operation) {
case "add": {
return;
}
case "remove": {
this.newReps = this.newReps.filter(item => item.id !== action.id);
return this.newReps;
}
default: {
this.newReps = req["items"];
return this.newReps;
}
}
})
);
and your subscription will stay the same using the async pipe.
Here is a Stackblitz demo
You need to have a look at the rxjs documentation now if you are wondering about the different operators.
merge
- The output Observable only completes once all input Observables have completed. You will have to retrigger your request every time.
combineLatest
- Will emit a projection of the latest values for ALL inputs every time there is a new value for one of the inputs.
Moreover there are at least 15 operators that do this kind of merging of two or more streams with a slightly different behaviours. You just have to make sure to pick the one that fits best your case.
Upvotes: 2
Reputation: 8791
not sure if I get your question right but let me give it a try with pseudocode
import {merge, of, from, ...} from 'rxjs/operators';
assign(item){
this.assigned = merge(this.assigned, of(item)); // add to list
this.available = this.available.pipe(map(x => x.filter(y => y.id != item.id)); //remove from list
}
In case you might want to implement combileLatest solution provided by Ivan here is some pseudo code how to filter out deleted item from its list:
this.available$ = this.service.getMembers(false)
.pipe(
map(__ => ...),
combineLatest(newMember.pipe(map(delItem => {action:"del", item = delItem}))
filter(x => x[action] != 'del')));
something like this?
Upvotes: 3