将全局对象/模型传递给Aurelia中的自定义元素

时间:2018-03-08 18:42:58

标签: aurelia aurelia-binding aurelia-cli aurelia-framework aurelia-templating

参考以下帖子StackOverflow Question我有一个完全不同的场景,我想知道Aurelia是否有解决方案。

情境:

我有一个用户模型:

export class User{
    @bindable name: string;
    @bindable address: Address

如您所见,"地址"是一个子模型。

我有一个主视图模型"注册"。在这个视图模型中,我有一个模型" user":

export class RegistrationView{
    @bindable user: User

    public attached(){
        this.user = userService.fetchUserFromApi();
    }

除此之外,我还有一个自定义元素"用户地址"我有一个"用户地址" -model(因为我想拥有专用的封装自定义元素)。

export class userAddress{
    @bindable userAddress: Address

现在我只想从API请求用户模型一次,并将用户地址发送到custom-element:

<template>
  <require from="user-address"></require>

  <user-address user.address.bind="${dj.address}"></user-address>

最后我会(有可以在任何地方使用的专用封装自定义元素)检查附加方法,如果用户已经加载,如果没有,那么custom元素将加载所有需要的数据:

export class userAddress{
    @bindable userId: string
    @bindable address: Address

    public attached(){
        if(!(typeof this.address === "undefined")){
             this.address = this.addressAPIService.getAddressByUserId(id)
        }
    }
  1. 问题1 :我知道,提到的模板dj.address.bind不起作用。但现在我的问题是,我该如何处理这种情况?
  2. 问题2 :我如何确保只请求一次用户对象?
  3. 我的概念是否有意义,是否是Aurelia的想法?

1 个答案:

答案 0 :(得分:2)

如果我正确理解您的问题,您只需要某种形式的客户端持久性。

如果您在用户关闭浏览器后仍需要此持久性,那么您将要使用https://bitbucket.org/owner/repo/pull-requests/new?source=branchname&t=1或其中某些封装。有许多好的插件,例如localForageLokiJS和最近开发的(仍在测试中)aurelia插件aurelia-store

您可能希望将用户的检索封装在某种localStorage中。这对于Aurelia来说并不是特定的,通常只是你想在大多数类型的应用程序中这样做。

实施例

所以在你的viewmodel中你可能有这样的东西(跳过一些实现细节,比如检查参数,配置路由器等以简洁起见):

UserService

在您的用户服务中:

@autoinject()
export class UserViewModel {
  public user: User;

  constructor(private userService: UserService){}

  // happens before bind or attached, so your child views will always have the user in time
  public async activate(params: any): Promise<void> {
    this.user = await this.userService.getUserById(params.id);
  }
}

让你的视图模型变得愚蠢,并在需要加载用户时调用UserService,让你的服务变得聪明,只有当它尚未缓存时才从API中获取它。

我还想指出// singleton will ensure this service lives as long as the app lives @singleton() export class UserService { // create a simple cache object to store users private cache: any = Object.create(null); constructor(private client: HttpClient) {} public async getUserById(id: number): Promise<User> { let user = this.cache[id]; if (user === undefined) { // immediately store the user in cache user = this.cache[id] = await this.client.fetch(...); } return user; } } 不是你想抓取数据的时候。 attached()是你做DOM的东西(添加/删除元素,样式,其他化妆品)。 attached()最好限制在抓取/操纵客户端上已有的数据。

那么何时获取数据?

在路由生命周期中的路由视图模型中。这应该是bind()configureRoutercanActivateactivatecanDeactivate。这些将在任何deactivate参与之前递归解决。

不在自定义元素中。或者你很快就会发现自己处于维护状态,通知机制和额外的绑定只是因为组件可以让彼此知道并且现在可以安全地渲染它,因为我有我的数据&#34;。

如果您的自定义元素在DOM出现bind()后就会假设其数据已经存在,则所有内容都变得更加很多更易于管理。

那么用户调用的API调用呢?

比你想象的更频繁,你可以让行动成为路线,而不是直接的方法。你可以无限地嵌套router-view,他们真的不需要成为页面,它们可以像你想的那样细化。

当通过特定路线直接访问小子视图时,它增加了很多可访问性。它为您提供额外的钩子来处理授权,未保存的更改和排序的警告,它为用户提供后退/前进导航等。

对于所有其他情况:

通过事件触发方法调用服务,就像通常在activate()期间那样,除非通常路由器推迟页面加载直到数据存在,现在你必须自己为该元素做。

最简单的方法是使用if.bind="someEntityThatCanBeUndefined"。该元素仅在该对象具有值时呈现。并且它不需要处理获取数据的基础设施。