This is the second day of my participation in Gwen Challenge

What is the observer model

When there is a one-to-many dependency between objects, the state of one object changes and all dependent objects are notified. This is the observer pattern.

Second, the actual scene

1. DOMThe event

The most common observer mode scenario during development is the DOM event function. Let’s start with the code:

document.body.addEventListener('click', () => {
    alert(2)
}, false)
Copy the code

When the body node is clicked, alert(2) will be triggered. From the perspective of the observer mode, it means that we have subscribed to the click event of the BDOY node. When the click event is triggered, we will receive the notification.

2. Website login

Most of the students who have done platform requirements have implemented the website login function. When different modules in the website, such as Header module /Nav module/body module, all depend on the user data obtained after login, how to implement it?

2.1 Common Practices

First look at the code:

login.succ((data= > {
    header.setAvatar(data.avatar) // Set the profile picture
    nav.setAvatar(data.avatar) // Set the profile picture in the navigation area
    list.refresh() // Refresh the list
})
Copy the code

Is this code particularly familiar? Put the dependent method in the callback function. This is how each module is added to the successful login callback function. This leads to a high degree of coupling between each module and the login module, and when an address bar module is added, the login module’s callback function has to be modified again, violating the open-closed principle.

2.2 Observer Model

Optimize the above requirements with the observer pattern.

The login module is a subscription object. Header module /Nav module/body module adds the subscription to the login module and notifies each module that subscribed to the login module when the login module changes. The code is as follows:

// Log in to the module js
// After the login is successful, the loginSucc message is published and data is transmitted
login.succ(data= > {
    login.trigger('loginSucc', data)
})

// Header module js
// Subscribe loginSucc login success message
login.listen('loginSucc'.() = > {
    header.setAvatar(data.avatar)
})

// nav module js
// Subscribe loginSucc login success message
login.listen('loginSucc'.() = > {
    nav.setAvatar(data.avatar)
})

Copy the code

The above code uses the observer mode to reconstruct the login function of the website. No matter how many business modules are added in the future, the subscription for successful login only needs to be added in the module, without changing the login module.

3. Two-way data binding

Two-way data binding can also be implemented through the observer pattern.

Bidirectional refers to the view and model. When the view changes, the model changes, and similarly, when the model changes, the view changes with it.

It is divided into the following steps:

3.1 Creating a Publish-subscribe Object

Create a publish-subscribe object to publish and subscribe messages.

  • subscrib: subscription function that calls back when other objects add subscription messagespushcallbacksObject array;
  • publish: Publish function that fires when a message is publishedcallbacksOf the messagecallback.
const Pubsub = {
    subscrib: function (ev, callback) {
        this._callbacks || (this._callbacks = {});
        (this._callbacks[ev] || (this._callbacks[ev] = [])).push(callback);
    },

    publish: function () {
        const args = [...arguments]
        const ev = args.shift()

        if (!this._callbacks) return
        if (!this._callbacks[ev]) return

        this._callbacks[ev].forEach(callback= >{ callback(... args) }) } }Copy the code

3.2 uiupdate

3.2.1 releaseuiUpdate news

Register the KeyUp/change event of the Document. When the DOM element that activates the event has the data-bind attribute, it indicates that the UI is being updated. Publish the UI update message to notify the subscriber.

function eventHander (e) {
    const { target } = e
    const { value: propValue } = target

    const propNameWhole = target.getAttribute('data-bind')
    if (propNameWhole) {
        // Publish UI update messages
        Pubsub.publish('ui-update-event', { propNameWhole, propValue })
    }
}

console.log(document.addEventListener)
document.addEventListener('change', eventHander, false)
document.addEventListener('keyup', eventHander, false)
Copy the code

3.2.2 subscriptionmodelUpdate news

All DOM elements that contain the data-bind attribute subscribe to model update messages, and the UI will be notified when the Model is updated.

// Subscribe to model update messages, after which all eligible DOM nodes are notified to update
Pubsub.subscrib('model-update-event'.function ({propNameWhole, propValue}) {
    const elements = document.querySelectorAll(`[data-bind="${propNameWhole}"] `)

    elements.forEach(element= > {
        const elementTagName = element.tagName.toLowerCase()
        const formTypeTagNames = ['input'.'select'.'textarea']
        if (formTypeTagNames.includes(elementTagName)) {
            element.value = propValue
        } else {
            element.innerHTML = propValue
        }
    })
})
Copy the code

3.3 modelupdate

3.3.1 subscriptionuiUpdate news

Subscribes to UI update messages and triggers modal updates when the UI is updated.

class Bind {
    constructor () {
        this.modelName = ' '
    }

    initModel ({ modelName }) {
        this.modelName = modelName

        // Subscribe to UI update messages
        Pubsub.subscrib('ui-update-event'.({propNameWhole, propValue}) = > {
            const [ , _propName] = propNameWhole.split('. ')
            this.updateModalData(_propName, propValue)
        })
    }
    
    // XXX omitted XXX

    updateModalData (propName, propValue) {
        const propNameWhole = `The ${this.modelName}.${propName}`
        // Publish model updates
        Pubsub.publish('model-update-event', { propNameWhole, propValue }); }}Copy the code

3.3.2 Release model update messages

When a Model update message is published, uIs that subscribed to the Model update message are notified.

class Bind {
    constructor () {
        this.modelName = ' '
    }
    
    // XXX omitted XXX

    loadModalData (modelData) {
        for (let propName in modelData) {
            this.updateModalData(propName, modelData[propName])
        }
    }

    updateModalData (propName, propValue) {
        const propNameWhole = `The ${this.modelName}.${propName}`
        // Publish model updates
        Pubsub.publish('model-update-event', { propNameWhole, propValue }); }}Copy the code

See github for the full source code

Third, summary

As you can see from the real-world example above, the observer pattern sets up a trigger mechanism that helps us write more loosely coupled code. But don’t overuse it, or it will make the program difficult to track and understand. The following scenarios apply to the observer mode:

  1. One aspect of an abstract model depends on another aspect. Encapsulate these aspects in separate objects so that they can be changed and reused independently.
  2. A change in one object will cause one or more other objects to change, and not knowing exactly how many objects will change can reduce the coupling between objects.
  3. One object must notify other objects without knowing who they are.
  4. You need to create A chain of triggers in the system where the behavior of object A affects object B, and the behavior of object B affects object C… You can use observer mode to create a chain trigger mechanism.

Github source code for practical exercises. I hope you found this article helpful. Thanks for reading ❤️ ~


· Past wonderful ·

Design Patterns – Who hasn’t seen a few singleton patterns (PART 1)

Design Patterns – What is Happy Planet and What is Strategic Pattern (2)

Design Mode – So this is the agent mode (3)

Design Patterns – Easy to Understand Observer Patterns (4)

Design patterns – No, no, there are still people who do not know the decorative pattern (five)