A simple way to write MVC Javascript.

For a long time I try understand with the mind of top javascript developers works and what the vision of that professionals about javascript frameworks.

Well, now I have my self way of thinking about that, and I think who javascript it’s easy and don’t have motivation for complicate the scenary of development learning about 1000 frameworks with long documentation who in the final not help me truthy.

In this post Emulating React and JSX in Vanilla JS , Juan Carlos shows how to simulate the behavior of React with JSX. My idea is to show a slightly different approach.

Recently I’ve been working in a structure javascript simple who help me the develop web applications the most fast possible and without complications and hard paradgims.

In the next lines I’ll go try to present that structure.

Basicaly I’m work with javascript, literal templates, a routing system, native resources of html5 and best pratices of css3 for develop that structure.

before starting work, I need explain for you who I’m.

I’m a student of javascript and english and now I have engaged in learn this language. For that, if you see grammatical errors in this text. Apologise me.

Now let’s talk about the javascript structure in question.

When I started writing the code the focus wold be make a simple structure but poowerfull and very flexible. Because of that I thought in MVC structure.

That structure is a variable of MVC concepts and I’m using ES6 for that.

I wold see also a necessary way of writing reutilizable components with isolated scope that allows write short, simple and elegant code.

In the next lines you go see the class with the responsability make components.

export default class Component {
constructor (config) {
this.name = config.name;
this.template = config.template;
this.model = this.proxify(config.model, this, this.rerender);
this.params = config.params;
this.controller = config.controller;
this.routed = config.routed || false;
this.routerElement = '';
}
render (element = '') {
if (!this.routed) {
const template = document.querySelector(this.name);
template.innerHTML = this.template(this.model)
return;
}
if (element != '') {
this.routerElement = element;
const container = document.querySelector(element);
const template = document.createElement(this.name);
template.innerHTML = this.template(this.model);
container.insertAdjacentHTML('beforeend', template.outerHTML.trim())
}
setTimeout( () => this.controller(this), 300)
}
rerender (context) {
const component = document.querySelector(context.name);
const template = document.createElement(context.name);
template.innerHTML = context.template(context.model);
component.innerHTML = template.outerHTML;
}
proxify (model, context, callback) {

const self = this;
const handler = {
get (target, name) {
if(name in target) {
return target[name]
}
console.error(`key doesnot exist: <<${name}>>`);
},
set (target, prop, val) {
target[prop] = val;

callback(context);
return true
}
}
const proxy = new Proxy(model, handler);
return proxy;
}
}

The class Component provide way for configure the components to works correctly, and in this case it’s necessary provide the name of component, the path to template html and name of template to the components, the model of data to interactivity with external data information provided by back-end aplications or restfull API, a controller to interactivity with the DOM (Document Object Model), and configuring options to routed components.

This resources they are provided in the instantiation of that class. But this alone is not enought. It is necessary to provide a way of rendering and re-rendering to initialize and update the component when the data change. For that, I did the render(), rerender() and proxify() methods.

render (element = '') {
if (!this.routed) {
const template = document.querySelector(this.name);
template.innerHTML = this.template(this.model)
return;
}
if (element != '') {
this.routerElement = element;
const container = document.querySelector(element);
const template = document.createElement(this.name);
template.innerHTML = this.template(this.model);
container.insertAdjacentHTML('beforeend', template.outerHTML.trim())
}
setTimeout( () => this.controller(this), 300)
}
rerender (context) {
const component = document.querySelector(context.name);
const template = document.createElement(context.name);
template.innerHTML = context.template(context.model);
component.innerHTML = template.outerHTML;
}
proxify (model, context, callback) {

const self = this;
const handler = {
get (target, name) {
if(name in target) {
return target[name]
}
console.error(`key doesnot exist: <<${name}>>`);
},
set (target, prop, val) {
target[prop] = val;

callback(context);
return true
}
}
const proxy = new Proxy(model, handler);
return proxy;
}

The render() method is called when the application is loaded, the rerender() method is called by proxify() method when the data model of aplication changes.

You need understand the resource javascript proxy for understand how proxify() method works. But, basically the javascript proxy observe an object and when this object change the proxy reflect the changes and a method can call to execute any operation. For that, the rerender() method is called in this moment allowed the update of components in the DOM.

The next step is understand how the structure provide routed components, but, for that is necessary talk how the structure, handles routed components. For that, now I explain about App Class.

The App Class is responsible for make a initial structure of application and ensuring the correctly work of parts of application how the routing module and how that module handles the compontes injected in selected view.

export default class App {
constructor () {
this.modules = {};
this.routeElem = '';
}

init() {
this.change()
}
router () { 
this.modules.forEach(module => {
const hash = window.location.hash.replace('#/', '');            
const moduleName = hash.split('/').slice(0, 1).toString();
const reg = new RegExp(`${moduleName}`);
if (reg.test(module.path)) {
this.selectModule(moduleName, module.config)
}
});
}
selectModule(module, views) {
views.forEach(view => {
const hash = window.location.hash.replace('#/', '');
const reg = new RegExp(view.path);
if(reg.test(hash)) {
this.initComponents(view.components)
}
})
}
initComponents (components) {
if (this.routeElem) {
const routerElement = document.querySelector(this.routeElem)
routerElement.innerHTML = ''
}

components.forEach(component => {
component.render(this.routeElem)
})
}

change() {
window.addEventListener('hashchange', () => this.router(), false);
window.addEventListener('DOMContentLoaded', () => this.router(), false);
}

configModules (settings) {
this.modules = settings;
}
configRouter (config) {
this.routeElem = config.element;
}
}

In this class you can see the initial configuration provided in constructor method for to control the modules of aplication and the route element HTML.

In next lines you can see the change() method, and this method is responsible by to controll when the rendering is necessary.

The change() method observe two events provided by browsers, hashchange and DOMContentLoaded and when one this events is fired the rendering proccess it’s started, the route() method is called and the navigation of provided modules by configModule() method is started, loading the correctly module and providing the components for a specific view of according a selected route, throuth of the methods selectModule() and initComponents().

With the actual structure, is already possible configure the initial app and make reutilizable components, but we need provide a way for different components to communicate.

Now using de custom events API of javascript I can make a default strategy to construct these custom events and allow the trafic of data of a component for other components.

export default class Emitter {
constructor (name, element, options){
this.name = name;
this.element = element;
this.options = options;
this.event = this.makeEvent() || null;
}
emit () {
this.element.dispatchEvent(this.event)
}
makeEvent () {
        const config = {
detail: this.options.data,
bubbles:true,
cancelable:true
}
        return new CustomEvent(this.name, config);
   }
}

Now is necessary for you to understand the API of custom events of javascript for you understand the Emmiter class. But, basically, for construct a custom event you need to provide a name of custom event, a element to fire this event and optional configuration of behaviour for event and data transmiteted throuth of events.

The class Emmiter has three methods, the constructor with default configuration for make a event, the emit() method for dispatch the event and makeEvent() method to configure the others options of the events.

Now you just need to construct the components and provide html templates maked with literal template of the javascript for to built a powerfull and flexible application. Because that, I go talk about how make components in the next lines.

To contsruct a component with a class Component you need import and instantiate theese class, in next step you need provide the default necessary configuration to the component, (name, template, controller, data model and if is a routed component you need provide the routed option defined to true). See the code down.

const orderTemplate = (orders) => { 
return /*html*/`
<div class="container gutter">
<div class="box-title">
<i class="fa fa-home"></i> ${orders.section.title}
</div>
<div class="grid grid-size-4">

${orders.summary.map(order => {
return /*html*/`
<div class="resume">
<div class="title">
<i class="fa fa-send-o"></i> ${order.title}
</div>
<div class="description">${order.description}</div>
<a href="" class="link">
<i class="fa fa-plus"></i> Detalhes
</a>
</div>
`
}).join('')}
</div>
</div>`;
}
export default orderTemplate

The template is built with literal template resouce of javascript and because that you can have big flexibility and it’s very easy make anything, how by example, call functions, transform data, format data and much more.

import  $  from '../../utils/DOM.js';
import Component from '../../../core/Ignis.component.class.js';
import orderTemplate from './summary.order.template.js';
import orderModel from './summary.order.model.js';
import Emitter from '../../../core/emmit.js';
const SummaryOrderComponent =  new Component({
name: 'order-component',
template: orderTemplate,
model: orderModel,
routed: true,
controller: (context) => {

const orderComponent = $(context.name);

const options = {
data: {teste: 'Um teste de eventos'}
}
const addSupport = new Emitter('addSupport', orderComponent, options);
orderComponent.addEventListener('click', event => {
addSupport.emit()
})
}
})
export default SummaryOrderComponent

The controller inside of component is a tool to define behaviour and others necessary configurations in the component, how by example, fire custom events.

The context provided by controller can be used for access the data model, the html template and others configurations of component.

const orderModel = {
section:{
title:'Resume de Pedidos'
},
summary:[
{ theme:'theme-yellow', title:'Novos', description: 75 },
{ theme:'', title:'Em Andamento', description: 62 },
{ theme:'', title:'Concluídos', description: 123 },
{ theme:'', title:'Cancelados', description: 15 }
]
}
export default orderModel

The model is responsible to provide informations injeted into template html. It’s in the data model who the methods to get data provided throuth of API’s needing to be written.

Through the context provided by controller, the component can access these methods and change automatically the view selected showing the data model in view trough of the template html of the component.

For you see a simple example of application working you can clone these repository in git hub: https://github.com/devfreelex/simple-mvc
after cloned, select the master branch with git and install nodejs and http-server for to run the project with the command line http-server -o and access http://localhost:8080/#/dashboard/details in the browser url.

My final considerations:
I think every developer need learn more about javascript than about framewoks like angular, vue and react by axemple.

I think in the most of cases the use of frameworks like the citeds before can be a error principaly because of the long time for learn and because the dificulties created for them are very big.

If we learn javascript, we can develop profissional applications very powerfull without becoming a prisoner of the thoughts of others.

For me, don’t exist a unic way of write powerfull solutions, and I think valid use my self coding way without listening to the absurd criticism of other people who do not know javascript or think of other way.

I don’t now if you think how I think, but I’m just a student of javascript with desire very big for productivity and quality.

Anyway I hope this post shows you the power of javascript and how we can implement our own solutions, meeting requirements with much productivity and quality without having to resort to books and books or training about big frameworks.