-
Notifications
You must be signed in to change notification settings - Fork 0
Home
In modern web if you want not just a static pages returned by server you need to write client side code on Javascript.
At first this is of course just simple animations. However, as you app becoming more and more you need to write more client side code, and now it's not just simple animation, you now use ajax for give data from server and so on.
Over time your code will be so large that, if required to changed it can be very difficult. Then you can think about using some of popular frameworks. Of course all of them great decide problems with data visualisation and storage, provide simple methods to work with ajax and so on.
But what about interaction? How are you manage model and views ( will talk in MVC terms ). For this purposes there have controller. In controller you can manage all your views and models. But when you app is growing controller grow too. At same point your controller will so big that making changes become impossible to do, when you change something in one part of application in another part something breaks. This is fundamental problem with which you can encounter.
Some simple method to change it system it is create parent controller calling mediator that redirect requests to child controllers. Main problem whith this is that mediator need to know about all another controllers. This system is tigh-coopling. We need to do it loose-coupling, but before we do it let's talk about javascript.
Maybe it sounds weird but largest part of time javascript client side program doing... nothing! That's true, code on javascript wait user interaction or data from server ( when it ajax or socket ). Biggest part of clients code is callbacks for events. And this is nature of javascript - he is asynchronous. But not only callbacks make it asynchronous, it is expressed in the language structure. All javascript code work in one thread in main loop. This is really big talk so don't talk about it in detail now. Just remember, nature of javascript is an asynchrony.
But we can do more than listen browser, we can create your own custom events and listeners for it.
For example: we have object called Emitter which have method on to listen events and have method trigger to call event listeners.
function someGreatFunction() {
...
}
/**
* This is sync code
**/
someGreatFunction();
/**
* This is async code
**/
//register event
Emitter.on( 'some_event', someGreatFunction );
//call it
Emitter.trigger( 'some_event' );That's it! Right now we create custom event and call function asynchronous! But how it's help us to do our system better? Ok, let's change our example
function someGreatFunction() {
...
}
function anotherGreatFunction() {
...
}
function andOneMoreGreatFunction() {
...
}
/**
* This is sync code
* We need to know about all that functions
**/
someGreatFunction();
anotherGreatFunction();
andOneMoreGreatFunction();
/**
* This is async code
* We associate all functions with event
**/
Emitter.on( 'some_event', someGreatFunction );
Emitter.on( 'some_event', anotherGreatFunction );
Emitter.on( 'some_event', andOneMoreGreatFunction );
/**
* Right now we can call all functions just to trigger event!
* We can do it multyple times and what is more important
* we don't need to remember all function, we just know about
* this event. You can call all this functions for example in another module
* all what you need know is event name
*/
Emitter.trigger( 'some_event' );That's example show how events help to make our system more loose-coupling.
Return to MVC and to our improved version with mediator. See it structure in picture:

And code:
mediator.js
import Controller1 from './controller1.js';
import Controller2 from './controller2.js';
import Controller3 from './controller3.js';
(function Mediator() {
var controller1 = new Controller1();
var controller2 = new Controller1();
var controller3 = new Controller1();
//for example mediator get request and choose what to do
$.ajax({
method: "GET",
success: function ( data ) {
switch( data ) {
case 1: controller1.doIt(); break;
case 2: controller2.doIt(); break;
case 3: controller3.doIt(); break;
}
}
});
}())controller1.js
function Controller1() {
this.doIt = function () {
...
}
}controller2.js
function Controller2() {
this.doIt = function () {
...
}
}controller3.js
function Controller3() {
this.doIt = function () {
...
}
}As I already say the main problem of this architecture is that mediator need to know about all childs controllers. How to improve it? Let's use events! Now our structure have little changes:

And refactor code:
mediator.js
import Emitter from './Emitter';
(function Mediator() {
//for example mediator get request and choose what to do
$.ajax({
method: "GET",
success: function ( data ) {
switch( data ) {
case 1: Emitter.trigger('data-first'); break;
case 2: Emitter.trigger('data-second'); break;
case 3: Emitter.trigger('data-third'); break;
}
}
});
}())controller1.js
import Emitter from './Emitter';
function Controller1() {
this.doIt = function () {
...
}
Emitter.on( 'data-first', this.doIt );
}controller2.js
import Emitter from './Emitter';
function Controller2() {
this.doIt = function () {
...
}
Emitter.on( 'data-second', this.doIt );
}controller3.js
import Emitter from './Emitter';
function Controller3() {
this.doIt = function () {
...
}
Emitter.on( 'data-third', this.doIt );
}What we do? We do our system loose-coupling. In this system all three controllers it is independent mini apps which only listen events. And what is more important our mediator even does not know about them! For example if I want to create fourth controller which want to use data-first event I can just create it and listen this event, I don't have change mediator!
All of this is controllers is components (Wikipedia - the constituents of a system) with his own incapsulated logic. To tell the truth the mediator is also component and in system hierarchy equal to controllers. Now we would call all of them component. Each component must create and listen events by global emitter. Now our architecture looks like this:

And that's it! This is idea of components and why this is help to build better systems. To use it right now it's only necessary library to create custom events. Or not..?
Ok, I just show example
Emitter.on('do-it-event', function ( data ) {
if( data ) {
//need data from another component
Emitter.trigger( 'event-to-another-component' );
Emitter.once( 'event-from-another-component', function ( anotherData ) {
//and maybe from another component
Emitter.trigger( 'another-one-event', anotherData );
Emitter.once( 'from-another-one-event', function ( anotherOneData ) {
...
} );
} );
} else {
Emitter.trigger( 'event-to-show-error', new Error( 'something bad' );
}
} );Do you agree that it's doesn't look good? To fix this we need something different and in this case look pretty concepts of signals and slots. This approach can help to describe component interface. Signals - events which components can trigger. Slots - events listeners. And that's all! Look at previous example now with signals and slots:
class Component extends BaseComponent {
constructor() {
super()
}
signals() {
return {
'trigger@event-to-another-component': 'toAnotherComponent',
'trigger@another-one-event': 'anotherOneEvent',
'trigger@event-to-show-error': 'error'
}
}
slots() {
return {
'on@do-it-event': function ( data ) {
if( data ) {
this.emit.toAnotherComponent();
} else {
this.emit.error( new Error( 'something bad' ) );
}
},
'once@event-from-another-component': function ( anotherData ) {
this.emit.anotherOneEvent( anotherData );
},
'once@from-another-one-event': function ( anotherOneData ) {
...
}
}
}
}Now we have visual way to see component entry and output interface points. And this is what base-components do. Expand idea of components with signals and slots.