Dans mon service Angular, j'ai les méthodes suivantes:
this.itemService.createItem(item) .pipe( switchMap(createdId => combineLatest(this.itemService.getAllItems(), of(createdId))) ).subscribe(result => { this.items = result[0]; // doing some stuff with result[1], which is the value of createdId });
Dans mon modèle, j'affiche les éléments dans une liste.
Je voudrais pouvoir pour créer un nouvel élément, puis recharger la liste (pour inclure l'élément nouvellement créé), j'ai donc implémenté ce qui suit:
this.itemService.createItem(item) .pipe( switchMap(createdId => this.itemService.getAllItems()) ).subscribe(result => { this.items = result; // i would like to use createdId here as well });
Cela fonctionne apparemment bien, mais à la fin, je voudrais également effectuer un traitement avec createdId
:
this.itemService.createItem(item) .pipe( switchMap(createdId => this.itemService.getAllItems()) ).subscribe(result => { this.items = result; });
Donc, j'ai trouvé ce qui suit: p>
// creates an Item and returns the ID of the created Item createItem(item: Item): Observable<ItemId> { return this.http.post<ItemId>('some_api_url', item); } // returns all Items getAllItems(): Observable<Item[]> { return this.http.get<Item[]>('some_api_url'); }
Mais avoir à utiliser combineLatest
dans le switchMap
et à faire explicitement de createdId
un Observable
me fait me demander si c'est une bonne solution.
Donc, fondamentalement, je voudrais créer un élément, mettre à jour la liste (lorsque la création de l'élément est terminée) et utiliser l'ID de l'élément créé élément lorsque la mise à jour est terminée.
Y a-t-il une meilleure façon de faire cela?
J'apprécierais vraiment tout conseil.
5 Réponses :
Vous pouvez utiliser l'opérateur concatMap
.
Essayez d'abord de créer l'élément, puis attendez l'exécution à l'aide de l'opérateur concatMap
. Prenez la sortie de l'exécution dans l'observable allItems
puis fusionnez les résultats à l'aide d'un opérateur map
. Dans l'abonnement, vous aurez un objet de createdItem
et allItems
const createdItem = this.itemService.createItem(item); const allItems = this.itemService.getAllItems(); createdItem .pipe( concatMap(item => allItems.pipe( map(items => ({ createdItem: item, items }) ) ) )
Vous devez canaliser le résultat de l'observable interne afin de transmettre davantage la valeur émise par la source. Vous pouvez le faire comme suit:
// I use a function here for the purpose of making the code more readable const itemsWithNew = (newItemId) => this.itemService.getAllItems() .pipe(map(items => ({items,newItemId}))); this.itemService.createItem(item) .pipe( switchMap(itemsWithNew) ).subscribe(({items, newItemId})=> { this.items = items; // newItemId is the new value });
Alternativement, switchmap
accepte également un deuxième rappel de résultat pour traiter à la fois la valeur externe et la valeur interne ensemble.
this.itemService.createItem(item).pipe( switchMap( () => this.itemService.getAllItems(), (createdId, items) => { // can do some stuff here with both outer and inner values return { createdId, items }; }, ), ).subscribe({ createdId, allItems }) => { this.items = allItems; });
Le paramètre de fonction de sélection est obsolète
Après quelques recherches supplémentaires sur les opérateurs RxJS, j'ai pensé que la solution la plus propre pourrait être de simplement combiner concat
avec toArray
:
// import { concat } from 'rxjs'; // import { toArray } from 'rxjs/operators'; concat( this.itemService.createItem(item), this.itemService.getAllItems()) .pipe(toArray()) .subscribe((result: [ItemId, Item[]]) => { // result[0] is the ItemId // result[1] is the Item[] });
Je suis presque sûr que toArray
n'est pas obligatoire
@ Jota.Toledo Si je comprends bien, sans le toArray
, cela entraînerait l'émission de deux valeurs distinctes, d'abord le ItemId
, puis le Item []
. J'ai besoin des deux et seulement quand les deux sont terminés, donc c'est nécessaire je pense.
En effet, a supervisé que
// in your service items: Item[]=[]; $$items=new BehaviorSubject(this.items); // creates an Item and returns the ID of the created Item createItem(item: Item): Observable<ItemId> { return this.http.post<ItemId>('some_api_url', item) } // returns all Items getAllItems(): Observable<Item[]> { return this.http.get<Item[]>('some_api_url').pipe(map(response=>{ return {//format response here//} }).subscribe(result=>{ this.items=result; this.$$items.next(this.items); }) } returnItemsAsObs(){ return this.$$items.asObservable(); } //in your component items:Item[] $$items: Subscription ngOninit(){ this.service.getAllItems() this.$items=this.service.returnItemsAsObs().subscribe(items=>{ this.items=items; return this.items; }) onCreateItem(item){ return this.service.createItem(item).subscribe(item=>{ if(item.length<0){ //call other service for itemid manipulation this.service.getAllItems(); } }) } }
Je recommande de changer un peu cela et de retourner un sujet, ou mieux encore un sujet de comportement. De cette façon, vous appelez la valeur suivante du service observable et renvoyez le sujet comme observable. Tous les abonnés recevront la nouvelle variable.