将全局 Object/Model 传递给 Aurelia 中的自定义元素
Pass global Object/Model to custom element in Aurelia
参考以下 post Whosebug Question 我有一个完全不同的场景,我想知道 Aurelia 是否有解决方案。
场景:
我有一个用户模型:
export class User{
@bindable name: string;
@bindable address: Address
如你所见,"Address"是一个子模型。
我有一个主视图模型 "registration"。在这个视图模型中,我有一个模型 "user":
export class RegistrationView{
@bindable user: User
public attached(){
this.user = userService.fetchUserFromApi();
}
除此之外,我还有一个自定义元素 "user-address",其中我有一个 "user-address" 模型(因为我想要专用的封装自定义元素)。
export class userAddress{
@bindable userAddress: Address
现在我只想从 API 请求一次用户模型并将用户地址发送到自定义元素:
<template>
<require from="user-address"></require>
<user-address user.address.bind="${dj.address}"></user-address>
最后,如果用户已经加载,我将(拥有我可以在任何地方使用的专用封装自定义元素)检查附加方法,如果没有,那么自定义元素将加载所有需要的数据:
export class userAddress{
@bindable userId: string
@bindable address: Address
public attached(){
if(!(typeof this.address === "undefined")){
this.address = this.addressAPIService.getAddressByUserId(id)
}
}
- 问题 1:我知道,提到的模板
dj.address.bind
不起作用。但现在我的问题是,我该如何处理这种情况?
- 问题 2:如何确保用户对象仅被请求一次?
- 我的想法是否合理,是 Aurelia 的想法吗?
如果我对你的问题的理解正确,你只需要某种形式的 client-side 坚持。
如果即使在用户关闭浏览器后您仍需要这种持久性,您将需要使用 localStorage
或对其进行一些封装。有很多不错的插件可用,例如 localForage, LokiJS and a recently developed (still in beta) aurelia plugin aurelia-store
您可能希望将用户的检索封装在某种 UserService
中。这不是 Aurelia 特有的,只是您希望在大多数类型的应用程序中如何执行此操作。
例子
所以在你的视图模型中你可能有这样的东西(跳过一些实现细节,例如检查参数,为简洁起见配置路由器等):
@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);
}
}
并且在您的用户服务中:
// 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;
}
}
让你的视图模型变得愚蠢,并在需要加载用户时调用 UserService,让你的服务变得聪明,只在尚未缓存时从 API 中获取它。
我还想指出 attached()
不是您想要获取数据的时候。 attached()
是当你做 DOM 东西(add/remove 元素、样式、其他装饰性东西)。 bind()
最好限制为您在客户端上已有的 grabbing/manipulating 数据。
那么什么时候取数据?
在路由生命周期中的路由视图模型中。那将是 configureRouter
、canActivate
、activate
、canDeactivate
和 deactivate
。这些将在任何 DOM
涉及之前递归解决。
不在您的自定义元素中。或者你很快就会发现自己陷入了通知机制和额外绑定的维护地狱,只是为了让组件可以让彼此知道 "it's safe to render now because I have my data".
如果您的自定义元素可以假定它们在 bind()
发生后就拥有它们的数据,那么一切都会变得 很多 更易于管理。
那么 API 用户调用的调用呢?
比您想象的更多的时候,您可以让一个动作成为一个路线而不是一个直接的方法。您可以无限地嵌套 router-view
,它们真的不需要是页面,它们可以像您喜欢的那样精细。
当很少sub-views可以通过特定路线直接访问时,它增加了很多可访问性。它为您提供额外的挂钩来处理授权、未保存更改的警告和排序,它为用户提供 back/forward 导航等
对于所有其他情况:
从 event-triggered 方法调用服务,就像您通常在 activate()
期间所做的那样,除了通常情况下路由器会延迟页面加载直到数据存在,现在您必须为此自己做元素.
最简单的方法是使用 if.bind="someEntityThatCanBeUndefined"
。该元素仅在该对象具有值时才会呈现。而且它不需要处理获取数据的基础设施。
参考以下 post Whosebug Question 我有一个完全不同的场景,我想知道 Aurelia 是否有解决方案。
场景:
我有一个用户模型:
export class User{
@bindable name: string;
@bindable address: Address
如你所见,"Address"是一个子模型。
我有一个主视图模型 "registration"。在这个视图模型中,我有一个模型 "user":
export class RegistrationView{
@bindable user: User
public attached(){
this.user = userService.fetchUserFromApi();
}
除此之外,我还有一个自定义元素 "user-address",其中我有一个 "user-address" 模型(因为我想要专用的封装自定义元素)。
export class userAddress{
@bindable userAddress: Address
现在我只想从 API 请求一次用户模型并将用户地址发送到自定义元素:
<template>
<require from="user-address"></require>
<user-address user.address.bind="${dj.address}"></user-address>
最后,如果用户已经加载,我将(拥有我可以在任何地方使用的专用封装自定义元素)检查附加方法,如果没有,那么自定义元素将加载所有需要的数据:
export class userAddress{
@bindable userId: string
@bindable address: Address
public attached(){
if(!(typeof this.address === "undefined")){
this.address = this.addressAPIService.getAddressByUserId(id)
}
}
- 问题 1:我知道,提到的模板
dj.address.bind
不起作用。但现在我的问题是,我该如何处理这种情况? - 问题 2:如何确保用户对象仅被请求一次?
- 我的想法是否合理,是 Aurelia 的想法吗?
如果我对你的问题的理解正确,你只需要某种形式的 client-side 坚持。
如果即使在用户关闭浏览器后您仍需要这种持久性,您将需要使用 localStorage
或对其进行一些封装。有很多不错的插件可用,例如 localForage, LokiJS and a recently developed (still in beta) aurelia plugin aurelia-store
您可能希望将用户的检索封装在某种 UserService
中。这不是 Aurelia 特有的,只是您希望在大多数类型的应用程序中如何执行此操作。
例子
所以在你的视图模型中你可能有这样的东西(跳过一些实现细节,例如检查参数,为简洁起见配置路由器等):
@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);
}
}
并且在您的用户服务中:
// 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;
}
}
让你的视图模型变得愚蠢,并在需要加载用户时调用 UserService,让你的服务变得聪明,只在尚未缓存时从 API 中获取它。
我还想指出 attached()
不是您想要获取数据的时候。 attached()
是当你做 DOM 东西(add/remove 元素、样式、其他装饰性东西)。 bind()
最好限制为您在客户端上已有的 grabbing/manipulating 数据。
那么什么时候取数据?
在路由生命周期中的路由视图模型中。那将是 configureRouter
、canActivate
、activate
、canDeactivate
和 deactivate
。这些将在任何 DOM
涉及之前递归解决。
不在您的自定义元素中。或者你很快就会发现自己陷入了通知机制和额外绑定的维护地狱,只是为了让组件可以让彼此知道 "it's safe to render now because I have my data".
如果您的自定义元素可以假定它们在 bind()
发生后就拥有它们的数据,那么一切都会变得 很多 更易于管理。
那么 API 用户调用的调用呢?
比您想象的更多的时候,您可以让一个动作成为一个路线而不是一个直接的方法。您可以无限地嵌套 router-view
,它们真的不需要是页面,它们可以像您喜欢的那样精细。
当很少sub-views可以通过特定路线直接访问时,它增加了很多可访问性。它为您提供额外的挂钩来处理授权、未保存更改的警告和排序,它为用户提供 back/forward 导航等
对于所有其他情况:
从 event-triggered 方法调用服务,就像您通常在 activate()
期间所做的那样,除了通常情况下路由器会延迟页面加载直到数据存在,现在您必须为此自己做元素.
最简单的方法是使用 if.bind="someEntityThatCanBeUndefined"
。该元素仅在该对象具有值时才会呈现。而且它不需要处理获取数据的基础设施。