12

我有一组 Angular 路由,其中​​包含实体列表,以及用于创建此类实体和编辑现有实体的两个子路由。实体列表附有一个resolver附件,用于在显示之前为组件预取数据。这些路线可以总结如下,请进一步了解这些路线在代码中的描述方式。

  • 指数:/items
  • 创造:/items/create
  • 编辑:/items/:itemId/edit

但是,如果我在/items/create,并成功创建了一个项目,导航“返回”/items或任何编辑路线,甚至返回/不会导致我的解析器像我预期的那样获取更新的数据。尽管将runGuardsAndResolvers属性设置为 "always"我的理解是这个属性应该启用我正在寻找的功能。

为什么会这样,以及如何启用我正在寻找的功能,而无需执行诸如订阅组件中的路由器事件和复制逻辑之类的操作。

路线

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;
            }
        });
    }
}

项目HttpService

(应要求发布)

@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)
        );
    }
}
4

2 回答 2

4

首先。

不是你应该使用的方式Resolver

Resolver仅当存在所需数据对您的路线至关重要时,您才应该使用。因此,当您有一条专用数据的路由并且您希望在继续加载组件之前确保该数据存在时,解析器是有意义的。

让我们以您的Item App为例,从用户的角度来面对问题。

/项目

用户导航到/items,他首先会看到什么。这是因为解析器会在用户访问组件之前获取所有项目。只有在解析器从 api 获取所有项目后,用户才会被委派给ItemsComponent.

但这不是必需的,而且用户体验不好。

为什么不直接将用户委托给ItemsComponent. 例如,如果你有一个很棒的表格,里面有很多按钮和花哨的 CSS,你可以在你Service从 api 获取数据的同时给你的应用程序时间来渲染这些元素。为了向用户指示数据正在加载,您可以在表格内显示一个不错的进度微调器(-bar),直到数据到达。

/项目/创建

同样在这里。用户必须等到Resolver从您的 api 获取所有数据后才能访问CreateComponent. 应该清楚的是,只有用户可以创建一个项目,才能获取所有项目。所以不需要一个Resolver

/items/: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/stackoverflow-48640721

结论

AResolver仅在编辑项目路线上有意义。

Resolver谨慎使用!

于 2018-02-16T22:51:01.940 回答
0

如果您正在导航到相同的路由条目,则 Angular 正在重用该路由,因此不会重新执行解析器。要更改此行为,您可以从 routeReuseStrategy 覆盖 shouldReuseRoute。

public ngOnInit(): void {

     this.router.routeReuseStrategy.shouldReuseRoute = () => false;

}
于 2022-02-27T21:04:44.493 回答