There are many times when one part of the application changes, other parts needs to be updated. In AngularJS, if the $scope
object updates, an event can be triggered to notify another component. The observer pattern incorporates just that - if an object is modified it broadcasts to dependent objects that a change has occurred.
Another prime example is the model-view-controller (MVC) architecture; The view updates when the model changes. One benefit is decoupling the view from the model to reduce dependencies.
Observer Design Pattern on Wikipedia
As shown in the UML diagram, the necessary objects are the subject
, observer
, and concrete
objects. The subject contains references to the concrete observers to notify for any changes. The Observer object is an abstract class that allows for the concrete observers to implements the notify method.
Let’s take a look at an AngularJS example that encompasses the observer pattern through event management.
// Controller 1
$scope.$on('nameChanged', function(event, args) {
$scope.name = args.name;
});
...
// Controller 2
$scope.userNameChanged = function(name) {
$scope.$emit('nameChanged', {name: name});
};
With the observer pattern, it is important to distinguish the independent object or the subject.
It is important to note that although the observer pattern does offer many advantages, one of the disadvantages is a significant drop in performance as the number of observers increased. One of the most notorious observers is watchers. In AngularJS, we can watch variables, functions, and objects. The $$digest cycle runs and notifies each of the watchers with the new values whenever a scope object is modified.
We can create our own Subjects and Observers in JavaScript. Let’s see how this is implemented:
var Subject = function() {
this.observers = [];
return {
subscribeObserver: function(observer) {
this.observers.push(observer);
},
unsubscribeObserver: function(observer) {
var index = this.observers.indexOf(observer);
if(index > -1) {
this.observers.splice(index, 1);
}
},
notifyObserver: function(observer) {
var index = this.observers.indexOf(observer);
if(index > -1) {
this.observers[index].notify(index);
}
},
notifyAllObservers: function() {
for(var i = 0; i < this.observers.length; i++){
this.observers[i].notify(i);
};
}
};
};
var Observer = function() {
return {
notify: function(index) {
console.log("Observer " + index + " is notified!");
}
}
}
var subject = new Subject();
var observer1 = new Observer();
var observer2 = new Observer();
var observer3 = new Observer();
var observer4 = new Observer();
subject.subscribeObserver(observer1);
subject.subscribeObserver(observer2);
subject.subscribeObserver(observer3);
subject.subscribeObserver(observer4);
subject.notifyObserver(observer2); // Observer 2 is notified!
subject.notifyAllObservers();
// Observer 1 is notified!
// Observer 2 is notified!
// Observer 3 is notified!
// Observer 4 is notified!
The Publish/Subscribe pattern, however, uses a topic/event channel that sits between the objects wishing to receive notifications (subscribers) and the object firing the event (the publisher). This event system allows code to define application-specific events that can pass custom arguments containing values needed by the subscriber. The idea here is to avoid dependencies between the subscriber and publisher.
This differs from the Observer pattern since any subscriber implementing an appropriate event handler to register for and receive topic notifications broadcast by the publisher.
Many developers choose to aggregate the publish/subscribe design pattern with the observer though there is a distinction. Subscribers in the publish/subscribe pattern are notified through some messaging medium, but observers are notified by implementing a handler similar to the subject.
In AngularJS, a subscriber ‘subscribes’ to an event using $on(‘event’, callback), and a publisher ‘publishes’ an event using $emit(‘event’, args) or $broadcast(‘event’, args).
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
Every developer strives to write maintainable, readable, and reusable code. Code structuring becomes more important as applications become larger. Design patterns prove crucial to solving this challenge - providing an organization structure for common issues in a particular circumstance.
The design pattern below is only one of many useful patterns that can help you level up as a JavaScript developer. For the full set, see JavaScript Design Patterns.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
We could remove the
return
statements from ourSubject
andObserver
functions. The main difference is that writing using an explicitreturn { ... }
would expose our methods whereas withclass
offunction
constructors it wouldn’t (see my previous comment).JavaScript is very dynamic and has always more than one way to write the same thing with very subtle diffs. 🤷♂️
Which would be very similar to ES6’s
class
syntax:Hey, @ayeshamalik8751 and @akinyeleolat, you’re right! Just remove
this
and should work fine. I’ve made a few changes so it’s less verbose.this.observers has issue.
The above code has an error, we cannot access this.observers inside subscribeObserver and other similar methods.
this will be undefined and will give an error { Cannot read property ‘push’ of undefined }