Угловой распознаватель не обновляет и не перезагружает данные, несмотря на то, что для runGuardsAndResolvers установлено "всегда"?
У меня есть набор угловых маршрутов с перечнем объектов, с двумя дочерними маршрутами для создания такого объекта и редактирования существующего объекта. Список лиц имеет resolver
прикреплен к нему для предварительной выборки данных для компонента перед его отображением. Эти маршруты могут быть обобщены следующим образом, смотрите далее, как эти маршруты описаны в коде.
- индекс:
/items
- Создайте:
/items/create
- редактировать:
/items/:itemId/edit
Однако, если я в /items/create
и успешно создать элемент, перейдя "назад" к /items
или любой редактировать маршрут, или даже вернуться к /
не приведет к тому, что мой распознаватель получит обновленные данные, как я ожидал. Это несмотря на то, что runGuardsAndResolvers
свойство установлено на "всегда". Насколько я понимаю, это свойство должно включать функциональность, которую я ищу.
Почему это так, и как я могу включить нужную функциональность, не делая при этом что-то вроде подписки на события маршрутизатора в моем компоненте и дублирования логики.
Маршруты
const itemRoutes: Routes = [
{
path: '', // nb: this is a lazily loaded module that is itself a child of a parent, the _actual_ path for this is `/items`.
runGuardsAndResolvers: 'always',
component: ItemsComponent,
data: {
breadcrumb: 'Items'
},
resolve: {
items: ItemsResolver
},
children: [
{
path: 'create',
component: CreateItemComponent,
data: {
breadcrumb: 'Create Item'
}
},
{
path: ':itemId/edit',
component: EditOrArchiveItemComponent,
data: {
breadcrumb: 'Editing Item :item.name'
},
resolve: {
item: ItemResolver
}
}
]
}
];
ItemsResolver
@Injectable()
export class ItemsResolver implements Resolve<ItemInIndex[]> {
public constructor(public itemsHttpService: ItemsHttpService, private router: Router) {}
public resolve(ars: ActivatedRouteSnapshot, rss: RouterStateSnapshot): Observable<ItemInIndex[]> {
return this.itemsHttpService.index().take(1).map(items => {
if (items) {
return items;
} else {
this.router.navigate(['/']);
return null;
}
});
}
}
ItemsHttpService
(Размещение по запросу)
@Injectable()
export class ItemsHttpService {
public constructor(private apollo: Apollo) {}
public index(): Observable<ItemInIndex[]> {
const itemsQuery = gql`
query ItemsQuery {
items {
id,
name
}
}
`;
return this.apollo.query<any>({
query: itemsQuery
}).pipe(
map(result => result.data.items)
);
}
}
3 ответа
Прежде всего.
Это не то, как вы должны использовать Resolver
,
Вы должны использовать Resolver
только если это необходимо для вашего маршрута, что необходимые данные существуют. Поэтому Resolver имеет смысл, когда у вас есть маршрут для выделенного фрагмента данных, и вы хотите убедиться, что эти данные существуют, прежде чем продолжить загрузку компонента.
Давайте возьмем ваше приложение Item в качестве примера и столкнемся с проблемой с точки зрения пользователя.
/Предметы
Пользователь переходит к /items
и то, что он увидит первым, - ничто. Это потому, что Resolver извлекает все элементы, прежде чем пользователь сможет добраться до компонента. Только после того, как распознаватель извлек все элементы из API, пользователь получает делегирование ItemsComponent
,
Но это не обязательно и плохой пользовательский опыт.
Почему бы не делегировать пользователя напрямую ItemsComponent
, Если у вас есть, к примеру, потрясающая таблица с множеством кнопок и фантастическим классным CSS, вы можете дать вашему приложению время на рендеринг этих элементов, пока Service
извлекает данные из API. Чтобы указать пользователю, какие данные загружаются, вы можете показывать хороший индикатор прогресса (-bar) внутри таблицы до тех пор, пока данные не поступят.
/ пункты / создать
Тоже самое. Пользователь должен ждать, пока Resolver
получил все данные из вашего API, прежде чем он сможет достичь CreateComponent
, Должно быть понятно, что это излишнее извлечение всех элементов, только элемент может создать только пользователь. Таким образом, нет необходимости в Resolver
/ Пункты /: Itemid/ редактировать
Это лучшее место для Резольвера. Пользователь должен иметь возможность маршрутизировать к этому компоненту, если и только если элемент существует. Но так как вы определили Resolver для извлечения всех элементов, когда активируется один из ваших дочерних маршрутов, пользователь сталкивается с тем же плохим пользовательским интерфейсом, что и с предыдущими маршрутами.
Решение
Удалите ItemsResolver из ваших маршрутов.
const routes: Routes = [
{
path: '',
component: ItemsComponent,
children: [
{
path: '',
component: ItemsListComponent,
data: {
breadcrumb: 'Items'
},
},
{
path: 'create',
component: CreateItemComponent,
data: {
breadcrumb: 'Create Item'
},
},
{
path: ':itemId/edit',
component: EditOrArchiveItemComponent,
data: {
breadcrumb: 'Editing Item :item.name'
},
resolve: {
item: ItemResolverService
}
}
]
}
];
Вы можете определить BehaviorSubject в вашем ItemsService
который содержит ваши последние извлеченные элементы как кеш. Каждый раз, когда пользователь возвращается из создания или редактирования в ItemsComponent
Ваше приложение может немедленно визуализировать кэшированные элементы, а служба может синхронизировать элементы в фоновом режиме.
Я реализовал небольшой рабочий пример приложения: https://stackblitz.com/github/SplitterAlex/stackru-48640721
Заключение
Resolver
имеет смысл только при редактировании маршрута предметов.
использование Resolver
осторожно!
Если вы переходите к одной и той же записи маршрута, angular повторно использует маршрут, поэтому распознаватели не выполняются повторно. Чтобы изменить это поведение, вы можете перезаписать shouldReuseRoute из routeReuseStrategy.
public ngOnInit(): void {
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
}
Я не знаю, как решить вашу конкретную проблему, но я работал под охраной на Angular, вот так, я мог бы помочь вам:
- Изменить:
routing.module.ts
path: '/items/create', canActivate: [AuthGuard]...
AuthGuard разрешает доступ к этому URL-адресу... Это пользовательская охрана.
- Создайте
file.guards.ts
// Imported at providers on your app.module.ts file
export class AuthGuard implements CanActivate {
constructor(){ }
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): any {
// create a function which returns boolean
// allow your access to this url
}
}