Angular 2+ - загрузка данных: лучшие практики
Мне было интересно, какой лучший подход заключается в загрузке данных в 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
@Component({
selector: 'a-super-fancy-name',
})
export class AComponent {
constructor(private readonly route: ActivatedRoute) {}
}
Ну, пока все хорошо, но:
- Что делать, если у меня есть резольверы с зависимостями? Итак, для разрешения B нам нужен идентификатор из A? Должен ли я использовать оболочку резольвера? Таким образом я делаю что-то вроде:
пример
@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 независимых резольвера, которые можно использовать повторно. В целом, является ли хорошей практикой создание оболочек распознавателя для всех компонентов, для которых требуются данные, или только для распознавателей, зависящих от других распознавателей, или мне вообще не следует использовать упаковщики распознавателей?
Как насчет других методов в резольвере (например,
resolveWithId
)?в
AResolver
route.paramMap используется для получения идентификатора. Не лучше ли передать идентификатор, потому что это выглядит тесно связанным?Какие есть альтернативы?
Каковы недостатки подхода, который я использую?
1 ответ
Решатели хороши на бумаге, но только когда вы можете легко получить все параметры из текущего маршрута, к сожалению, это редко бывает, иногда вам придется обращаться к родительским маршрутам, иногда вы не хотите хранить всю информацию в параметрах маршрута, иногда Вы не хотите блокировать навигацию, также параметры маршрутизатора - не очень гибкая функция, это может легко усложнить рефакторинг (вы не можете просто изменить параметры родительского маршрута, не влияя на дочерние средства распознавания, а ошибки не могут быть проверены компилятором).
Нет предопределенного / рекомендуемого способа загрузки данных в угловых направлениях. В зависимости от масштаба приложения вы можете либо просто загрузить данные в каждый компонент по мере необходимости, либо навязать определенные правила или общие интерфейсы / абстрактные классы, либо использовать кеширование общих служб с помощью rxjs и т. Д. В основном вы можете выбирать только между пользовательской архитектурой и Redux-подобным подходом ngrx. Очевидно, что ngrx накладывает некоторые ограничения и ограничения, а также обеспечивает структуру и общее руководство.
Вместо этого вы можете использовать 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>,
) {}
...
Вы также можете создать другой токен DI, который зависит от этого токена идентификатора и службы http и предоставляет 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,
};