Angular Property Decorator создает только один экземпляр свойства для каждого типа класса

Я использую Property Decorator для создания Observable со статическим getter/setter для каждого свойства.

В конце концов, вы можете использовать декоратор таким образом

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 ответ

Решение

Я собираюсь ответить на вопрос с решением, которое я нашел после одного дня размышлений.

Прежде всего, главная проблема, для которой я не мог получить доступ к экземпляру, заключалась в использовании функции стрелки в определении декоратора. Итак, я изменился:

 return (target, key, descriptor) => {

в

return  function (target, key) {

Таким образом, я мог получить доступ к экземпляру из getter/setter, используя this,

Затем мне нужно было найти хорошее место для инициализации BehaviourSubject. Делать это в getter или setter основного свойства не получится (я хочу получить доступ this.cats$ без доступа в первую очередь this.cats).

Так что я решил с новым геттером для cats$, Это хранит переменную в секретном свойстве и создает ее, если она не существует.

Вот окончательный код!

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);
            },
        });
    };
}
Другие вопросы по тегам