我正在查看nativescript hello world typescript回购,我发现了一些使用nativescript实现可观察对象的繁琐工作。
如果你看看如何定义view model,你会发现它是一个只扩展Observable
库的类。无论何时为属性定义setter
方法,
您手动需要致电super.notifyPropertyChange("propertyName", propertyValue);
恕我直言,如果您的视图模型具有许多属性,则此过程非常低效并且容易出错。
有没有办法自动执行此任务? (也许有一个基类告诉任何setter notifyPropertyChange
?)
如果没有,你是如何处理这个问题的?是否存在Observable
机制的其他实现?
答案 0 :(得分:7)
以下是我在几个制作应用中使用的内容:
import { Observable } from "data/observable";
export class ObservableModel extends Observable {
constructor() {
super();
}
public get(propertyName: string) {
return this["_" + propertyName];
}
public set(propertyName: string, value) {
if (this["_" + propertyName] === value) {
return;
}
this["_" + propertyName] = value;
this.refresh(propertyName);
}
public refresh(propertyName: string) {
super.notify({
eventName: Observable.propertyChangeEvent,
propertyName,
object: this,
value: this["_" + propertyName],
});
}
}
然后你的模型看起来像:
export class LoginViewModel extends ObservableModel {
get userName(): string { return this.get("userName"); }
set userName(val: string) { this.set("userName", val); }
get password(): string { return this.get("password"); }
set password(val: string) { this.set("password", val); }
}
当您需要使用您刚才使用的值时:
const vm = new LoginViewModel();
vm.userName = "jdoe";
vm.password = "$3cr3T";
<强>更新强> 装饰者实施:
export function ObservableProperty() {
return (target: Observable, propertyKey: string) => {
Object.defineProperty(target, propertyKey, {
get: function () {
return this["_" + propertyKey];
},
set: function (value) {
if (this["_" + propertyKey] === value) {
return;
}
this["_" + propertyKey] = value;
this.notify({
eventName: Observable.propertyChangeEvent,
propertyName: propertyKey,
object: this,
value,
});
},
enumerable: true,
configurable: true
});
};
}
型号:
export class LoginViewModel extends Observable {
@ObservableProperty() public userName: string;
@ObservableProperty() public password: string;
}
答案 1 :(得分:1)
确实很冗长且容易出错。这是对DRY的违反,应该避免。有很多方法可以使用JavaScript完全实现这一目标。
一种方法可能是使用装饰器以通用方式自动连接所有样板并保持模型类清洁和声明。
例如,我们可以创建以下函数
<强> observed.ts 强>
export default function<T extends Notifier<T, K>, K extends keyof T>(target: T, key: K) {
let storedValue = target[key];
const get = () => storedValue;
const set = (value: T[K]) => {
if (storedValue !== value) {
storedValue = value;
target.notifyPropertyChange(key, storedValue);
}
};
Object.defineProperty(target, key, {
get,
set,
enumerable: true,
configurable: false
});
}
export interface Notifier<T, K extends keyof T> {
notifyPropertyChange(key: K, value: T[K]): void;
}
现在我们可以使用它从模型本身中删除所有样板。我们甚至删除了getter和setter并使用简单的属性
<强> model.ts 强>
// Stub observable class to verify inheritance works correctly (as requested)
class Observable {
notifyPropertyChange(key: string, value: {}) {
console.log(`from super: ${key} ===> ${value}`);
}
}
export class Model extends Observable {
import observed from './observed';
export class Model extends Observable {
@observed name = 'Bob';
@observed age = 38;
@observed birthdate = moment();
notifyPropertyChange<K extends keyof this>(key: K, value: this[K]): void {
super.notifyPropertyChange(key, value);
console.log(`${key} ===> ${value}}`);
}
}
const model = new Model();
model.name = 'Rob';
model.name = 'Robert';
这种方法的一些好处是
相当DRY
我们有一个比我们手动定义getter和setter更简洁易读的模型类
我们的装饰器通过要求类提供notifyPropertyChange
方法并在正确的属性键上调用它来改进类型检查。如果我们违反此规则,TypeScript将发出编译器错误
用于存储值的实际变量是真正的私有。它只在装饰器闭包内的范围内,除了通过getter和setter之外无法访问。这不是命名惯例,它是真正的隐私,良好的老式JavaScript方式
这可以概括为装饰整个班级,甚至是DRYer。