HttpClient angular. Сохранить данные в переменной и получить доступ из другого компонента. Пустой или неопределенный - PullRequest
0 голосов
/ 11 апреля 2020

У меня есть запрос на получение с Httpclient. У меня проблема, когда я пытаюсь получить доступ к этой переменной, загруженной по этому запросу из другого компонента. Когда я пытаюсь это сделать, я нахожу его пустым.

Это метод в службе:

import { HttpClient } from '@angular/common/http';
  ...
  getBooks(page: string) {
    let url = `${this.urlAPI}books?page=${page}`;
    return this.http.get<Book[]>(url).pipe(map((res: Book[]) => res));
  }

Это код в component.ts. Это рекурсивно, потому что API загружает только 50 каждый раз.

  import { ApiService } from 'src/app/services/api.service';
  ...
  books: Book[] = [];
  ...

  constructor(public auth: AuthService, public _api: ApiService, private router: Router) {
    this.loadBooks(1);
  }
  ...
  loadBooks(page: number) {

    this._api.getBooks(page.toString()).subscribe((resp: Book[]) => {

      this.books = this.books.concat(resp);
      if (resp.length !== 0)
        this.loadBooks(page + 1)
      else{
        ...
      }

    });
  }

И это способ загрузки переменной из дочернего компонента enet:

import { Component, OnInit, Input } from '@angular/core';
import { BooksComponent } from '../../tab/books/books.component';
...
import { ApiService } from 'src/app/services/api.service';


@Component({
  selector: 'app-book',
  templateUrl: './book.component.html',
  styleUrls: ['./book.component.scss'],
  providers: [BooksComponent]
})
export class BookComponent implements OnInit {

  book: Book = new Book();

  constructor(private bc:BooksComponent, public _api: ApiService, private route: ActivatedRoute) {
    console.log(bc.books);
    ...
  }

Вызов дочерний компонент это сделано после родителя. Также я знаю, что это асинхронный вызов, поэтому пока он не завершится sh, переменная не заполнена. Но я называю это после просмотра всех данных в родительском компоненте. Я не понимаю, почему, если у меня есть переменная заполнена, этот массив пуст от другого компонента. Спасибо за ваши ответы.

ОБНОВЛЕНИЕ: Это HTML Я использую для вызова компонента книги:

<ng-container matColumnDef="detail">
     <th mat-header-cell *matHeaderCellDef> Details </th>
     <td mat-cell *matCellDef="let row">
         <button mat-button (click)="detail(row.url)">
             <mat-icon>info</mat-icon>
         </button>
    </td>
</ng-container>

Это часть таблицы angular материал. Я использую функцию go, чтобы забронировать компонент.

Это NgModule:

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    ListComponent,
    NavbarComponent,
    ProfileComponent,
    BooksComponent,
    BookComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    APP_ROUTING,
    MaterialModule,
    HttpClientModule
  ],
  providers: [ApiService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Родительский компонент - второй код. Остальная часть кода относится к MatPaginator, MatSort или MatTableDataSource.

ОБНОВЛЕНИЕ 2:

Я оставляю код в GitHub: WikiIceAndFire

Ответы [ 2 ]

1 голос
/ 11 апреля 2020

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

@Component({
  selector: 'app-book',
  templateUrl: './book.component.html',
  styleUrls: ['./book.component.scss'],
  providers: [BooksComponent] // <= remove this
})
export class BookComponent implements OnInit {...}

Есть дерево компонентов форсунки в Angular. Когда вы пытаетесь внедрить что-то, вы просто передаете токен в качестве параметра конструктора. Инжектор компонентов начинает поиск этого токена у поставщиков компонентов. В этом случае инжектор компонента обнаружил один экземпляр среди своих собственных провайдеров, который не является требуемым экземпляром (родительским компонентом), поэтому он остановил рекурсивный поиск, который будет выполняться (запросив инъектор компонента родителя, который бы возвратил правильный экземпляр).

0 голосов
/ 11 апреля 2020

Я заметил пару вещей:
1. Не используйте Component внутри providers для Services или других providers в основном
2. Используйте Сервис или @Input() / @Output() для обмена данными между компонентами.
3. Старайтесь избегать вложенных подписок, а также рекурсивных, поскольку это будет серьезным риском утечек памяти.

Теперь для моей идеи по улучшению: я предполагаю, что компонент книги предназначен для отображения компонента для 1 книги.

это book.component.ts

    import { Component, OnInit, Input } from '@angular/core';
    ...
    import { ApiService } from 'src/app/services/api.service';


    @Component({
      selector: 'app-book',
      templateUrl: './book.component.html',
      styleUrls: ['./book.component.scss'],
    })
    export class BookComponent implements OnInit {

      @Input() book: Book = new Book();

      constructor(public _api: ApiService, private route: ActivatedRoute) {

      }

      ngOnInit() {
        console.log(this.book) // either is new Book() or the given book from its parent (look parent .html)
      }
...
import { ApiService } from 'src/app/services/api.service';
import { Subject } from 'rxjs'
import { mergeMap } from 'rxjs/operators'

@Component({
  ...
})
export class ParentComponent implements OnInit, OnDestroy {

...
  books: Book[] = [];

  private loadPage = new Subject<number>()
...

  constructor(public auth: AuthService, public _api: ApiService, private router: Router) {
    //
  } 

  ngOnInit() {

    this.loadPage
    .pipe(
      mergeMap(number => this.loadBooks(number),
        (page, books) => {
          if (books.length !== 0) {
            this.loadPage.next(page+1)
          }
          return books
        }
      )
    ).subscribe({
      next: books => {
        this.books = this.books.concat(books)
      };
      error: () => console.log("error");
    })    
  }

  ngOnDestroy() {
    this.loadPage.complete()
  }

  loadBooks(page: number) {
    return this._api.getBooks(page.toString())
  }

Служба API остается неизменной.

и parent.component.html:

<app-book *ngFor="let book of books" [book]="book"></app-book>
...