Angular 2+ - 数据加载:最佳实践

12

我想知道在Angular 5中加载数据的最佳方法是什么。 当然,我们希望保持组件的简单。 目前,我正在使用解析器,并且Angular在特定路由中调用解析器。 基本上,在组件初始化之前加载数据,因此我可以向用户显示加载程序动画。

例如:

AResolver.resolver.ts

@Injectable()
export class AResolver implements Resolve<any> {
    constructor(private readonly aService: AService) {}

    resolve(route: ActivatedRouteSnapshot) {
        return this.aService.find(route.paramMap.get('id'));
    }
}

M.module.ts

export const MRoutes: Routes = [
    {
        path: 'route-x',
        component: AComponent,
        resolve: AResolver
    }
];

AComponent.component.ts

AComponent.component.ts

@Component({
    selector: 'a-super-fancy-name',
})
export class AComponent {

    constructor(private readonly route: ActivatedRoute) {}

}

目前为止一切都很好,但是:

  1. 如果我有依赖关系的解析器呢?也就是说,要解决B,我们需要从A获取一个ID?我应该使用解析器包装器吗?这样我做的事情会像:

示例

@Injectable()
export class ABResolver implements Resolve<any> {

    constructor(private readonly aService: AService, private readonly bService: BService) {}

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        return this.aService.resolve(route, state).map((result) => this.bService.resolveWithId(result.Id));
    }
}

我认为这是最好的方法,因为我们仍然有2个独立的解析器可以重复使用。一般来说,是将解析器包装器应用于所有需要数据的组件还是只针对依赖其他解析器的解析器包装器,或者根本不使用解析器包装器是一个好的做法?

2. 解析器中的其他方法(例如resolveWithId)呢?

3. 在AResolver 中,使用路由.paramMap获取id。直接传递id是否更好,因为这看起来紧密耦合?

4. 其他替代方案是什么?

5. 我所使用的方法有哪些缺点?


解析器可能有一些改进的空间。但是由于(在我看来)目前存在巨大的限制,我现在几乎从不使用它们。它似乎与流和反应式应用程序不太兼容。在这里查看更多信息 https://dev59.com/-VUM5IYBdhLWcg3wSepQ 我通常使用ngrx,因此我会将所有HTTP调用管理到effects中。 - maxime1992
2个回答

0

解析器在理论上是很好的,但只有当您可以轻松地从当前路由获取所有参数时才有效。不幸的是,这种情况很少发生,有时您需要访问父级路由,有时您不想将所有信息存储在路由参数中,有时您不想阻止导航,而且路由器参数不是非常灵活的功能,它可能会使重构变得复杂(您不能简单地更改父路由参数而不影响子解析器,并且编译器无法检查错误)。

在Angular中没有任何预定义/推荐的方法来加载数据。根据应用程序规模,您可以根据需要在每个组件中简单地加载数据,或者强制执行某些规则或公共接口/抽象类,或使用具有rxjs等缓存共享服务。基本上,您只能在自定义架构和类似Redux的ngrx方法之间进行选择。 ngrx显然也会带来一些限制和局限性,同时提供结构和总体方向。


谢谢您的回答,但我正在寻找最佳实践和权衡。我想听听常见的陷阱以及为什么我应该选择x而不是y的原因 :) - GuyT

0

而您可以使用InjectionToken

export const DI_ROUTE_ID = new InjectionToken<Observable<string>>('Route id$');

export function factoryRouteId(routeService: WhateverRouteParamObserver): Observable<string> {
  return routeService.params$.pipe(map((params) => params?.id ?? null));
}

export const DI_ROUTE_ID_PROVIDER: Provider = {
  provide: DI_ROUTE_ID,
  deps: [WhateverRouteParamObserver],
  useFactory: factoryRouteId,
};

将其提供在某个模块中(可能直接在app.module中),例如:

@NgModule({
  providers: [
    ...
    DI_ROUTE_ID_PROVIDER,
  ],
})

并在需要的地方注入。

export class DetailComponent {
  constructor(
    @Inject(DI_ROUTE_ID) private readonly id$: Observable<string>,
  ) {}
...

你还可以创建另一个依赖于该ID令牌和http服务的DI令牌,并直接提供 Observable<WhateverDetailData>(并且可以在令牌中直接共享它)。
export const DI_ROUTE_DETAILS = new InjectionToken<Observable<DetailsData>>('Route details$');

export function factoryRouteDetails(id$: Observable<string>, httpDataService: HttpDataService): Observable<DetailsData> {
  return id$.pipe(
    switchMap((id) => httpDataService.getData$(id).pipe(
      catchError(() => of(null))
    ),
    shareReplay({refCount: true, bufferSize: 1}),
  );
}

export const DI_ROUTE_DETAILS_PROVIDER: Provider = {
  provide: DI_ROUTE_DETAILS,
  deps: [DI_ROUTE_ID, HttpDataService],
  useFactory: factoryRouteDetails,
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接