Я хочу использовать результаты .subscribe в методе внутри второго метода в ngOnInit () {}

У меня есть метод, который использует службу для получения списка книг, мне нужен второй метод, который использует список книг, чтобы получить одну книгу, фильтруя ее по идентификатору. Эти два метода вызываются в ngOnInit одного и того же компонента. Я получаю то, что второй метод использует список книг до того, как получит свои результаты.

Я исправил проблему, вызвав второй метод в первом внутри результатов .subscribe, а затем вызвав только первый в ngOnInit. Я не нашел решение вполне удовлетворительным, мне нужен более общий и организованный способ ...

export class BookComponent implements OnInit {
  public books: Book[];
  public book: Book;
  public id;
  public sub;

  constructor(private activatedRoute: ActivatedRoute, private router: Router, private bookService: BookService) {}
  getBooks() {
    this.bookService.getBooks().subscribe(
      result => {
        this.books = result;
      }
    );
  }

  findById() {
    this.sub = this.activatedRoute.paramMap.subscribe(params => {
      console.log(params);
      this.id = params.get('id');
      this.book = this.books.find(a => a.id == this.id);
    });
  }


  ngOnInit() {
    this.getbooks();
    this.findById();
  }
}

Вот ошибка, которую я получаю:

 ERROR TypeError: Cannot read property 'find' of undefined

person e-ThimOverflow    schedule 17.07.2019    source источник


Ответы (3)


Попробуй это

findById() {
this.sub = this.activatedRoute.paramMap.subscribe(params => {
  console.log(params);
  this.id = params.get('id');
  this.book = this.books.find(a => a.id == this.id);
});
}


ngOnInit() {
  this.books = [];
  this.getbooks();
  this.findById();
}
person dota2pro    schedule 17.07.2019
comment
Это предотвращает неопределенную ошибку, но ничего не делает для решения основной проблемы, объясняющей, почему она не определена (или теперь []). - person Rich; 17.07.2019
comment
Спасибо за идею, это действительно работает, но только со второй попытки, с первой попытки, когда я сначала перехожу к одной книге и назначаю определенный идентификатор, это не работает ... - person e-ThimOverflow; 18.07.2019

Это происходит потому, что книги не инициализированы. Поскольку вы одновременно выполняете две асинхронные функции, нет гарантии, что содержимое books было инициализировано getBooks() перед вызовом findById().

Вы можете избежать этой проблемы, поместив вызов findById() в вызов getBooks(). Такой вид ведет к аду подписок (множественные вложенные подписки), но это можно исправить с помощью rxjs.

Измените исходный код соответствующим образом ниже.

export class BookComponent implements OnInit {
  public books: Book[];
  public book: Book;
  public id;
  public sub;

  constructor(private activatedRoute: ActivatedRoute, private router: Router, private bookService: BookService) {}
  ngOnInit() {
    this.getbooks();
  }

  getBooks() {
    this.bookService.getBooks().subscribe(
      result => {
        this.books = result;
        this.findById()
      }
    );
  }

  findById() {
    this.sub = this.activatedRoute.paramMap.subscribe(params => {
      console.log(params);
      this.id = params.get('id');
      this.book = this.books.find(a => a.id == this.id);
    });
  }

}
person Rich    schedule 17.07.2019
comment
Спасибо за ваш ответ, на самом деле это решение, которое я описал, которое мне не понравилось, потому что таким образом getBooks () не только получает книги, но и применяет фильтрацию по идентификатору ... Вот почему я хотел более организованный способ. - person e-ThimOverflow; 18.07.2019
comment
Для этого вы можете использовать rxjs, но он работает иначе, чем стандартный TS. См. этот вопрос / ответ для получения дополнительной информации. . Я бы сказал, что, вероятно, получите идентификатор из параметров, а затем используйте его в своем вызове http - person Rich; 18.07.2019

Ваш инстинкт подсказывает, что вы не должны пытаться вызывать подписки внутри подписок ... Вы должны использовать наблюдаемые объекты более высокого порядка для достижения этой цели и убедиться, что вы получаете все наблюдаемые данные там, где это необходимо. В этом случае вы хотите использовать combLatest:

ngOnInit() {
  this.sub = combineLatest(this.bookService.getBooks(), this.activatedRoute.paramMap)
               .subscribe(([books, params]) => }
                 this.books = books;
                 this.id = params.get('id');
                 this.book = this.books.find(a => a.id == this.id);
               });
}

теперь обратный вызов подписки будет запускаться каждый раз, когда любой наблюдаемый поток излучает, но только после того, как оба излучают хотя бы один раз. В остальном нет необходимости. В этом случае я предполагаю, что getBooks () сгенерирует только один раз, но paramMap может сгенерировать снова, если пользователь перейдет к другому идентификатору книги.

person bryan60    schedule 17.07.2019
comment
Спасибо за хорошо объясненный ответ, мне понравилась ваша идея, но я также подумал о том, как некоторые строки кода будут повторяться, потому что мне действительно нужен отдельный метод, который дает мне книги, а не повторное использование результатов без повторного получения их, возможно / потенциально, другими методами, такими как findById ... - person e-ThimOverflow; 18.07.2019