【问题标题】:Wait for parent component/module to load data等待父组件/模块加载数据
【发布时间】:2019-08-14 08:22:42
【问题描述】:

请注意:我知道有 APP_INITIALIZER,但这似乎只适用于应用程序的顶级模块 (app.module.ts)。

我有作为加载一些数据的父组件:

admin-area.component.ts

ngOnInit(): void {

  forkJoin([
    this._userService.getMe(),
    this._placeService.getAll()
    ])
    .pipe(finalize(() => this.afterInit()))
    .subscribe(
      ([user, businesses]) => {
        this._appState.user = user;
        this._appState.businesses = businesses;
        this.afterInit();
      }
    );
}

问题是存在依赖this._appState.userthis._appState.businesses 的子组件。

我无法使用APP_INITIALIZER 加载它,因为只有在用户登录时才会加载上述数据。“登录”区域本身就是一个延迟加载的模块。

所以,问题很简单:

如何确保在子组件尝试显示自己之前加载我的数据?

这个:

providers: [
  AdminAreaState,
  { provide: APP_INITIALIZER, useFactory: adminAreaInit, deps: [AdminAreaState], multi: true}
]

不起作用。 adminAreaInit() 没有被调用。这仅适用于我的顶级模块 app.module.ts。所以,APP_INITIALIZER 似乎不适用于任何其他模块,对吗?

我有什么选择?


我想我需要提供更多细节。

注意,我已经在尝试使用*ngIf

<div *ngIf="!business?.servicePlan">
  Please Upgrade
</div>

但问题是,如果我导航到这个特定页面并刷新该页面,business 始终是undefinedbusiness 是使用businessIdbusinesses 的数组中找到正确的business 的结果。

在加载子组件时,我会加载其他数据。在这种情况下,一些评论 (cmets)。

现在,loadPage() 加载 一个 页面我要求的业务数据。 _appState 是一个应该首先加载的服务。如果我这样做:

  private loadPage() {
    console.log(this._appState);
    console.log(this._appState.businesses);
    this._appState.businesses.forEach(
      (b) => {
        console.log(b);
      });
    setTimeout(() => this._appState.businesses.forEach(
      (b) => {
        console.log(b);
      }
    ), 100);

    const businessId = this._appState.businessId;
    this.business = this._appState.businesses.find(b => b.id === businessId);
  }

这是我得到的:

如您所见,this._appState 个业务,但 this._appState.businesses 确实打印了一个空数组。

_appState.businesses 只是一个Observable

ApplicationState服务:

@Injectable()
export class ApplicationState {

  public businessIdChange;

  public businessesChange;

  private _user = new BehaviorSubject<UserModel>(null);

  private _businessId = new BehaviorSubject<number>(null);

  private _businesses = new BehaviorSubject<Array<BusinessModel>>([]);

  constructor() {
    this.businessIdChange = this._businessId.asObservable();
    this.businessesChange = this._businesses.asObservable();
  }

  set user(value: UserModel) {
    this._user.next(value);
  }

  get user(): UserModel {
    return this._user.getValue();
  }

  set businessId(businessId: number) {

    if (businessId === this.businessId) {
      return;
    }

    this._businessId.next(businessId);
  }

  get businessId() {
    return this._businessId.getValue();
  }

  set businesses(value) {
    this._businesses.next(value);
  }

  get businesses(): Array<BusinessModel> {
    return this._businesses.getValue();
  }

}

我不知道为什么我会看到这个,我认为无论如何预加载数据都是有意义的。但也许我在这里有不同的问题?

【问题讨论】:

  • 在加载子组件的模板中使用*ngIf="_appstate_user"有什么要防止的吗?
  • @SGalea 可以与forkJoin 一起使用吗?
  • 我会使用@edkeveked 建议的技术或解析器。
  • 这个应用程序可能需要一些重构,但是你可以快速解决这个问题
    请升级
    阅读有关 Smart/Dumb 组件和 OnPush 更改检测的信息,它将使您的代码非常优雅且易于推理。

标签: angular typescript


【解决方案1】:

在组件级别使用 Angular 解析器。更多信息请查看this

【讨论】:

  • 我刚试过这个。它仅在“数据已加载”的意义上起作用,但在数据完全到达之前仍会呈现子级。
  • @displayname 你能告诉我你的解析器吗?通常,Resolvers 使用 resolve() 返回一个 Observable,该 Observable 在组件被渲染之前被解析,因此数据可用
  • 因为我的问题已经非常大了,所以我把它贴在这里:pastecode.xyz/view/227132a3 我只是从forkJoin 返回 Observable。
  • @displayname 不返回订阅。它必须是一个 Observable!用水龙头或其他东西将数据设置在管道中。像这样:pastecode.xyz/view/ab506506
  • 啊,我想我需要像这里一样使用exhaustMapstackoverflow.com/a/50575875/826983
【解决方案2】:

如果子组件通过路由显示,那么使用解析器可能是一个不错的选择。这是doc中的一个示例

如果父组件只是通过使用子组件标签来调用子组件,那么*ngIf 将是一个不错的选择——像这样

<child-component *ngIf="condition"></child-component>

最后一个选项是使用ngOnInit 来初始化子组件中的数据。

【讨论】:

  • 我更新了我的问题。 *ngIf 已经是我的首选武器,但当我刷新页面时它就坏了。在这种情况下,数据没有加载,我的conditiontrue。 (条件要求 not null
  • 也许你需要改用这个条件"business &amp;&amp; !business?.servicePlan"
  • 这是在另一条评论中提出的。我试过了,但这只在 50% 的时间内有效。
  • 我认为您需要调整该条件以满足您的需求。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-23
  • 2018-04-04
  • 1970-01-01
  • 2016-12-21
  • 2021-12-31
  • 1970-01-01
  • 2021-04-16
相关资源
最近更新 更多