In this article we're going to discuss about implementing the structure of MVC framework.
Upto now we've got basic knowledge to construct the MVC framework and also we've setup structure of MVC. In the previous part we've discussed about creation of structure with some assumed basic rules.
let's recall the rules
- You should write your code which is repeatable in "Factory" and if a factory is dependant on other factory then you should be able to import it into your factory.
- These factories should be instantiated when the project gets loaded.
- Constants should be able to modified in any component like controller or factory.
- We should be able to define routes that means when particular route comes then the specified controller should get executed.
- The controllers are the functions which should be able to import factories and constants which are mapped for urls.
- These controller should get executed when the particular url comes.
Step-1:
Maintain a private JSON object to hold all the factories, constants, routers and controllers.
For example:
var MiApp = (function () { 'use strict'; //Private Data var Resource = { 'constants' : { }, 'factory' : { }, 'mode' : null, 'root' : '/', 'routes' : [], 'controller' : { }, 'controller_dependancy':{ }, }; function constants() { var key = arguments[0],val = arguments[1]; } function routes(){ var key = arguments[0],val = arguments[1]; } function controller(){ var key = arguments[0],val = arguments[1]; } function factory(){ var key = arguments[0],val = arguments[1]; } return { 'factory': factory, 'routes': routes, 'controller': controller, 'constants': constants } });
In above we've an object called "Resource" which is used to store the user defined data like factories, constants, routes and controllers.
Step-2:
Maintain an adapter to serve private functionality to publicly accessable items. This is adapter is the actual engine we need to implement to serve the stored data in Resource object. This will act as an adapter between and private and public items.
var api = { 'factory': function (key, arrayArg) { }, 'routes' : function(route, controller){ }, 'controller' : function(controller, handler){ }, 'constants': function (key, val) { } 'loadDependancies' : function(arrayArg){ }, };
Now we've a structure of storing data and serve the data to public items.
Concept of constants:
Assumed structure:
app.constants('name', function(){ return { } });
Assumed rules:
Constants should be able to modified in any component like controller or factory. Constants can be functions, objects or hybrid. So, whenever you create a constant then the instantiated object need to get stored in private object "Resource" inside the constant with the given key through adapter. Then the constant function will be
'constants': function (key, val) { resources.constants[key] = val(); },
In above we've stored the constants as private so that these are not accessable outside.
Concept of Factory:
Assumed structure:
app.factory('factory_name', ['dependancy1', 'dependacy2', function(dependacy1, dependancy2){ //you should access dependancy1 return { 'publicAccess': function(){ return "something" } } }]);
Assumed rules:
- You should write your code which is repeatable in "Factory" and if a factory is dependant on other factory then you should be able to import it into your factory.
- These factories should be instantiated when the project gets loaded.
Factories are the functions which need to be reused in another factory or controllers. And like constants we need to instantiate the factories whenever they defined and store the instantiated factory functions in Private resource because these factories should not be able to access by outside and we need to implement the adapter to instantiate, store and to serve the factories over the controllers.
But, we've discussed that factories can be used inside another factory. So before creating and storing a factory plan for injection of factory in another factory.
Concept of dependacy injection:
In order to create a factory, a factory or constant can be used inside another factory. Here we
need to inject those dependencies to the factory.
How to inject constants into a factory ?
We're maintaining a Private object called Resource to hold constants with their key name. If we've already created eonstant with the name "constant1" with value 1.
Now if we want to inject this value into factories you can access through Resource.constants[constant1] So,
app.factory('factory1', ['constant1', function(constant1){ //you should access dependancy1 return { 'publicAccess': function(){ console.log(constant1); } } }]);
Now create an adpter function to serve the dependencies from constants and factories based on keys.
'loadDependancies' : function(arrayArg){ var dependancy = [], iter; for (iter = 0; iter < arrayArg.length; iter += 1) { if (typeof arrayArg[iter] === "string") { //look in modules if (resources.hasOwnProperty(arrayArg[iter])){ dependancy.push(api.loadModule(arrayArg[iter])); } else { //look in factory if (resources.factory.hasOwnProperty(arrayArg[iter])) { dependancy.push(api.loadDependancy(arrayArg[iter])); } else { //look in constants if (resources.constants.hasOwnProperty(arrayArg[iter])) { dependancy.push(api.loadConstant(arrayArg[iter])); } else { //if it is $me scope if (arrayArg[iter] === "$mi") { dependancy.push({}); } else { console.log("Error: " + arrayArg[iter] + " is not Found in constants and Factories"); } } } } } } return dependancy; },
The above code will recieve list of names or keys. It will first look in modules, factories, constants respectively.
we'll discuss later about modules.
Simply, it will take array of strings and return array of Functions which are stored in Private object called Resource.
So the factory adapter implementation will be,
'factory': function (key, arrayArg) { var last_index = arrayArg.length-1; var dependancies = arrayArg.slice(0, -1); if (typeof arrayArg[last_index] === "function") { console.log("-"+api.loadDependancies(dependancies)); resources.factory[key] = arrayArg[last_index].apply(this, api.loadDependancies(dependancies)); // arrayArg[last_index]; } else { console.log("Nan"); } },
In the above we're storing the instantiated object in private object called Resource inside the factory.
Concept of Routes and Controllers
Assumed structure
//routes app.routes('routeurlwithregularexpression','contrllername'); //controllers app.Controller('TestContrller', ['dependancy1', 'dependancyn', function(dependancy1, dependancyn){ //your stuff that runs when the page gets loaded }]);
Assumed rules
- We should be able to define routes that means when particular route comes then the specified controller should get executed.
- The controllers are the functions which should be able to import factories and constants which are mapped for urls.
- These controller should get executed when the particular url comes.
Concept of routes
Routes are the urls those can be partial urls or normal urls ( /url or #url ). When particular route come then we need to execute the specified controller which is collection of dependencies and your logic. But when comes to the structure routes able to specfy the route name and contrller name. So, route adapter will take the route and controller and store it in the private object called Resource.
'routes' : function(route, controller){ var temp = {'path':route, 'handler':controller }; resources.routes.push(temp); },
Concept of Controllers:
Controller is a module which is capable of loading dependencies and should be loaded when particualr url come. So we should not instantiate it whenever it get created. But here the problem is we need to remember its dependencies. So,
'controller' : function(controller, handler){ var last_index = handler.length-1; var dependancies = handler.slice(0, -1); if (typeof handler[last_index] === "function") { resources.controller[controller] = handler[last_index]; resources.controller_dependancy[controller] = dependancies; } else { console.log("Nan"); } },
Loading controllers when route come:
I've found a resource to load some functionality when a particular url come from here http://krasimirtsonev.com/blog/article/A-modern-JavaScript-router-in-100-lines-history-api-pushState-hash-url by Krasimir.
I've integreated those code with this. Please refer to Krasimir article.
Concept of template binding.
We've made this code as OpenSource. So anybody can make changes in order to improve or to add some features.
Right now this is not supporting template binding.
There are libraries which are availble purely for template binding.
We're trying to integrate those libraries to this project.
Github link: https://github.com/aptuz/mi-js