【问题标题】:HttpClient angular. Save data in variable and access from other component. Empty or undefinedHttpClient 角度。将数据保存在变量中并从其他组件访问。空或未定义
【发布时间】:2020-04-13 10:10:26
【问题描述】:

我有一个通过 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{
        ...
      }

    });
  }

这是从子组件加载变量的方法:

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

在父组件之后对子组件的调用。我也知道这是异步调用,所以在它没有完成之前,我还没有填充变量。但是我在查看父组件中的所有数据后调用它。 我不明白为什么如果我填充了变量,这个数组对于其他组件是空的。 谢谢你的回答。

更新: 这是我用来调用书籍组件的 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>

它是角材料表的一部分。我使用该功能去预订组件。

这是 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

【问题讨论】:

  • 哦,顺便说一句,this.http.get&lt;Book[]&gt;(url).pipe(map((res: Book[]) =&gt; res)) 没有任何意义 - 您正在“映射”到原始值。你应该把它改成this.http.get&lt;Book[]&gt;(url);
  • 是的,嘿嘿,我忘了取下来。谢谢

标签: angular httpclient angular-components


【解决方案1】:

从 providers 数组中移除 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 中有一个组件注入器树。当您尝试注入某些东西时,您只需将令牌作为构造函数参数传递。组件注入器开始在组件提供者上寻找该令牌。在这种情况下,组件注入器在它自己的提供者中找到了一个实例,它不是所需的实例(父组件),因此它停止了将发生的递归搜索(通过询问父组件注入器,它会返回正确的实例)。

【讨论】:

  • 问题是如果我不使用将组件放入提供程序中,我会出现此错误:ERROR Error: Uncaught (in promise): NullInjectorError: StaticInjectorError(AppModule)[BookComponent -&gt; BooksComponent]: StaticInjectorError(Platform: core)[BookComponent -&gt; BooksComponent]: NullInjectorError: No provider for BooksComponent! NullInjectorError: StaticInjectorError(AppModule)[BookComponent -&gt; BooksComponent]: StaticInjectorError(Platform: core)[BookComponent -&gt; BooksComponent]: NullInjectorError: No provider for BooksComponent!
  • 您不应该看到任何错误...您的项目中似乎有一些结构不合理。如果您还发布了父组件模板和声明它们的NgModel,这将有助于发现问题。
  • 谢谢朱利安。我写了 NgModule。当我在 Book 组件或服务的构造函数中写入时出现错误:private bc:BooksComponent
  • 所以BookComponent 根本不是BooksComponent 的孩子。你在detail(row.url)做什么?这个很重要。如果您使用路由器导航到BookComponent,则BooksComponent 很可能已被破坏。
  • 嗯。是的,一定是最后一个。是的,detail 发送一个 url,用于转到 Book 组件 html。为此,一切都被摧毁了。我以为它会留在记忆中。
【解决方案2】:

我注意到了几件事:
1. 不要在providers 中使用Component Services 或其他providers,主要是
2. 使用服务或@Input()/@Output() 在组件之间共享数据。
3. 尽量避免嵌套订阅,递归订阅也一样,因为这将是内存泄漏的主要风险。

现在我的改进想法: 我假设 book 组件被设计为一本书的显示组件。

这是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>

【讨论】:

  • 感谢您的回答。如果我删除提供程序,我有这个错误:ERROR Error: Uncaught (in promise): NullInjectorError: StaticInjectorError(AppModule)[BookComponent -&gt; BooksComponent]: StaticInjectorError(Platform: core)[BookComponent -&gt; BooksComponent]: NullInjectorError: No provider for BooksComponent! 我也不能使用输入,因为我没有调用 。我正在使用我现在更新的 HTML 代码。关于订阅,我没有找到其他方法。我想获取所有数据并在我的应用程序中进行管理。 API 每次请求只提供 50 本书。
  • 好的。因此,对于这样的用例,您应该使用 Service 进行通信。例如,将 API 调用和 books[] 数组放在一个服务中,该服务跟踪它们并将服务注入两个组件。如果您需要进一步的帮助来实现这一点,请告诉我。
  • 谢谢。我去试试这个。
  • 我已经这样做了,但它不起作用。当我加载 BooksComponent 时,我从服务中获取书籍,但是对 api 的调用没有完成,所以我看不到书籍列表。那么如果我查看 api 的书籍,是的,他们有书籍。即使是现在,当我从服务手动设置书籍时,表格也不会更新。
  • 您能否提供一个最小的 stackblitz.com 复制您所做的事情?或用您更改的代码更新问题(可能在不同的部分,以便按时间顺序清楚您已经开始的内容)。谢谢!
猜你喜欢
  • 1970-01-01
  • 2019-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-15
  • 2012-06-28
相关资源
最近更新 更多