Angular 4, angular-in-memory-web-api и Observables: createDb (), возвращающий Observable или Promise, не работает

Я борюсь с этой проблемой уже пару дней и не могу двигаться дальше.

Я пытаюсь имитировать базу данных, используя некоторые исходные файлы JSON, которые представляют таблицы с отношениями внешнего ключа. Допустим, есть сущность A и сущность B, где A имеет отношение "многие к одному" с B. Помимо необходимости дополнительной очистки, в файлах JSON перечислены только первичные ключи для этих отношений, поэтому у меня есть оболочки DTO RawA и RawB, которые я могу сопоставить с A и B соответственно после получения данных.

Я использую angular-in-memory-web-api, поэтому я настроил свой app.module.ts для использования настраиваемой службы, которая позволяет мне получать файлы JSON следующим образом:

HttpClientInMemoryWebApiModule.forRoot(
  InMemoryDataService,
  { apiBase: '/api', dataEncapsulation: false, passThruUnknownUrl: true }
)

В моем app.component.ts у меня временно есть ngOnInit() метод, который проверяет построение этой базы данных:

ngOnInit() {
  let result = this.http.get('/api/A')
    .subscribe(
      next => { console.log('next:'); console.log(next); },
      error => { console.log('error:'); console.log(error); },
      () => console.log('complete')
    );
}

Насколько я понимаю, база данных в памяти должна увидеть этот запрос, вызвать createDb() в моем сервисе и подписаться на Observable, который возвращает мой метод, а затем использовать список A, которые я создаю в subscribe().

Я не могу заставить это работать. Я пробовал много вещей, возможно, наиболее интуитивно понятным из них является:

// most imports omitted, but I am using
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';

@Injectable()
export class InMemoryDataService implements InMemoryDbService() {
  private static aUrl = './assets/data/a.json';
  private static bUrl = './assets/data/b.json';

  private http: HttpClient;

  constructor( private injector: Injector ) {}

  createDb( reqInfo?: RequestInfo ): {} | Observable<{}> | Promise<{}> {
    this.http = this.injector.get(HttpClient);

    return Observable.forkJoin(
        this.http.get<RawA[]>(InMemoryDataService.aUrl),
        this.http.get<RawB[]>(InMemoryDataService.bUrl)
      ).map(
        result => {
          let bs = (result[1] as RawB[]).map(raw => new B(raw));
          let as = (result[0] as RawA[]).map(raw => new A(raw, bs));
          return { A: as, B: bs };
        }
      );
  }
}

Обратные вызовы subscribe() в ngOnInit() никогда не выполняются. Я пробовал различные комбинации сопоставления, подписки, преобразования в обещания и ругательства, и я не могу ничего лучше, чем ngOnInit() никогда ничего не делать или запрашивать базу данных в памяти для возврата 404.

Я знаю, что документация forkJoin() предупреждает, что все Observables должны быть возвращены, прежде чем они будут выдавать какие-либо значения, но, насколько я могу судить, два вызова http.get() должны возвращаться, если оберточная сантехника подписывается на них. Следующий код успешно регистрирует ожидаемую базу данных (но генерирует ошибку 404, вероятно, потому, что при немедленном вызове api / A обнаруживается пустой массив):

  createDb( reqInfo?: RequestInfo ): {} | Observable<{}> | Promise<{}> {
    this.http = this.injector.get(HttpClient);

    let database: { A: A[], B: B[] } = { A: [], B: [] };
    Observable.forkJoin(
        this.http.get<RawA[]>(InMemoryDataService.aUrl),
        this.http.get<RawB[]>(InMemoryDataService.bUrl)
      ).subscribe(
        result => {
          database.B = (result[1] as RawB[]).map(raw => new B(raw));
          database.A = (result[0] as RawA[]).map(raw => new A(raw, database.B));
          console.log(database);
          return database;
        }
      );
    return database;
  }
  // console logs {A: Array(100), B: Array(20)}, and inspection shows the field values are all correct

Что мне не хватает, чтобы эта работа работала? Кроме того, это действительно сложно, или я упускаю что-то очевидное?


person sadakatsu    schedule 02.03.2018    source источник


Ответы (1)


CreateDb

Глядя на ваш код:

Observable.forkJoin(
    this.http.get<RawA[]>(InMemoryDataService.aUrl),
    this.http.get<RawB[]>(InMemoryDataService.bUrl)
  ).subscribe(

Я подозреваю, что, поскольку вы подключили все HTTP-запросы и еще не создали Db, эти запросы всегда будут в состоянии ожидания.

Чтобы преодолеть это, я бы использовал что-то еще для получения данных json.

Например, Fetch API:

createDb( reqInfo?: RequestInfo ): Observable<{}> | Promise<{}> {
  this.http = this.injector.get(HttpClient);

  return Promise.all([
      fetch(InMemoryDataService.aUrl),
      fetch(InMemoryDataService.bUrl)
    ])
    .then((result: any) => {
      return Promise.all(result.map(x => x.json()));
    })
    .then(
      result => {
        const bs = (result[1] as RawB[]).map(raw => new B(raw));
        const as = (result[0] as RawA[]).map(raw => new A(raw, bs));
        return { A: as, B: bs };
      }
    );
}

ApiBase

Еще вам нужно изменить URL-адрес api

app.module.ts

{ apiBase: 'api/', dataEncapsulation: false, passThruUnknownUrl: true }
           ^^^^^^

component.ts

let result = this.http.get('api/A')
                           ^^^^^^^^^

Или вы можете использовать следующую пару:

{ apiBase: '/', dataEncapsulation: false, passThruUnknownUrl: true }

let result = this.http.get('/A')

Причина в том, что angular-in-web-memory-api анализирует ваш URL

person yurzui    schedule 04.03.2018
comment
Небольшое переписывание вашего createdDb() метода и удаление неправильной косой черты в URL-адресе сработали отлично. Большое спасибо! +1, и награда, когда SO позволяет мне ее наградить. - person sadakatsu; 05.03.2018