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

时间:2017-08-14 15:00:55

标签: angular typescript angular2-observables angular-decorator

我使用Property 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);
    });
})

因为装饰器在组件实例的原型上工作,所创建的变量完全相同。

如何解决这个问题?

如果不能用装饰器来完成什么是一种优雅的替代方式?

1 个答案:

答案 0 :(得分:1)

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

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

 return (target, key, descriptor) => {

in

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);
            },
        });
    };
}