Angular 属性 装饰器只为每个 Class 类型创建一个 属性 实例
Angular Property Decorator only creates one property instance per Class Type
我正在使用 属性 装饰器为每个 属性.
创建一个带有静态 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);
});
})
因为装饰器作用于组件实例的原型,所以创建的变量完全相同。
我该如何解决这个问题?
如果不能用装饰器完成,有什么优雅的替代方法?
我将用我思考一天后找到的解决方案来回答这个问题。
首先,我无法访问实例的主要问题是在装饰器定义中使用了箭头函数。所以我改变了:
return (target, key, descriptor) => {
在
return function (target, key) {
这样我就可以使用 this
从 getter/setter 内部访问实例。
然后我必须找到一个好的地方来初始化 BehaviourSubject。在主要 属性 的 getter 或 setter 中执行此操作是行不通的(我想访问 this.cats$
而不是先访问 this.cats
)。
所以我用 cats$
的新 getter 解决了问题。它将变量存储在一个秘密 属性 中,如果它不存在则创建它。
这是最终代码!
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);
},
});
};
}
我正在使用 属性 装饰器为每个 属性.
创建一个带有静态 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);
});
})
因为装饰器作用于组件实例的原型,所以创建的变量完全相同。
我该如何解决这个问题?
如果不能用装饰器完成,有什么优雅的替代方法?
我将用我思考一天后找到的解决方案来回答这个问题。
首先,我无法访问实例的主要问题是在装饰器定义中使用了箭头函数。所以我改变了:
return (target, key, descriptor) => {
在
return function (target, key) {
这样我就可以使用 this
从 getter/setter 内部访问实例。
然后我必须找到一个好的地方来初始化 BehaviourSubject。在主要 属性 的 getter 或 setter 中执行此操作是行不通的(我想访问 this.cats$
而不是先访问 this.cats
)。
所以我用 cats$
的新 getter 解决了问题。它将变量存储在一个秘密 属性 中,如果它不存在则创建它。
这是最终代码!
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);
},
});
};
}