用嵌套元素实现观察者模式

问题描述 投票:0回答:1

我正在尝试在没有控制器的情况下在 JS 中实现观察者模式,IE:模型只是在其数据更改时重新呈现相关视图。

我的问题是我需要将一个

view
传递给
model
registerObserver
函数作为参考,否则我无法检查观察者是否尚未注册,这里:

if (!this.observers.includes(observer)) 
  this.observers.push(observer)

如果我无法检查观察者,它们会在每次重新渲染时不断添加,所以我在

nestedViews
属性中保留对它们的引用。但是我正在努力解决父视图也可能需要观察模型的问题 - 所以我添加了对
self
的引用。但是,如果我在模型更改时重新渲染父视图,我将丢失对嵌套视图的引用,并且会出现同样的问题。

相对于这种方法,我是 webdev 的新手(这只是一个改进我的 vanilla js 的练习),所以我确定这一定是一个我看不到的简单修复方法?

父视图:

class View {
   nestedViews = {};
   self = this;

   constructor(parentEl, model){
     this.model = model;
     this.parentEl = parentEl;
   }

   update() { this.render(); }

   render() {
     // method to render child classes
     this.onRender();
     // bind model
     this.bindModel();
    
     //... Logic to insert the item into the dom
   }
}

class NestedView extends View {
   bindModel() {
    Object.entries(this.nestedViews).forEach(([key, value]) =>
      this.model.registerObserver(value)
    );
   }
   onRender() {
    this.nestedViews['nestedView1'] = new NestedView1(
      parent,
      this.model
    );
    this.nestedViews['nestedView1'].render();
    
    this.nestedViews['nestedView2'] = new NestedView2(
      parent,
      this.model
    );
    this.nestedViews['nestedView2'].render();
   }}

型号:

class Model {
   observers = [];

   registerObserver(observer) {

   if (!this.observers.includes(observer)) this.observers.push(observer);
  }
  unregisterObserver(observer){
    this.observers = this.observers.filter((obs) => obs !== observer);
  }
  updateObservers() {
    this.observers.forEach((observer) => observer.update());
  }

  set() {
    // set attr
    this.updateObservers();
  }
}

谢谢

javascript model-view-controller observer-pattern
1个回答
0
投票

我想我理解你面临的问题。您希望避免每次模型更改时都重新渲染父视图,因为这会导致对嵌套视图的引用丢失。一种解决方案是使用单独的方法来更新嵌套视图,并从父视图的更新方法中调用它。这样,父视图只负责更新嵌套视图,而不是重新渲染它们。

这是一个示例,说明如何修改代码以实现此目的:

class View {
  nestedViews = {};

  constructor(parentEl, model) {
    this.model = model;
    this.parentEl = parentEl;
  }

  update() {
    // Update nested views without re-rendering the parent view
    this.updateNestedViews();
  }

  updateNestedViews() {
    Object.values(this.nestedViews).forEach((nestedView) => nestedView.update());
  }

  render() {
    // method to render child classes
    this.onRender();
    // bind model
    this.bindModel();
  }
}

class NestedView extends View {
  bindModel() {
    Object.values(this.nestedViews).forEach((nestedView) =>
      this.model.registerObserver(nestedView)
    );
  }

  onRender() {
    this.nestedViews['nestedView1'] = new NestedView1(this.parentEl, this.model);
    this.nestedViews['nestedView1'].render();

    this.nestedViews['nestedView2'] = new NestedView2(this.parentEl, this.model);
    this.nestedViews['nestedView2'].render();
  }
}

在这个解决方案中,父视图的更新方法不会重新渲染自己;相反,它调用 updateNestedViews,后者又调用每个嵌套视图的更新方法。这应该有助于维护对嵌套视图的引用,并防止它们被多次注册为观察者。

此外,考虑在您的模型类中使用以下经过修改的 registerObserver 方法,以便在观察者被销毁或不再需要时自动注销观察者:

class Model {
  // ...

  registerObserver(observer) {
    if (!this.observers.includes(observer)) {
      this.observers.push(observer);

      // Automatically unregister the observer when it's destroyed
      const unregister = () => {
        this.unregisterObserver(observer);
      };
      observer.onDestroy(unregister);
    }
  }

  // ...
}

在您的 View 类中,添加 onDestroy 方法:

class View {
  // ...

  onDestroy(callback) {
    this._onDestroyCallback = callback;
  }

  destroy() {
    if (this._onDestroyCallback) {
      this._onDestroyCallback();
    }
  }

  // ...
}

这样,您可以在不再需要视图时调用 destroy 方法,它会自动从模型中注销自己作为观察者。

© www.soinside.com 2019 - 2024. All rights reserved.