MVC Pattern

In our MVC pattern, the Controller acts as a mediator to interact with the View and model: The View layer triggers actions that modify the model through the Controller, which in turn updates the View through the Controller.

So in observer mode, we can transform our Model into an observable, and our View into an observer. When the observed changes, it informs the observer of its current state, and then the observer updates himself according to this state. This makes data flow a one-way data flow. In this case, instead of updating the view, the View updates itself based on the Model.

Here we use Model as our observed interface and View as our observer interface. Unlike the event listener interface, which is implemented through the DOM API, the event interface is more abstract. The naming of all methods is not particularly important, but how they interact.

We give the Model class a notifyAll and the View an Update. We also give Model a method for registering observers called registerOberver, and in the Model class there is an array for hiding observers. Once the Model and view are instantiated, we use registerObserver to register the view. This way, whenever the Controller changes the model, we can call notifyAll() on the model. The method now iterates through all observers, calling update() on each obsever, and passing the state of the Model as a parameter to the View.

Model

function Model() {
    const self = this;
    this.heading = "hello"; This. Observers = []; // Add observer to observer collection this.registerObserver =function(observer) { self.observers.push(observer); } // Iterate over the observer, calling the update() method this.notifyall = on each observerfunction() {
        self.observers.forEach(function(observer) { observer.update(self); }}Copy the code

View

function View(controller) {
    this.controller = controller;
    this.heading = document.querySelector("#heading");
    this.heading.addEventListener('click',controller);
    this.update = function(data) {
        this.heading.innerText = data.heading;
    }
    this.controller.model.registerObserver(this);
}
Copy the code

Now instead of MVC changing the text through the Controller, the View updates itself after the Model is updated. Our main function still works just as it did in the previous article.

There are several improvements or simpler implementations in our discussion above:

  • GetterandSetterThe logic can be included in the Model so that we can use it to trigger notifications when values in the Model are updated. Instead of doing it manually in the Controller.
  • We can use the State mode to abstract the heading value. In this case, we can trigger “hello” and “world” in both directions. It’s not just a one-way trigger.

Object.defineproperties () differs from the simple life getHeading() and setHeading() methods in that we can use object.defineproperties () to do some metadata programming. With this static object method, we can all pass “dot” or “bracket” methods (i.e. Object. Property or object[“property”]) or assign values to the object attributes together. You can use the above methods to accomplish many meaningful things, but these are beyond the scope of the article.

We actually use Object.defineProperty() instead of Object.defineProperties() because we only care about a property, But both (Object.defineProperty() and Object.defineProperties()) are the same in principle. Now we write this in object creation:

function Model() { const self = this; // Heading is no longer an attribute, but a local environment variablelet heading = "hello"; This. Observers = []; // Add observer to observer collection this.registerObserver =function(observer) { self.observers.push(observer); } // Iterate over the observer, calling the update() method this.notifyall = on each observerfunction() {
        self.observers.forEach(function(observer) { observer.update(self); } // Through this, this is the object.object.defineProperty we want to influence (this,"heading",{
    get: function() {returnheading; },set: function(value) { heading = value; // Call notifyAll this.notifyall (); }})}Copy the code

We call in the constructor, pass this in the target, ‘heading’ as the value/key name of the object, and we use this fetch or assignment, define get() and set() to make the object configurable. Note that instead of defining the heading attribute directly on this, we specify a scope environment variable that allows configurable objects to pass through the fetch. Now we can call notifyAll with the setter, so whenever we assign a value to the heading, the View can be updated.

(This is similar to _getitem_ and _setitem_ in class definitions, if you’re familiar with Python.)

This also works, we need to remove self.model.notifyall ().

The Observer Pattern with Vanilla JavaScript