Передача значений из нескольких Observables

В моем сервисе Angular у меня есть следующие методы:

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

В моем шаблоне я показываю элементы в списке.

Я хотел бы иметь возможность создать новый элемент, а затем повторно загрузить список (чтобы включить вновь созданный элемент), поэтому я реализовал следующее:

this.itemService.createItem(item)
    .pipe(
      switchMap(createdId => this.itemService.getAllItems())
    ).subscribe(result => {
      this.items = result;
    });

Это, казалось бы, работает нормально, но, в конце концов, я хотел бы также немного обработать createdId:

this.itemService.createItem(item)
    .pipe(
      switchMap(createdId => this.itemService.getAllItems())
    ).subscribe(result => {
      this.items = result;

      // i would like to use createdId here as well
    });

Итак, я придумал следующее:

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

Но использование combineLatest внутри switchMap и явное преобразование createdId в Observable заставляет задуматься, хорошее ли это решение.

Итак, в основном я хотел бы создать и элемент, обновить список (когда создание элемента завершено) и использовать идентификатор созданного элемента, когда обновление будет завершено.

Есть лучший способ сделать это?

Буду очень признателен за любой совет.

Я рекомендую немного изменить это и вернуть тему или, еще лучше, тему поведения. Таким образом, вы вызываете следующее наблюдаемое значение службы и возвращаете субъект как наблюдаемое. Любые подписчики получат новую переменную.

Pari Baker 23.02.2019 19:39
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
7
1
922
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Вы можете использовать оператор concatMap. Попробуйте сначала создать элемент, затем дождитесь выполнения с помощью оператора concatMap. Возьмите результат выполнения в наблюдаемую allItems, а затем объедините результаты с помощью оператора map. В подписке у вас будет объект createdItem и allItems

const createdItem = this.itemService.createItem(item);
const allItems = this.itemService.getAllItems();

createdItem
  .pipe(
    concatMap(item =>
              allItems.pipe(
                map(items => ({
                  createdItem: item,
                  items
                })
              )
    )
  )

Вам нужно передать результат внутренней наблюдаемой, чтобы далее передать значение, испускаемое источником. Вы можете сделать это следующим образом:

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

В качестве альтернативы switchmap также принимает второй обратный вызов результата для совместной обработки как внешнего значения, так и внутреннего значения.

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

Параметр функции селектора устарел

Jota.Toledo 26.02.2019 16:11
Ответ принят как подходящий

Еще немного покопавшись в операторах RxJS, я понял, что самым простым решением может быть простое объединение concat с 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[]
    });

Я почти уверен, что toArray не требуется

Jota.Toledo 26.02.2019 16:17

@ Jota.Toledo Как я понимаю, без toArray это привело бы к выделению двух отдельных значений, сначала ItemId, а затем Item[]. Мне нужны они оба и только тогда, когда они оба завершатся, поэтому я думаю, что это необходимо.

justanoob 26.02.2019 17:25

Действительно, следил за тем,

Jota.Toledo 26.02.2019 17:30
      // 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();


     }

    })


    }


    }

Другие вопросы по теме