簡體   English   中英

如何將 changeDetectorRef 傳遞給裝飾器?

[英]How to pass changeDetectorRef to decorator?

我正在為屬性創建自己的裝飾器,該裝飾器將自動將屬性的值與 firebase db 同步。 我的裝飾器很簡單,看起來像這樣:

export function AutoSync(firebasePath: string) {
    return (target: any, propertyKey: string) => {
        let   _propertyValue = target[propertyKey];

        // get reference to db
        const _ref           = firebase.database().ref(firebasePath);

        // listen for data from db
        _ref.on('value', snapshot => {
            _propertyValue = snapshot.val();
        });

        Object.defineProperty(target, propertyKey, {
            get: () => _propertyValue,
            set: (v) => {
                _propertyValue = v;
                _ref.set(v);
            },
        });
    }
}

我是這樣使用它的:

@AutoSync('/config') configuration;

它(幾乎)就像一個魅力。 我的configuration屬性會自動與路徑/config上的 firebase db 對象同步。 屬性設置器會自動更新 db 中的值 - 效果很好!

現在的問題是:當其他一些應用程序在 firebase db 中更新數據庫中的值時,我們得到了 snapshow,並且_propertyValue值正在正確更新,但由於configuration屬性沒有直接更改,因此不會觸發 changeDetection。

所以我需要手動完成。 我正在考慮從裝飾器中的函數觸發更改檢測器,但我不確定如何將更改檢測器的實例傳遞給裝飾器。

我帶來了一個解決方法:在app.component的構造函數中,我保存了對全局窗口對象中更改檢測器實例的引用:

constructor(cd: ChangeDetectorRef) {
    window['cd'] = cd;

    (...)
}

現在我可以像這樣在我的AutoSync裝飾器中使用它:

// listen for data from db
_ref.on('value', snapshot => {
    _propertyValue = snapshot.val();
    window['cd'].detectChanges();
});

但這是一種hacky和骯臟的解決方案。 什么是正確的方法?

沒有好的方法可以將類屬性值傳遞給裝飾器。 保存對全局變量的提供者引用不僅是笨拙而且是錯誤的解決方案,因為可能有多個類實例,因此可能有多個提供者實例。

屬性裝飾器在類定義上評估一次,其中target是類prototype ,並且期望在那里定義propertyKey屬性。 _ref_propertyValue對於所有實例都是相同的,即使組件從未實例化, _ref.on調用和監聽_ref.on

一種解決方法是在類實例上公開所需的提供者實例 - 如果應該在裝飾器中訪問多個提供者,則使用injector器。 由於每個組件實例都應該設置自己的_ref.on偵聽器,因此應該在類構造函數或ngOnInit鈎子中執行。 不可能從屬性裝飾器修補構造函數,但應該修補ngOnInit

export interface IChangeDetector {
  cdRef: ChangeDetectorRef;
}

export function AutoSync(firebasePath: string) {
  return (target: IChangeDetector, propertyKey: string) => {
    // same for all class instances
    const initialValue = target[propertyKey];

    const _cbKey = '_autosync_' + propertyKey + '_callback';
    const _refKey = '_autosync_' + propertyKey + '_ref';
    const _flagInitKey = '_autosync_' + propertyKey + '_flagInit';
    const _flagDestroyKey = '_autosync_' + propertyKey + '_flagDestroy';
    const _propKey = '_autosync_' + propertyKey;

    let ngOnInitOriginal = target['ngOnInit'];
    let ngOnDestroyOriginal = target['ngOnDestroy']

    target['ngOnInit'] = function () {
      if (!this[_flagInitKey]) {
        // wasn't patched for this key yet
        this[_flagInitKey] = true;

        this[_cbKey] = (snapshot) => {
          this[_propKey] = snapshot.val();
        };

        this[_refKey] = firebase.database().ref(firebasePath);
        this[_refKey].on('value', this[_cbKey]);
      }

      if (ngOnInitOriginal)
        return ngOnInitOriginal.call(this);
    };

    target['ngOnDestroy'] = function () {
      if (!this[_flagDestroyKey]) {
        this[_flagDestroyKey] = true;
        this[_refKey].off('value', this[_cbKey]);
      }

      if (ngOnDestroyOriginal)
        return ngOnDestroyOriginal.call(this);
    };

    Object.defineProperty(target, propertyKey, {
      get() {
        return (_propKey in this) ? this[_propKey] : initialValue;
      },
      set(v) {
        this[_propKey] = v;
        this[_refKey].set(v);
        this.cdRef.detectChanges();
      },
    });

  }
}

class FooComponent implements IChangeDetector {
  @AutoSync('/config') configuration;

  constructor(public cdRef: ChangeDetectorRef) {}
}

它被認為是一個黑客

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM