Angular 4 Как вернуть несколько наблюдаемых в резолвере

Как указано в заголовке, мне нужно вернуть несколько наблюдаемых или, возможно, результатов. Цель состоит в том, чтобы загрузить, скажем, список библиотек, а затем загрузить книги на основе идентификаторов этих библиотек. Я не хочу вызывать службу в компонентах, вместо этого я хочу, чтобы все данные были загружены до загрузки страницы.

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { UserService } from './../_services/index';

@Injectable()
export class LibraryResolver implements Resolve<any> {
    constructor(private _userService: UserService) {}

    resolve(route: ActivatedRouteSnapshot) {
        return this._userService.getLibraryList();
    }
}

Как я могу сначала загрузить список библиотек, а затем загрузить информацию о книге для каждой библиотеки и вернуться к своему компоненту?

PS: Моя служба получила этот метод для загрузки по идентификатору

this.userService.getLibraryBooks(this.library["id"]).subscribe((response) => {
 // response processing
})

person hxdef    schedule 08.01.2018    source источник
comment
Если вам нужно, чтобы данные были доступны до загрузки компонента, вы можете выполнять свои HTTP-вызовы в конструкторе службы. Теперь, когда компонент загружен, поскольку есть внедрение зависимости, объект службы будет предоставлен компоненту с необходимыми данными.   -  person Vinod Bhavnani    schedule 08.01.2018
comment
@VinodBhavnani, спасибо, но я хочу решить эту проблему с помощью резолверов   -  person hxdef    schedule 08.01.2018


Ответы (4)


Я нашел решение этой проблемы, может быть, кому-то поможет, поэтому в основном я использовал forkJoin, чтобы объединить несколько Observables и разрешить их все.

resolve(route: ActivatedRouteSnapshot): Observable<any> {
        return forkJoin([
                this._elementsService.getElementTypes(),
                this._elementsService.getDepartments()
                .catch(error => {

                    /* if(error.status === 404) {
                        this.router.navigate(['subscription-create']);
                    } */

                    return Observable.throw(error);
                })
        ]).map(result => {
            return {
                types: result[0],
                departments: result[1]
            };
        });
    };

Теперь все работает правильно, как задумано.

person hxdef    schedule 12.12.2018

Вы можете легко разрешить несколько наблюдаемых с помощью withLatestFrom.

Вот рабочая демонстрация в Stackblitz: https://stackblitz.com/edit/angular-xfd5xx

Решение ниже.

В вашем преобразователе используйте withLatestFrom для объединения ваших наблюдаемых:

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Library } from '../models/library.model';
import { LibraryBook } from '../models/library-book.model';
import { LibraryService } from '../services/library.service';
import { Observable } from 'rxjs';
import { withLatestFrom } from 'rxjs/operators';

@Injectable()
export class LibraryDisplayResolver implements Resolve<[Library, LibraryBook[]]> {

  constructor(
    private _libraryService: LibraryService,
  ) { }

  resolve (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<[Library, LibraryBook[]]> {
    const libraryId = route.params['id'];
    return this._libraryService.getLibrary(libraryId).pipe(
      withLatestFrom(
        this._libraryService.getBooksFromLibrary(libraryId)
      )
    );
  }
}

Убедитесь, что ваш преобразователь настроен на ваш маршрут с соответствующим идентификатором:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LibraryDisplayComponent } from './library-display.component';
import { LibraryDisplayResolver } from '../resolvers/library-display.resolver';

const routes: Routes = [
  {
    path: ':id',
    component: LibraryDisplayComponent,
    resolve: {
      libraryResolverData: LibraryDisplayResolver
    }
  },
  {
    path: '',
    redirectTo: '1',
    pathMatch: 'full'
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class LibraryDisplayRoutingModule { }

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

import { Component, OnInit } from '@angular/core';
import { LibraryBook } from '../models/library-book.model';
import { Library } from '../models/library.model';
import { ActivatedRoute } from '@angular/router';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'library-display',
  templateUrl: './library-display.component.html',
  styleUrls: ['./library-display.component.scss']
})
export class LibraryDisplayComponent implements OnInit {

  library: Library;
  libraryBooks: LibraryBook[];

  constructor(
    private route: ActivatedRoute
  ) { }

  ngOnInit() {
    this.library = this.route.snapshot.data['libraryResolverData'][0];
    this.libraryBooks = this.route.snapshot.data['libraryResolverData'][1];
  }
}
person Greg    schedule 10.12.2018
comment
спасибо, что поделились, но после тестирования вашего решения я понял, что оно не всегда разрешает все данные, но да, сначала это сработало - person hxdef; 12.12.2018
comment
Хм, в моем приложении он отлично работает (я использовал этот шаблон только для этого варианта использования). Приветствую любые исправления. - person Greg; 13.12.2018
comment
Добавлен пример Stackblitz, fyi. Буду признателен за просмотр перед голосованием против, ти - person Greg; 13.12.2018
comment
Я не голосовал против вас, возможно, кого-то еще. Ваш ответ действительно помог мне с моим решением :) - person hxdef; 13.12.2018

У меня похожая ситуация, но я подошел к ней немного иначе.

В вашем случае, насколько я понимаю, вы хотите получить параметр запроса, а затем на основе ответа вызвать еще несколько служб.

Я делаю это с помощью компонента, который оборачивается вокруг других и передает нужные им объекты. Я подписываюсь на них через route paramMap, а затем помещаю все остальные вызовы в файл. Observalbe.forkJoin

В моем компоненте-оболочке я делаю следующее:

ngOnInit() {

    this.route.params.subscribe((params) => {
        if (params.hasOwnProperty('id') && params['id'] != '') {
            const slug = params['slug'];
            Observable.forkJoin([
                this.myService.getData(id),
                this.myService.getOtherData(id),
            ]).subscribe(
                ([data, otherData]) => {
                    this.data = data;
                    this.otherData = otherData;
                },
                error => {
                    console.log('An error occurred:', error);
                },
                () => {
                    this.loading = false;
                }
                );
        }
    });

Не совсем то, что вам нужно, но я надеюсь, что это укажет вам в правильном направлении

person Evonet    schedule 08.01.2018
comment
Спасибо, чувак, да, это то, что я ищу, спасибо за ваше решение, но я надеюсь, что есть способ сделать это, используя только резолверы, поскольку моих знаний в этом недостаточно. - person hxdef; 08.01.2018
comment
Извините, я сам ими не пользуюсь! - person Evonet; 08.01.2018

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

http://www.syntaxsuccess.com/viewarticle/combining-multiple-rxjs-streams-in-angular-2.0

http://blog.danieleghidoli.it/2016/10/22/http-rxjs-observables-angular/

Я лично использую flatMap

Удачи.

person Nadhir Falta    schedule 08.01.2018
comment
Я уже использую наблюдаемое. Тема о том, как использовать несколько единиц в резолвере - person hxdef; 08.01.2018
comment
Можете ли вы привести пример резолвера, в котором вы выбираете один, а затем на основе этого вы получаете для него другие значения? - person hxdef; 08.01.2018