Typescript 实现可观察默认setter行为的Nativescript类
我正在研究回购协议,在使用nativescript实现可观测数据时遇到了一些麻烦 如果您看一下是如何定义的,您会发现它是一个简单地扩展了Typescript 实现可观察默认setter行为的Nativescript类,typescript,nativescript,Typescript,Nativescript,我正在研究回购协议,在使用nativescript实现可观测数据时遇到了一些麻烦 如果您看一下是如何定义的,您会发现它是一个简单地扩展了可观察的库的类。无论何时为属性定义setter方法, 手动需要调用super.notifyPropertyChange(“propertyName”,propertyValue) IMHO如果您有具有许多属性的视图模型,则此过程非常低效且容易出错 有没有办法使这项任务自动化?(可能有一个基类告诉任何setternotifyPropertyChange?) 如果没
可观察的
库的类。无论何时为属性定义setter
方法,
手动需要调用super.notifyPropertyChange(“propertyName”,propertyValue)代码>
IMHO如果您有具有许多属性的视图模型,则此过程非常低效且容易出错
有没有办法使这项任务自动化?(可能有一个基类告诉任何setternotifyPropertyChange
?)
如果没有,您将如何处理此问题?可观察
机制还有其他实现吗?以下是我在两个生产应用程序中使用的内容:
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;
}
以下是我在两个生产应用程序中使用的内容:
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;
}
它确实冗长而且容易出错。这是一种违反干法的行为,应该避免。有很多方法可以使用JavaScript干净地实现这一点
一种方法可能是使用装饰器以通用方式自动连接所有样板文件,并保持模型类干净和声明性
例如,我们可以创建以下函数
观察到。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;
}
// 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';
导出默认函数(目标:T,键:K){
让storedValue=target[key];
const get=()=>storedValue;
常量集=(值:T[K])=>{
如果(存储值!==值){
storedValue=价值;
target.notifyPropertyChange(key,storedValue);
}
};
Object.defineProperty(目标、键、{
收到
设置
可枚举:正确,
可配置:false
});
}
导出接口通知程序{
notifyPropertyChange(key:K,value:T[K]):void;
}
现在我们可以使用它从模型本身删除所有样板文件。我们甚至删除了getter和setter,并使用了简单的属性
model.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;
}
// 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';
//存根可观察类,用于验证继承是否正确工作(根据请求)
类可观测{
notifyPropertyChange(键:字符串,值:{}){
log(`from super:${key}===>${value}`);
}
}
导出类模型扩展了可观测{
从“./observed”导入观测值;
导出类模型扩展了可观测{
@观察到的名称='Bob';
@观察年龄=38岁;
@观察到的出生日期=时刻();
notifyPropertyChange(键:K,值:this[K]):void{
super.notifyPropertyChange(键,值);
log(`${key}===>${value}}`);
}
}
常量模型=新模型();
model.name='Rob';
model.name='Robert';
这种方法的一些好处是
- 天气相当干燥
- 与手动定义getter和setter相比,我们的模型类更加简洁易读
- 我们的decorator通过要求类提供
notifyPropertyChange
方法并在正确的属性键上调用它来改进类型检查。如果我们违反了这一点,TypeScript将发出一个编译器错误
- 用于存储值的实际变量是真正私有的。它只在装饰器闭包的作用域内,不能通过getter和setter访问。这不是一种命名约定,而是真正的隐私,一种老式的JavaScript方式
- 我们避免仅仅为了代码共享而引入基类。也就是说,我们可以选择组合而不是继承
- 我们获得了内联初始值设定项的便利性以及由此产生的类型推断
这可以推广到装饰整个类,使其更加干燥。它确实冗长且容易出错。这是一种违反干法的行为,应该避免。有很多方法可以使用JavaScript干净地实现这一点
一种方法可能是使用装饰器以通用方式自动连接所有样板文件,并保持模型类干净和声明性
例如,我们可以创建以下函数
观察到。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;
}
// 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';
导出默认函数(目标:T,键:K){
让storedValue=target[key];
const get=()=>storedValue;
常量集=(值:T[K])=>{
如果(存储值!==值){
storedValue=价值;
target.notifyPropertyChange(key,storedValue);
}
};
Object.defineProperty(目标、键、{
收到
设置
可枚举:正确,
可配置:false
});
}
导出接口通知程序{
notifyPropertyChange(key:K,value:T[K]):void;
}
现在我们可以使用它从模型本身删除所有样板文件。我们甚至删除了getter和setter,并使用了简单的属性
model.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;
}
// 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';
//存根可观察类,用于验证继承是否正确工作(根据请求)
类可观测{
notifyPropertyChange(键:字符串,值:{}){
log(`from super:${key}===>${value}`);
}
}
导出类模型扩展了可观测{
从“./observed”导入观测值;
导出类模型扩展了可观测{
@观察到的名称='Bob';
@观察年龄=38岁;
@观察到的出生日期=时刻();
notifyPropertyChange(键:K,值:this[K]):void{
super.notifyPropertyChange(键,值);
log(`${key}===>${value}}`);
}
}
常量模型=新模型();
model.name='Rob';
model.name='Robert';
这种方法的一些好处是
- 天气相当干燥
- 我们有一个更加简洁易读的模型