Angular Property Decorator 只为每个类类型创建一个属性实例

标签 angular typescript angular2-observables angular-decorator

我正在使用属性装饰器为每个属性创建一个带有静态 getter/setter 的 Observable。

最后就可以这样使用装饰器了

class Test {
    @ObservableProperty(DEFAULT_CATS) 
    cats: number;

    @ObservableProperty(DEFAULT_PIGS) 
    pigs: number;
}

装饰器的实际代码是

export function ObservableProperty(defaultValue = null): any {
    return (target, key, descriptor) => {
        const accessor = `${key}$`;
        target[accessor] = new BehaviorSubject(defaultValue);

        return Object.assign({}, descriptor, {
            get: function() {
                return this[accessor].getValue();
            },
            set: function(value: any) {
                this[accessor].next(value);
            },
        });
    };
}

现在,Test 组件的一个实例一切正常。

但是有两个实例这个测试实际上失败了。

fdescribe('ObservableProperty Decorator', () => {
    let test: Test;
    let doppleganger: Test;

    beforeEach(() => {
        test = new Test();
        doppleganger = new Test();
    });

    it('should create different observables for each props', () => {
        expect(test['cats$'] === doppleganger['cats$']).toBe(false);
    });
})

因为装饰器作用于组件实例的原型(prototype),所以创建的变量完全相同。

我怎样才能解决这个问题?

如果不能用装饰器完成,有什么优雅的替代方法?

最佳答案

我将用经过一天思考后找到的解决方案来回答这个问题。

首先,我无法访问实例的主要问题在于装饰器定义中箭头函数的使用。所以我改变了:

 return (target, key, descriptor) => {

return  function (target, key) {

这样我就可以使用 this 从 getter/setter 内部访问实例。

然后我必须找到一个好的位置来初始化 BehaviourSubject。在主要属性的 getter 或 setter 中执行此操作是行不通的(我想访问 this.cats$ 而不是首先访问 this.cats)。

所以我用 cats$ 的新 getter 解决了这个问题。它将变量存储在一个 secret 属性中,如果它不存在则创建它。

这是最终代码!

export function ObservableProperty(defaultValue = null): any {
    return  function (target, key) {
        const accessor = `${key}$`;
        const secret = `_${key}$`;

        Object.defineProperty(target, accessor, {
            get: function () {
                if (this[secret]) {
                    return this[secret];
                }
                this[secret] = new BehaviorSubject(defaultValue);
                return this[secret];
            },
            set: function() {
                throw new Error('You cannot set this property in the Component if you use @ObservableProperty');
            },
        });

        Object.defineProperty(target, key, {
            get: function () {
                return this[accessor].getValue();
            },
            set: function (value: any) {
                this[accessor].next(value);
            },
        });
    };
}

关于Angular Property Decorator 只为每个类类型创建一个属性实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45677476/

相关文章:

由于延迟加载模块,无法在手动输入 URL 时从 CanActivate Guard 进行 Angular 路由

angular - 为什么用catchError处理错误而不是在Angular的订阅错误回调中处理

php - MYSQL中如何保存图片路径

angular - ionic 作用表 : How to use Custom Icons from Fontawesome?

javascript - 使用 .d.ts 文件中的类型定义而不导入

javascript - 使用异步库的 Google PubSub 中的异步订阅者

angular - rxjs中sample和throttle的区别

angular - 如何使用 switchMap 而不是嵌套订阅?

javascript - 错误类型错误 : Cannot set property 'product' of null

angular - 更改 Angular 6 中的默认 html 模板