J'ai un ensemble de Angular, avec une liste d'entités, avec deux routes enfants pour la création d'une telle entité et l'édition d'une entité existante. La liste d'entités a un resolver
qui lui est attaché pour pré-extraire les données du composant avant de l'afficher. Ces routes peuvent être résumées comme suit, voir plus loin comment ces routes sont décrites dans le code.
/items
/items/create
/items/:itemId/edit
Cependant, si je suis à /items/create
, et créer un élément avec succès, en naviguant "retour" vers /items
ou n'importe quel itinéraire d'édition, ou même revenir à /
ne permettra pas à mon résolveur de récupérer les données mises à jour comme je m'y attendais. Ceci malgré le fait que la propriété runGuardsAndResolvers
soit "toujours" . Ma compréhension est que cette propriété devrait permettre la fonctionnalité que je recherche.
Pourquoi cela et comment puis-je activer la fonctionnalité que je recherche, sans faire quelque chose comme s'abonner aux événements du routeur dans mon composant et dupliquer la logique.
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
}
}
]
}
];
@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;
}
});
}
}
(Affichage sur demande)
@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)
);
}
}
Ce n'est pas comment utiliser un Resolver
.
Vous ne devez utiliser un Resolver
que s'il est absolument nécessaire pour votre itinéraire que les données nécessaires existent. Un résolveur a donc un sens lorsque vous avez un itinéraire pour une donnée dédiée et que vous voulez vous assurer que ces données existent avant de continuer avec le chargement du composant.
Prenons votre Item App comme exemple et affrontons le problème du point de vue de l'utilisateur.
L'utilisateur accède à /items
et ce qu'il va voir en premier n'est rien. En effet, le résolveur récupère tous les éléments avant que l'utilisateur puisse atteindre le composant. Ce n'est qu'après que le résolveur a récupéré tous les éléments de l'API que l'utilisateur est délégué au ItemsComponent
.
Mais ce n'est pas une expérience utilisateur nécessaire et mauvaise.
Pourquoi ne pas déléguer directement l'utilisateur à ItemsComponent
. Si vous avez par exemple une table impressionnante avec beaucoup de boutons et des CSS sympas, vous pouvez donner à votre application le temps de rendre ces éléments pendant que votre Service
récupère les données de l'API. Pour indiquer à l'utilisateur le chargement des données, vous pouvez afficher un Nice progress-spinner (-bar) à l'intérieur du tableau jusqu'à ce que les données arrivent.
Pareil ici. L'utilisateur doit attendre que le Resolver
récupère toutes les données de votre API avant de pouvoir atteindre le CreateComponent
. Il doit être clair que c'est une exagération pour récupérer tous les éléments, seul l'utilisateur peut créer un élément. Il n'y a donc pas besoin d'un Resolver
C'est le meilleur endroit pour un résolveur. L'utilisateur doit pouvoir router vers ce composant si et seulement si l'élément existe. Mais comme vous avez défini un résolveur pour récupérer tous les éléments lorsque l'un de vos itinéraires enfants est activé, l'utilisateur fait face à la même mauvaise expérience utilisateur qu'avec les itinéraires précédents.
Supprimez le ItemsResolver de vos itinéraires.
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
}
}
]
}
];
Vous pouvez définir un BehaviorSubject dans votre ItemsService
qui contient vos derniers éléments récupérés comme un cache. Chaque fois qu'un utilisateur recule de - créer ou éditez vers ItemsComponent
votre application peut immédiatement afficher les éléments mis en cache et en attendant, votre service peut synchroniser les éléments en arrière-plan.
J'ai implémenté un petit exemple d'application: https://stackblitz.com/github/SplitterAlex/stackoverflow-48640721
Un Resolver
n'a de sens que sur l'itinéraire de modification des éléments.
Utilisez Resolver
avec prudence!