dhxmvp
v0.0.94
Published
A complete boilerplate for building online, offline and syncable MVP Single Page Applications using DHTMLX.
Downloads
17
Maintainers
Readme
dhxMVP
dhxMVP is a boilerplate system for building online, offline and syncable Javascript MVP Single Page Applications using DHTMLX.
Some print screens
Why another MV* implementation?
The DHTMLX framework, the main actor on the UI side of this project, does not implement any MV* model to create applications, or even implement any complex design pattern in their component calls.
Rather than the facade pattern used in several frameworks to init components, generally, DHTMLX pragmatically set up components line by line
, by explicit calling component methods and passing the proper arguments on each call. That is their default model
. Of course it also let you to set up your component using facade.
It makes DHTMLX simple and powerful. It let starters javascript programmers to build Enterprise Javascript applications.
Another reason is, actually there is no MV* framework or library which let you to build MV* aplications focused in DHTMLX components and using the same paradigm
, or even similar in the way they were created.
DHTMLX fits to a paradigm that is very similar to other frameworks like EXT JS (Senha actually), Dojo, Qooxdoo and others in term of how to implement applications using the Component Driven Developmemnt
approach.
Frameworks like DHTMLX provides mature and tested components API, rather the model where you create your own components.
There is no problem with the paradigm where you create your own components, it can be great for small applications or when you have sufficient time to start from the scratch to create components mature like the components which already exists in frameworks for Enterprise applications.
Frameworks like Angular, React an others, render views
by using predefined
or declarative
plain HTML templates.
Frameworks like DHTMLX provides built in
components where you can consume it APIs. The HTML, by itself, is generated in runtime, when the code which call the components is executed.
Frameworks for Enterprise Applications keep the developer focus on the business logic, avoiding time wasting to create HTML, CSS, Cross Browser hacks and several other problems which already are solved by those solutions.
You may be wondering: "What about concepts like reaction
and data binding?"
Don't worry. Take a deep look into DHTMLX documentation and you will see those concepts are nothing new.
Framerworks for Enterprise Javascript Applications are the first citizens of the Javascript Development World
.
There is nothing you can not do
with those frameworks, certainly they may not be the best choice for a small POC or small application, but they are the best choice in several scenarios which I will not discuss here.
As DHTMLX offers rich components through simple interfaces (API) with a great docs, it may be a right choice for small application too. Not only for Enterprise applications.
Although, generally, a Enterprise application has it code increasing on it lifetime, resulting in a big codebase. In this scenario, you may see a lack in DHTMLX that is it does not encourage any pattern for development. Requiring from the developers to create their own application foundation.
Another common problems are:
Giant Javascript modules/classes
Are harder to maintain and get new people envolved on it development
Load the whole app in just one file
methodologyEncouraged in top most several modern frameworks may not work for Enterprise applications, where you may have
2, 3, 4, 5 or even more
MB loading at the same time before application starts.Keep clients data in sync and in realtime
The succes of a online SPA directly rely on how and when data are displayed on the view
The dhxMVP boilerplate solves all those problems by using dhx.ui.mvp
, a MVP framework.
dhx.ui.mvp
let you to declare your routes and it will call it proper presenter
and view
associated to it, automatically creating references to the model on each view and presenter.
For each route, you declare (will use route as file name if not declared) a view
and a presenter
, but not least, you may also to attach any javascript file to the view scope.
In the view, you basically declare DHTMLX component settings and it calls.
In the presenter, you orchestrate your view, meanwhile you may implement and provide any required helper method.
Models are decoupled
, completely independent
and framework agnostic
Javascript Objects. Models are bound to the dhxMVP application through a Engine\Data Driver
.
Engines
or Data Drivers
, are JavaScript Modules which defines certain logic of how about data is accessed, manipuled and stored.
To solve the problems regarding loading performance, dhx.ui.mvp
implements a on demand loading
model for loading your javascript views and presenters, let the end user to load it on their browsers only when they really need that file on their cache.
Kit installation
List of the softwares you need to have installed in your computer to use the whole features of the development kit.
Install process:
Via Github repo
Clone this repository to a given directory, lets assume: /Users/YourName/apps/
$ cd /Users/YourName/apps/
$ git clone https://github.com/web2solutions/dhxMVP.git
Or Download this repository as zip and uncompress it at /Users/YourName/apps/
$ cd dhxMVP
$ npm install --global gulp-cli
$ npm install
Boilerplate architecture
The File system for a properly fresh install version of dhxMVP should be something like:
apps/
|---- dhxMVP/
|---- assets/
|---- cache/ -> automatically created when creating Application installers
|---- coverage/ -> Instabul coverage files
|---- deps/
|---- dhx
|---- thirdparty
|---- dist/ -> automatically created when running $ gulp dist
|---- dist_chrome_app/ -> automatically created when running $ gulp dist
|---- dist_chrome_extension/ -> automatically created when running $ gulp dist
|---- dist_electron/ -> automatically created when running `$ gulp dist`
|---- installers/ -> automatically created when creating Application installers
|---- lib/
|---- model
|---- collections
|---- questions.js
|---- users.js
|---- engines
|---- backboneIDB.js
|---- models
|---- question.js
|---- user.js
|---- presenter
|---- presenter.js
|---- view
|---- view.js
|---- node_modules/ -> automatically created when installing the Boilerplate system
|---- test/
|---- .gitignore
|---- .travis.yml
|---- boilerplate_sidebar.html
|---- boilerplate_sidebar_chrome.html
|---- electron.js
|---- google_app_backgound.js
|---- google_app_manifest.json
|---- google_extension_manifest.json
|---- Gulpfile.js
|---- LICENSE
|---- package.json
|---- Readme.md
What the boilerplate provides?
This boilerplate provides:
features to build full featured offline applications with powerful local database using IndexedDB.
features to build full featured syncable applications using a Message Mediator working over a
remote
provider. PubNub is the first built in remote provider.
For future releases, this boilerplate will also provides a complete REST back end to be consumed by your client side application, in this way, letting you to build offline, online and syncable applications.
The currently implemented features are:
Foundation for Single Page Applications
- Application script
- Main View
- Initially Using DHTMLX Sidebar as application
wrapper and navigation
- Initially Using DHTMLX Sidebar as application
- Main Presenter
- Demo Model
- Initially using Backbone.js and indexeddb-backbonejs-adapter
- Demo routes
- Main view
- Main presente
- /help
- view
- presenter
- Online Data sync
Code Validation and Automation Suite
gulp jshint - Run jsHint against application code
gulp dist
- Run jshint (The dist will not be executed if is there any programmimng error reported by jshint)
- Create minified version of the JavaScript files used by the application
- Create distributable web version
- Create distributable Google Chrome application version
- Create distributable Google Chrome extension version
- Create distributable Electron version
gulp test - Unit Tests
gulp git-init - init git on the repository
gulp git-add - prepare staged content to the next commit
sudo gulp git-add-commit-push - prepare staged content, commit and push to the repository
gulp git-push - push commit from Origin to Master branch
sudo gulp build - Build distribution. Performs several tasks over the code. If any task breaks, the upcoming task will not be executed. The tasks order is:
- dist
- test
- git-add-commit-push
gulp build-installer-mac - generate a Mac installer of the current build.
gulp build-installer-windows - generate a Windows installer of the current build.
gulp build-installer-linux - generate a Linux installer of the current build.
Note about git
features:
You will need GIT LFS
to push large files to the repository.
Please install it. Check the doc at Git-LFS repo at Github
How to use the boilerplate to get my application done?
Setup development environment
- Give a name to your application.
Open the file ./package.json
and alter the the following properties.
Example:
{
"name": "MyApplicationName",
"version": "0.0.0",
"description": "Provide a description here.",
"keywords": [
"Your Keyword 1"
],
"author": {
"name": "Your Name",
"email": "Your E-mail"
}
}
Note:
The version property
shall to be set to 0.0.0
. On every time you make a distribution of your application by running gulp build
, the application version will be automatically incremented.
- Rename the Application directory
The currently directory name is dhxMVP
. Change it by providing your application name.
For example
dhxMVP/
--> MyApplicationName/
Start the development server:
$ cd MyApplicationName/ $ gulp start-development-server
Now open your browser and reach http://localhost:9999/boilerplate_sidebar.html
Note about boilerplate_sidebar.html
This is the demo html file which uses DHTMLX Sidebar as the main Application wrapper and navigation component
.
On a future version I will release a new demo example using DHTMLX Layout
and DHTMLX Toolbar
as the main Application wrapper and navigation components
- Set up a new project on your prefered HTML5 and Javascript IDE:
I'm using Sublime Text here and it provides a excellent plugin to run gulp
directly from the IDE rather than requiring from you to run the gulp commands
via terminal
. Atom is a excellent choice too.
- Setup a new git repository for your application.
On this step you will need a github account and it client installed locally.
You may use the Github client to setup the new repository.
Or you may prefer to use gulp
to init it:
$ cd MyApplicationName/
$ gulp git-init
The application
html
The HTML code wich is reponsible for loading your dhxMVP application should looks like the following code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MVP DHTMLX Demo app</title>
<link rel="icon" href="assets/images/favicon.ico">
<!-- deps -->
<script src='deps/dhx/min.shim.js'></script>
<script src='deps/dhx/min.dhx.js'></script>
<script src='deps/dhx/dhx.ui.mvp.js'></script>
<!-- app -->
<link rel="stylesheet" type="text/css" href="assets/css/app.css">
<script src='boilerplate_sidebar.js'></script>
<!-- app -->
</head>
<body>
</body>
</html>
The application
script
The application script
is the start point of you dhxMVP application. On that, you will declare your application and routes.
document.addEventListener('DOMContentLoaded', function() {
//$dhx.environment = "production";
var dhx_application = $dhx.ui.mvp.application.extend({
initialize: function(options) {
//$dhx.debug.log('called initialize from factory');
//$dhx.debug.log('app initialized from ' + options.from );
}
}),
dhx_router = $dhx.ui.mvp.router.extend({ }),
router = new dhx_router({});
// lets use the events
dhx_application.on('before:start', function(options) {
console.log('fired app.onBeforeStart event 1');
//$dhx.debug.log('options ', options);
return true; // cancelable
});
dhx_application.on('start', function(options) {
console.log('fired app.onStart event');
//$dhx.debug.log('options ', options);
});
dhx_application.on('render', function(options) {
console.log('fired app.onRender event');
//app.main_view.dispatch( '/help' );
//$dhx.debug.log('options ', options);
});
// instantiate $dhx MVP application and router
var app = new dhx_application({
appId: "MV* DHTMLX Demo app",
container: document.body,
root: '',
//icons_path: '../assets/icons/64/', // not mandatory, default root + 'assets/icons/64/'
//deps_path: '../deps/', // not mandatory, default root + 'deps/'
//lib_path: '../lib/', // not mandatory, default root + 'lib/'
model:{
engine: 'backboneIDB',
models: [
"user",
"question",
"pet"
],
collections: [
"users",
"questions",
"pets"
]
}
});
/*
How to declare a route:
Properties:
url: Route address starting with "/". Mandatory
view: will assume url.replace(/\//g,'') as view name if view not explicitly defined
presenter: will assume url.replace(/\//g,'') as presenter name if presenter not explicitly defined
method: implicity call view.start() if not explicitly defined
router.route({
url: '/route_name',
view: 'view_file_name',
presenter: 'presenter_file_name',
method: 'start_method_name', // implicity call presenter.start() if not explicitly defined
append_views: [
{ "chatter" : 'chatter_view' }
]
});
*/
router.route({
url: 'help/:id:',
view: 'help',
presenter: 'help',
});
app.start({
backboneIDB: true, // require files used by backboneIDB engine
$dhx_form: true, // require dhx form helpers
$dhx_grid: true, // require dhx grid helpers
$dhx_crypt: true // require dhx crypt helpers
});
});
The application main
View
The Main View
on a dhxMVP application is JavaScript Module that is reponsible to render all the components that are shared by all the child views
.
Initially, it need to provides a wrapper
component which will be used as base to construct our entire application. All application componentes will be attached to the wrapper component
.
As wrapper component, we may use DHTMLX Sidebar and DHTMLX Layout.
Still yet initially, we also need a navigation
component which is responsible to provide navigation
feature to the end user meanwhile it dispatchs routes
(inject child views and presenters) into the application.
When using DHTMLX Sidebar
as main application wrapper component
we already have the navigation component
included.
When using DHTMLX Layout
as main application wrapper component
we will need to set up a navigation component
, and for this case we may use:
- DHTMLX Toolbar
- DHTMLX Menu
- DHTMLX Ribbon
As a Application Main View
you can assume it as what the end user sees when application starts
, nothing more.
The application Main View
shall to provide the following mandatory methods:
render
Used call methods which calls DHTMLX components and render the View on browser.
onDispatch
event function which is triggered always when a route is dispatched.
initialize
Used to perform any task before rendering the view
It should looks like the following:
$dhx.ui.mvp.views.declare({
"view": (function() {
'strict';
var route = '#',
main_view = $dhx.ui.mvp.main_view.extend({ }),
view = new main_view({
/**
* [initialize view. Called before view is rendered]
*/
initialize: function(options) {
console.log('VIEW:: called initialize from view.initialize');
console.debug.log(this);
},
/**
* [onDispatch event. Called each time a route is dispatched via main_view.dispatch() ]
*/
onDispatch:function( route ) {
var self = this;
self.sidebar.cells( route ).setActive();
},
/**
* [render the view. Called once application starts]
*/
render: function( render ) {
var self = this;
}
});
return view;
}())
});
A more complete Main View
demo code may be viewed here
The main view
is represented by the #
route and it is executed only one time
in the entire application lifetime when the same starts.
The application main view
code resides in the following path: MyApplicationName/lib/view/view.js
.
NOTE
The name of the file is mandatory to be view.js
. Don't change it.
The application main
Presenter
In order to take advantage of the MV*
development model, in the Main Presenter
we orchestrate the Main View
operations and tasks.
In the Main Presenter
we could define function event handlers
for the components defined in the 'Main View'.
Not least, we could start the application database or provide any 'help method' to be used into the Main View
.
In this way, we keep the code separated in accordance with it type of usage.
The application Main Presenter
shall to provide the following mandatory methods:
start
Called when the route code starts.
destroy
Used to destroy components when unloading the entire application
subscriber
Used to receive messages from Mediator. Used to perform actions on presenter or view when requested by Mediator
It should looks like the following:
$dhx.ui.mvp.presenters.declare({
"presenter": (function() {
'strict';
var API = {
start: function() {
var self = this,
view = self.view;
$dhx.debug.log('MAIN:PRESENTER: start from MAIN:PRESENTER');
},
destroy: function() {
$dhx.debug.log('MAIN:PRESENTER: destroy from MAIN:PRESENTER');
//$dhx.debug.log(this._view);
},
subscriber: function( event, message ){
var self = $dhx.ui.mvp.presenters.get('presenter'),
view = self.view;
}
}; // end API
return API;
}())
});
A more complete Main Presenter
demo code may be viewed here
The main presenter
is represented by the #
route and it is executed only one time
in the entire application lifetime when the same starts.
The application main presenter
code resides in the following path: MyApplicationName/lib/presenter/presenter.js
.
NOTE
The name of the file is mandatory to be presenter.js
. Don't change it.
The application Model
The client side data layer of a dhxMVP is basically divided in 3 parts:
- Models
- Collections
- Data driver or Engine
Models
Models are plain Javascript objects which:
- Maps to the data of a JSON document of a db collection.
- Define
Document Property
names. - Define types of a
Document Property
. - Define validation rules of a
Document Property
- Define UI related features of a
Document Property
. - Define Database (client) related features of a
Document Property
. - Define Database (server) related features of a
Document Property
.
Models are saved into the following directory:
./lib/model/models
A Model shall to provide at least 2 properties:
- __v Integer property used to transparently set versions of a document. NOTE: Don't set it directly when saving or updating a model.
- _id String property to provide sync support for Mongo on a future version of dhxMVP. NOTE: Don't set it directly when saving or updating a model on a fully offline environment. Only set it when using online data fetched from a Mongo.
Let's see a model which defines the JSON Document Schema for a question
Document of a collection of questions
to be used to implement a view of a Frequent Asked Questions
section.
var question = {
/*
* mandatory, don't change it
*/
__v: $dhx.ui.mvp.model.helpers.schema.defaults.__v,
/*
* mandatory, don't change it
*/
_id: $dhx.ui.mvp.model.helpers.schema.defaults._id,
/*
* question property. Stores a question string.
*/
question: {
/*
* Property type. Types are Javascript types. It may be:
* number, string, array, date
*/
type: 'string',
/*
* Property default value.
* Value used for automatically associate to this property when inserting a
* document which does not explicity declare this property
*/
default: '',
/*
* Property uniqueness.
* true or false. Define if the value stored on this property shall to be unique
* in the entire document collections
*/
unique: false,
/*
* Property validation.
* Javascrip object which define validation aspects to be used when manipulating documents
* on client and server side.
*/
validate: {
/*
* Is Property required?.
* Boolean value which defines if this property shall to have a associated value to it on every time
* you save or update this model.
*/
required: true,
/*
* UI field masking.
* define a mask name to be applied to a field which represents this property on the UI.
* undefined or ('') for none.
* other values:
* currency
* integer,
* credit_card,
* number,
* cpf (Brazilian SSN),
* cnpj (Brazilian Cadasro nacional de Pessoas Jurídicas)
* cep (Brazilian Zip Code)
* us_phone
* br_phone
* expiration_date
* ssn
* time
*/
mask_to_use: undefined,
/*
* Validation rules
* Are rules to test the property value against to, whenever you try to save or
* update the model (on client and server side) or submit a form.
* Separated comma list of rules
* Values may be:
* Empty, NotEmpty, ValidEmail, ValidInteger, ValidFloat, ValidNumeric
* ValidAplhaNumeric, ValidDatetime, ValidDate, ValidTime, ValidCurrency
* ValidSSN, ValidExpirationdate
*/
rules: 'NotEmpty,ValidNumeric'
},
/*
* Property UI related definitions.
* Javascrip object which define aspects related to this property on the User Interface
*/
ui: {
/*
* Expose a explanatory text on UI side related to that document property.
* Adds a text tooltip to the control related to that document property on the UI side.
*/
note: 'This is a user question',
/*
* Define a maxlength property value to be effective in any control on UI side
* related to that document property.
*/
maxLength: 100,
/*
* Aspects of this property on a UI Form
*/
form: {
/*
* Form text label
*/
label: 'Question',
/*
* Form input type
*/
type: 'input',
},
/*
* Aspects of this property on a UI Grid
*/
grid: {
/*
* Header text value on a grid
*/
header: 'Question',
/*
* Column alignment on a grid.
* String: left, right, center.
*/
align: 'left',
/*
* Column type on a grid.
* It is a DHTMLX Grid column type.
* Please refer to the DHTMLX docs for original types:
* -> http://docs.dhtmlx.com/grid__columns_types.html
* Another implemented column types are:
* UScurrency, ssn, USphone
* BRcurrency, BRphone, cep, cpf, cnpj
* integer, time
* moment_calendar, moment_to, moment_from, moment_fromNow, myprice
*/
coltype: 'ro',
/*
* Column width on a grid
* Integer values or * for automatically sizing
*/
width: '*'
}
}
},
answer: {
type: 'string',
default: '',
unique: false,
validate: {
required: true,
mask_to_use: undefined,
rules: 'NotEmpty'
},
ui: {
note: 'This is answer for the question',
maxLength: 1000,
form: {
label: 'Answer',
type: 'input',
},
grid: {
header: 'Answer',
align: 'left',
coltype: 'ro',
width: '0'
}
}
}
};
Collections
Collections are like a table on a RDBMS. Instead storing rows, it stores JSON documents.
In a dhxMVP application, a collection is a plain Javascript Object and it looks like the following code:
var questions = {
model: 'question'
};
Collections are saved into the following directory:
./lib/model/collections
Data driver - Engines
Models are
decoupled
,completely independent
andframework agnostic
Javascript Objects. Models are bound to the dhxMVP application through aEngine\Data Driver
.
Engines
orData Drivers
, are JavaScript Modules which defines certain logic of how about data is accessed, manipuled and stored.
Actually, the unique Engine
implemented uses Backbone to Create
and Handle
, Models
and Collections
using the predefined models. It also uses the indexeddb-backbonejs-adapter
plugin to persist data on a local database powered by IndexedDB.
If you are planning to implement a online and realtime Single Page Application, the currently implemented engined backboneIDB
provides to you automatically sync data between connected clients. It transparently keep the local IndexedDB
database of all connected clients in sync using a online Pub/Sub provider (currently implemented over PubNub only).
If you are planning in to build a offline application, the Pub/Sub will only provide local communication between application components.
The Engine automatically maps in runtime, the whole defined Models
and Collections
, and build a input
and output
CRUD style API to provide standardized data access and manipulation to the application. Rather than directly manipulate Models
and Collections
, you will count with a clear and well defined API which covers all your models and collections.
The advantage of implementing a Data Driver
consist in providing proxyed data access to the models. In other words, you have total freedom to create a new Data Driver that defines a completely different data access
, manipulation
and storage
rules and provide support to different environments and stacks.
A public API of a Engine
should looks like the following code:
It should looks like the following:
$dhx.ui.mvp.model.engine.declare({
"backboneIDB": (function() {
'strict';
var API = {
database:{
description: '',
id: ''
},
subscriber: function( event, message ){
},
schema:{
add_all_records_from_server: function(){},
/*
* Namespace: module.schema.io
* Provide a CRUD like input and output API for each defined collection
*/
*/
io:{
questions:{
create: function(){},
destroy: function(){},
read: function(){},
update: function(){}
},
users: {
create: function(){},
destroy: function(){},
read: function(){},
update: function(){}
},
books: {
create: function(){},
destroy: function(){},
read: function(){},
update: function(){}
},
},
start: function(){}
},
start: function(){}
}; // end API
return API;
}())
});
NOTE
The above code is just a simple example of a Public API for a Engine. This example is not defining any private
logic which implements that API.
Engines are saved into the following directory:
./lib/model/engines
A more complete engine
demo code may be viewed here
Declaring routes
Routes are declared inside the file MyApplicationName/lib/app.js
.
When you declare a route, you are defining 4 things inside a dhxMVP application:
- a hash url to be dispatched
- a
Child Presenter
file name to be injected when dispatching the route - a
Child View
file name to be injected when dispatching the route - A array of names of Javascript modules to be injected into the route scope. You may use the injected code inside your
Child view
.
Routes may be dispatched through 2 different ways:
- Calling the
dispatch()
method from theMain View
.
This method is automatically created when you declare your Main View
. Don't declare it again. Example:
On the Main Presenter
$dhx.ui.mvp.presenters.declare({
"presenter": (function() {
'strict';
var API = {
start: function() {
var self = this,
view = self.view;
$dhx.debug.log('MAIN:PRESENTER: start from MAIN:PRESENTER');
},
destroy: function() {
$dhx.debug.log('MAIN:PRESENTER: destroy from MAIN:PRESENTER');
//$dhx.debug.log(this._view);
},
dispatch_help_route: function() {
var self = this,
view = self.view;
view.dispatch('/help');
}
}; // end API
return API;
}())
});
On a Child Presenter
$dhx.ui.mvp.presenters.declare({
"help": (function() {
'strict';
var API = {
start: function() {
var self = this,
view = self.view;
$dhx.debug.log('MAIN:PRESENTER: start from MAIN:PRESENTER');
},
destroy: function() {
$dhx.debug.log('MAIN:PRESENTER: destroy from MAIN:PRESENTER');
//$dhx.debug.log(this._view);
},
dispatch_help_route: function() {
var self = this,
main_view = $dhx.ui.mvp.views.get('view');
main_view.dispatch('/help');
}
}; // end API
return API;
}())
});
- Calling the
dispatch()
method from the_router
Object from theMain View
.
On the Main Presenter
$dhx.ui.mvp.presenters.declare({
"presenter": (function() {
'strict';
var API = {
start: function() {
var self = this,
view = self.view;
$dhx.debug.log('MAIN:PRESENTER: start from MAIN:PRESENTER');
},
destroy: function() {
$dhx.debug.log('MAIN:PRESENTER: destroy from MAIN:PRESENTER');
//$dhx.debug.log(this._view);
},
dispatch_help_route: function() {
var self = this,
view = self.view;
view._router.dispatch('/help');
}
}; // end API
return API;
}())
});
On a Child Presenter
$dhx.ui.mvp.presenters.declare({
"help": (function() {
'strict';
var API = {
start: function() {
var self = this,
view = self.view;
$dhx.debug.log('MAIN:PRESENTER: start from MAIN:PRESENTER');
},
destroy: function() {
$dhx.debug.log('MAIN:PRESENTER: destroy from MAIN:PRESENTER');
//$dhx.debug.log(this._view);
},
dispatch_help_route: function() {
var self = this,
main_view = $dhx.ui.mvp.views.get('view');
main_view._router.dispatch('/help');
}
}; // end API
return API;
}())
});
Now, let's see how to declare the routes:
Complete setup
router.route({
url: '/help',
view: 'help_view_file_name',
presenter: 'help_presenter_file_name',
method: 'start_method_name', // implicity call presenter.start() if not explicitly defined
append_views: [
{ "chatter" : 'chatter_view' } // "name of the javascript object": "name of the javascript file"
]
});
Minimal setup
router.route({
url: 'help/:id:',
view: 'help',
presenter: 'help',
});
The above declared route will inject the files 'MyApplicationName/lib/view/help.js' and 'MyApplicationName/lib/presenter/help.js' when the route /help
is dispatched into the Application scope.
Child Views
Child Views are Javascript Modules/Object that are reponsible for rendering the view of a dispatched route.
It should looks like the following:
$dhx.ui.mvp.views.declare({
"help": (function () {
'strict';
var route = 'help',
child_view = $dhx.ui.mvp.child_view.extend({ }),
view = new child_view({
/**
* [_settings View's settings. Components' internal settings]
* @type {Object}
*/
_settings: {
},
initialize: function(options) {
$dhx.debug.log('CHILD:VIEW:: called initialize from help_view.initialize');
},
destroy: function() {
},
/**
* [render the view. Called once application starts]
*/
render: function() {
var self = this;
}
});
return view;
}())
});
A more complete Child View
demo code may be viewed here
As a example, we may consider a Help
button on the Main View
which dispatched the /help
route when clicked.
$dhx.ui.mvp
will then inject the 'lib/view/help.js' and this recently injected module will render a interface displaying a list of Frequent Asked Questions
On a dhxMVP application, every route is associated to a Child View
.
Child Presenters
Child Presenters are Javascript Modules/Object that are reponsible for orchestrating a Child View
. of a dispatched route.
It should looks like the following:
$dhx.ui.mvp.presenters.declare({
"help": (function() {
'strict';
var API = {
start: function() {
var self = this,
view = self.view;
},
destroy: function() {
},
subscriber: function( event, message ){
var self = $dhx.ui.mvp.presenters.get('help'),
view = self.view;
},
}; // end API
return API;
}())
});
A more complete Child Presenter
demo code may be viewed here
In the Child Presenter
we could define function event handlers
for the components defined in the associated 'Child View'.
Not least, provide any 'help method' to be used into the associated Child View
.
As a example, we may consider a Help
button on the Main View
which dispatched the /help
route when clicked.
$dhx.ui.mvp
will then inject the 'lib/presenter/help.js' and this recently injected module will orchestrate the interface displaying a list of Frequent Asked Questions
.
On a dhxMVP application, every route is associated to a Child Presenter
.
Resume
Considering the Application Demo provided in this boilerplate, every time you want to add a new button on the left sidebar, you will need:
- Declare a new route into
./boilerplate_sidebar.js
- Add a new sidebar button on the file
lib/presenter/presenter.js
inside add_sidebar_buttons() scope. - Declare a new Child View and create it file inside
lib/view/
folder. - Declare a new Child Presenter and create it file inside
lib/presenter/
folder.
Software validation and code automation
When you finish the development process of your dhxMVP application, you may want to move it to production.
Not least, you may want to create releases of your currently application version.
You may want too, to create Desktop version
of your fresh created Web Application
.
All those features are provided in this Boilerplate system. Although, you will not be able to produce new releases, or create installers for desktop version or even to create distributions of your application to run on a production server if one of the following 2 things occurs:
- programming error o bad pratice found in your codebase.
- Tests performed by the Testing Suite are failing.
It means you will not be able to delivery bad code to the end users.
Code quality
To ensure code quality and best pratices we use jsHint, a code quality tool, against your codebase.
Run JSHint to check the code and look for error and bad pratices.
Inside application directory, type:
$ gulp jshint
Unit Tests
Inside application directory, type:
$ gulp test
Deploy
Creating minified version of the javascript files
When working on localhost, the dhxMVP will generally load the non minified version of the javascript files. Although, the files used by the core are always loaded using the minified version.
In this case, on every time you change a file of the core (except views and presenters), you need to run the dist
command to see the changes in action using a browser.
It is necessary too when putting the application on production due it will load only the minified version of the javascript files.
To make a dist, run:
$ gulp dist
Note
It is a good pratice to run $ gulp jshint
before $ gulp dist
and check for programming error and bad pratices. Although, the jshint
is automatically runned before dist
Build a distribution
Building a distribution will check the whole code reliability and push it to Git repository as a new version(release) of your application.
$ sudo gulp build
The above command will run the following tasks in order:
- gulp jshint
- gulp dist
- gulp test
- gulp git-add-commit-push
Note
If a error occurs on any of the above task, the upcoming deploy task will not be runned.
Application distributing
Distributable Applications
Distributable applications are bundles of files automatically generated using the dist
command in gulp
.
$ gulp dist
Running dist
will create 4 types of distributable applications:
Web version
Stored in
./dist
The content of the web version can be simple served by a HTTP server.Google Chrome Application Version
Stored in
./dist_chrome_app
The content of the Google Chrome Application version can be directly imported using theGoogle Chrome Extension Manager
or compacted as zip and uploaded inChrome Web Store Control Panel
Google Chrome Extension Version
Stored in
./dist_chrome_extension
The content of the Google Chrome Extension version can be directly imported using theGoogle Chrome Extension Manager
or compacted as zip and uploaded inChrome Web Store Control Panel
Electron Version
Stored in
./dist_electron
The content of the Electron version will be used to create Windows, Linux and Mac installers of your application.
The File system of a distributable web version should be something like:
|---- dist/
|---- assets/
|---- deps/
|---- dhx
|---- thirdparty
|---- lib/
|---- model
|---- collections
|---- questions.js
|---- min.questions.js
|---- users.js
|---- min.users.js
|---- engines
|---- backboneIDB.js
|---- min.backboneIDB.js
|---- models
|---- min.question.js
|---- min.question.js
|---- min.user.js
|---- min.user.js
|---- presenter
|---- presenter.js
|---- min.presenter.js
|---- help.js
|---- min.help.js
|---- view
|---- view.js
|---- min.view.js
|---- help.js
|---- min.help.js
|---- index.html
The File system of a distributable Google Chrome Application version should be something like:
|---- dist_chrome_app/
|---- assets/
|---- deps/
|---- dhx
|---- thirdparty
|---- lib/
|---- model
|---- collections
|---- questions.js
|---- min.questions.js
|---- users.js
|---- min.users.js
|---- engines
|---- backboneIDB.js
|---- min.backboneIDB.js
|---- models
|---- min.question.js
|---- min.question.js
|---- min.user.js
|---- min.user.js
|---- presenter
|---- presenter.js
|---- min.presenter.js
|---- help.js
|---- min.help.js
|---- view
|---- view.js
|---- min.view.js
|---- help.js
|---- min.help.js
|---- boilerplate_sidebar.js
|---- google_app_background.js
|---- icon-128.png
|---- icon-16.png
|---- index.html
|---- manifest.json
The File system of a distributable Google Chrome Extension version should be something like:
|---- dist_chrome_extension/
|---- assets/
|---- deps/
|---- dhx
|---- thirdparty
|---- lib/
|---- model
|---- collections
|---- questions.js
|---- min.questions.js
|---- users.js
|---- min.users.js
|---- engines
|---- backboneIDB.js
|---- min.backboneIDB.js
|---- models
|---- min.question.js
|---- min.question.js
|---- min.user.js
|---- min.user.js
|---- presenter
|---- presenter.js
|---- min.presenter.js
|---- help.js
|---- min.help.js
|---- view
|---- view.js
|---- min.view.js
|---- help.js
|---- min.help.js
|---- boilerplate_sidebar.js
|---- icon-128.png
|---- icon-16.png
|---- index.html
|---- manifest.json
The File system of a distributable Electron version should be something like:
|---- dist_electron/
|---- public
|---- assets/
|---- deps/
|---- dhx
|---- thirdparty
|---- lib/
|---- model
|---- collections
|---- questions.js
|---- min.questions.js
|---- users.js
|---- min.users.js
|---- engines
|---- backboneIDB.js
|---- min.backboneIDB.js
|---- models
|---- min.question.js
|---- min.question.js
|---- min.user.js
|---- min.user.js
|---- presenter
|---- presenter.js
|---- min.presenter.js
|---- help.js
|---- min.help.js
|---- view
|---- view.js
|---- min.view.js
|---- help.js
|---- min.help.js
|---- boilerplate_sidebar.js
|---- index.html
|---- index.js
|---- package.json
Including the Web, Chrome Application and Chrome Extension versions, you may be interested in distributing your application as a Desktop Application
. For this case, the Boilerplate system provides you a mechanism to create executable versions
of your application with no efforts.
You can get the generated executables and distribute to your client.
The generated installers will be stored into installers/
.
To make the installers, run the following commands:
Creating Mac installers
$ gulp build-installer-mac
Creating Windows installers
$ gulp build-installer-windows
Creating Linux installers
$ gulp build-installer-windows
Creating a Google Chrome Application
A Google Chrome Application version of your app is automatically generated when you run gulp dist
. The generated application is saved under the ./dist_chrome_app
directory.
$ gulp dist
Creating a Google Chrome Extension
A Google Chrome Extension version of your app is automatically generated when you run gulp dist
. The generated application is saved under the ./dist_chrome_extension
directory.
$ gulp dist
Move to production
Release notes
Version: v0.0.94-alpha
- Complete data sync between connected clients. Online usage.
- Messaging Mediator
- Local Messaging Mediator
- Assynchronous Pub/Sub to provide local/offline communication between
isolated application pieces
.
- Assynchronous Pub/Sub to provide local/offline communication between
- Remote Messaging Mediator
- Assynchronous Pub/Sub to provide remote/online communication/data sync between
connected clients
. Currently using PubNub as communication stack. Easily replaced by your own stack with websocket and Redis for example.
- Assynchronous Pub/Sub to provide remote/online communication/data sync between
- Local Messaging Mediator
- Demo improvements
- New Pet model
- New Pets collection
- Main presenter subscriber
- Update Dashboard charts data when receives a message
- Help child presenter subscriber
- Insert data on view when receives a message
- Update data on view when receives a message
- Delete data on view when receives a message
- Help child view
- Insert data - UI - send message
- Update data - UI - send message
- Delete data - UI - send message
- $dhx.ui.mvp improvements
- $dhx.ui.session initial implementation
- $dhx.component improvements
- $dhx.getRandomColor
- backboneIDB engine improvements Create, Update and Destroy models when receiving proper messages from Mediator. Notify Mediator when performed action was requested by local client. Notify other connected users.
- New properties for model ui definition
- note Expose a explanatory text on UI side related to that document property. Adds a text tooltip to the control related to that document property on the UI side.
- maxLength Define a maxlength property value to be effective in any control on UI side related to that document property.
- navigator.geolocation shim
Todo
Generate Cordova distribution. Mobile application version.
Implement a
datastore
APISimilar to DHTMLX Datastore to be used internally by all Engines.
Provide server sync for backboneIDB Engine
Using Express, WS and Redis on Back End
Provide server storage for backboneIDB Engine
- Using Mongo on Back End
- Using Couchbase
Implement Pouch Engine and provide support to CouchBase
Implement Firebase Engine
REST API generator using defined models and collection
Express Mongoose Mongo
Implement REST client
License
This software is distributed under GPL-2.0 in accordance with DHTMLX license.
DISCLAIMER OF WARRANTY
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.