Typescript 实现可观察默认setter行为的Nativescript类

Typescript 实现可观察默认setter行为的Nativescript类,typescript,nativescript,Typescript,Nativescript,我正在研究回购协议,在使用nativescript实现可观测数据时遇到了一些麻烦 如果您看一下是如何定义的,您会发现它是一个简单地扩展了可观察的库的类。无论何时为属性定义setter方法, 手动需要调用super.notifyPropertyChange(“propertyName”,propertyValue) IMHO如果您有具有许多属性的视图模型,则此过程非常低效且容易出错 有没有办法使这项任务自动化?(可能有一个基类告诉任何setternotifyPropertyChange?) 如果没

我正在研究回购协议,在使用nativescript实现可观测数据时遇到了一些麻烦

如果您看一下是如何定义的,您会发现它是一个简单地扩展了
可观察的
库的类。无论何时为属性定义
setter
方法, 手动需要调用
super.notifyPropertyChange(“propertyName”,propertyValue)

IMHO如果您有具有许多属性的视图模型,则此过程非常低效且容易出错

有没有办法使这项任务自动化?(可能有一个基类告诉任何setter
notifyPropertyChange
?)
如果没有,您将如何处理此问题?
可观察
机制还有其他实现吗?

以下是我在两个生产应用程序中使用的内容:

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';
这种方法的一些好处是

  • 天气相当干燥

  • 我们有一个更加简洁易读的模型