Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New module architecture #12

Merged
merged 2 commits into from Mar 5, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
251 changes: 170 additions & 81 deletions modules_development/index.md
Expand Up @@ -12,7 +12,7 @@ Para que un módulo pueda definir una funcionalidad completa se propone la sigui
## Estructura del proyecto ##

```
src/main/webapp
app
├── css // Hojas de estilo transversales
├── res
│ └──config
Expand All @@ -26,6 +26,8 @@ src/main/webapp
└──myModule // Nombre del módulo
├── start.js // Inicialización y carga de dependencias
├── controller.js // Controlador del módulo
├── dispatcher.js // dispatcher del módulo
├── config.js // configuration del módulo
├── css // Hojas de estilo del módulo
│ └── *.scss
├── res
Expand Down Expand Up @@ -62,7 +64,7 @@ src/main/webapp
│ └── *.html
└── views // Vistas del módulo (ItemView/CollectionView)
└── *.js
src/test
app/test
├── define.js // Definición de dependencias requirejs transversales
├── index.html // Punto de partida de ejecución de tests
└── spec
Expand Down Expand Up @@ -310,63 +312,31 @@ Se pueden anidar tantos layouts como sean necesarios.
> * [Marionette.Region](https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.region.md)
> * [Marionette.Layout](https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.layout.md)
### Controllers ###

Para que nuestra aplicación pueda actuar en función de la ruta del usuario, usamos el componente `Router` de Backbone.

Generalmente, los manejadores de las rutas son los responsables de obtener todos los recursos necesarios e injectarselo a las vistas para que lo representen correctamente.

Para definir una ruta con su manejador hay que implementar `controller.js` en el módulo en cuestión:
### Extender una Vista-Modelo-Colección

Es posible extender cualquier otro componente accesible desde la factoría a través del `app.factory.get()`, de la forma que se muestra a continuación:

```javascript
// controller.js
'use strict';
/* global define */
define([
'corejs/app'
], function(app) {
var controller = {
myHandler: function(isbn) {
var myModel = app.factory.new('MyModel');
app.myRegion.show(app.factory.new('MyView', {
model: myModel
}));
}
};

return controller;
});
```

Para luego incorporarlo en el `start.js`:

```javascript
'use strict';
/* global define */
define([
'corejs/app',
'modules/myModule/models/myModel',
'modules/myModule/views/myView',
'modules/myModule/controller'
], function(app, MyModel, MyView, controller) {
// Add models to factory
app.factory.add('MyModel', MyModel);
var MyExtendedView = app.factory.get('MyCustomView').extend({

// Add views to factory
app.factory.add('MyView', MyView);
// Load the compiled template generated by
// 'modules/myModule/templates/myTemplate.html'
template: app.jst['myModule/myTemplate']

app.addInitializer(function() {
// Define router paths
app.router.route('myroute', 'trigger:this:event', controller.myHandler);
});
});

return MyExtendedView;
});
```

> **Más info**
> * [Backbone.Router](http://backbonejs.org/#Router)
### Layout Manager


Expand Down Expand Up @@ -498,77 +468,196 @@ Sin perder los eventos a los que la habíamos ligado la vista(a través de liste
**IMPORTANTE** Para evitar memory leaks, se ha proporcionado el método destroy, quedando así la vista inservible. Si se sabe que esa vista no se va a volver a usar, llamar manualmente al método destroy para que no se ensucie la memoria.


### Extender una Vista-Modelo-Colección
## Creación de módulos ##

Es posible extender cualquier otro componente accesible desde la factoría a través del `app.factory.get()`, de la forma que se muestra a continuación:
### Inicialización de un módulo ###

Uno de los pasos a la hora de desarrollar un módulo con diferentes componentes, es la de definir el 'punto de entrada' de nuestro módulo, para ello, definiremos un documento `start.js` para cada módulo implementado. Este será el encargado de instanciar nuestro dispatcher e inicializarlo.

Un ejemplo de `start.js` podría ser el siguiente:

```javascript
'use strict';
/* global define */
define([
'corejs/app'
'corejs/app',
'backbone',
'modules/mymodule/dispatcher'
], function(app) {
var MyExtendedView = app.factory.get('MyCustomView').extend({

// Load the compiled template generated by
// 'modules/myModule/templates/myTemplate.html'
template: app.jst['myModule/myTemplate']
app.module('MyModuleName', function(Module) {
var dispatcher = new Module.Dispatcher();

app.on('before:start', function() {

dispatcher.start();

});

});

return MyExtendedView;
});
```

Con esto conseguimos reutilizar otros componentes y añadir o modificar únicamente el comportamiento que nos haga falta.
Para guardar nuestras opciones de configuración a nivel de módulo necesitaremos definir nuestro fichero `config.js`
Podemos acceder a la siguiente configuración desde nuestro controller utilizando namespace MyModule.config, por ejemplo `MyModule.config.MainLayout`

### Inicialización de un módulo ###
```javascript
'use strict';
/* global define */
define([
'corejs/app',
'jquery',
'underscore',
'backbone.marionette',
], function(app, $, _, Backbone, Marionette) {

Uno de los pasos a la hora de desarrollar un módulo con diferentes componentes, es la de definir el lugar donde declarar e integrar todos esos componentes en la aplicación, para ello, definiremos un documento `start.js` para cada módulo implementado.
var Module = app.module('MyModuleName', function(Module) {

Un ejemplo de `start.js` podría ser el siguiente:
Module.Config = Marionette.Configuration.extend({

ModelName: 'MyModelName',
MainLayout: 'MainLayout',
MyCollection: 'BooksCollection'
...

});
});
return Module.Config;
});
```


Nuestros módulos estarán autocontenidos dentro del componente Module que nos ofrece Marionette.


> **Más info**
> * [Marionette.Module](http://marionettejs.com/docs/marionette.module.html)


### Dispatcher ###

El dispatcher es el componente encargado de comunicar los diferentes módulos de nuestra aplicación con el controller del propio módulo.

Para que nuestra aplicación pueda actuar en función de la ruta del usuario, usamos el componente `Router` de Backbone.

Generalmente, los manejadores de las rutas son los responsables de obtener todos los recursos necesarios e inyectarselo a las vistas para que lo representen correctamente.

Además de definir rutas para nuestro módulo, podemos escuchar eventos globales `app.vent | app.commands` con su correspondiente manejador definiendo los objetos events y commands.

Definiendo el objeto `factory` podremos agregar a nuestra factoría los objetos que necesitemos en nuestro módulo, generalmente modelos o colecciones.

Ejemplo de `dispatcher.js` implementado en nuestro módulo:

```javascript
// dispatcher.js
'use strict';
/* global define */
define([
'corejs/app',
'backbone.marionette',
'modules/mymodule/model/myModel',
'modules/mymodule/controller',
'modules/mymodule/config'
], function(app, BookModel, Controller) {

var Module = app.module('MyModuleName', function(Module) {

Module.Dispatcher = Marionette.Dispatcher.extend({

// Module Controller
Controller: Controller,

// Router handlers
routes: {
'book/:id': 'handlerBook',
'book/:title/:id': ...
},

// listen global app.vent
events: {
'show:books:related': 'handlerListRelateds'
},

// listen global app.commands
commands: {
'some:action': 'handlerSomeAction'
},

// Add objects to factory
factory: {
'MyModelName': myModel
},

handlerBook: function(id){
this.controller.showBook(id);
},

handlerListRelateds: function() {
this.controller.showBookRelateds();
},

handlerSomeAction: function() {
this.controller.SomeAction();
}
});

'modules/myModule/controller',
});

// app models
'modules/myModule/models/myModel',
return Module.Dispatcher;
});
```

// app collecctions
'modules/myModule/collections/myCollection',
> **Más info**
// app views
'modules/myModule/views/myView'
'modules/myModule/views/myCollectionView'
], function(app, controller, MyModel, MyCollection, MyView, MyCollectionView) {
> * [Backbone.Router](http://backbonejs.org/#Router)
// Add models to factory
app.factory.add('MyModel', MyModel);
// Add views to factory
app.factory.add('MyView', MyView);

app.addInitializer(function() {
// Define router paths (url, event, handlerFunction)
app.router.route('myroute', 'router:route:myroute', controller.myHandler);
});
Con esto conseguimos reutilizar otros componentes y añadir o modificar únicamente el comportamiento que nos haga falta.


### Controller ###

El controller.js implementará las funciones necesarias para crear nuestras vistas y mostrarlas, generalmente se encargará de instanciar modelos, collecciones y vistas para posteriormente mostrarlas además de realizar nuestra 'lógica de negocio' delegando en el dispatcher.js toda la lógica de comunicación entre módulos.

```javascript
// controller.js
'use strict';
/* global define */
define([
'corejs/app',
'backbone.marionette'
], function(app) {

var Module = app.module('MyModuleName', function(Module) {

Module.Controller = Marionette.Controller.extend({

app.on('initialize:after', function() {
// Code after initialization here
app.myRegion.show(app.factory.new('MyCollectionView'));
});
showBook: function(){
var myModel = app.factory.new(Module.config.myModelName);

app.myRegion.show(app.factory.new(Module.MyView, {
model: myModel
}));
},

showBooksRelated: function(){
// code here
}

});
});

return Module.Controller;
});
```


### Gestión de dependencias ###

Como se puede observar del ejemplo anterior lo primero que se define son las dependencias del módulo, que finalmente se mapean o exportan a variables que manejaremos dentro del módulo en el mismo orden en el que se declaran.
Observando con detalle la anterior cabecera:
Como se pude observar a continuación, lo primero que se define son las dependencias del módulo, que finalmente se mapean o exportan a variables que manejaremos dentro del módulo en el mismo orden en el que se declaran

```javascript
'use strict';
Expand Down Expand Up @@ -619,7 +708,7 @@ Para integrar una librería de terceros tenemos que evaluar antes varias cosas:

Si es un paquete **Bower**, hay que dar de alta dicha dependencia con su versión en `bower.json`.

Si es un módulo AMD, no es necesario hacer gran cosa, pero **si no está definido como AMD**, y dicho módulo exporta una variable, es necesario indicarlo en el documento `define.js`, tanto en `webapp/scripts/define.js`, como en `test/define.js` de la siguiente forma:
Si es un módulo AMD, no es necesario hacer gran cosa, pero **si no está definido como AMD**, y dicho módulo exporta una variable, es necesario indicarlo en el documento `define.js`, tanto en `app/scripts/define.js`, como en `test/define.js` de la siguiente forma:

```javascript
// define.js
Expand Down Expand Up @@ -659,11 +748,11 @@ define([
Los módulos pueden tener un documento donde defina su configuración, comportamiento e incluso su contenido, a través del mecanismo de configuración de módulos.

### Módulos de aplicación ##
Llamamos módulos de aplicación a aquellos módulos específicos de la webapp. Estos se pueden configurar a través de los archivos `src/main/webapp/scripts/modules/[moduleName]/res/config.json`, donde cada módulo puede tener definida su configuracion por defecto.
Llamamos módulos de aplicación a aquellos módulos específicos de la webapp. Estos se pueden configurar a través de los archivos `app/scripts/modules/[moduleName]/res/config.json`, donde cada módulo puede tener definida su configuracion por defecto.


### Módulo externos ###
Si queremos estableccer la configuración de un módulo ajeno a nuestro repositorio, por ejemplo, un módulo del core, es posible sobreescribir su configuración redefiniendo los archivos `src/main/webapp/res/config/[externalModuleName].json`. Esta configuración deberá definirse **con los mismos campos que la configuración original**.
Si queremos estableccer la configuración de un módulo ajeno a nuestro repositorio, por ejemplo, un módulo del core, es posible sobreescribir su configuración redefiniendo los archivos `app/res/config/[externalModuleName].json`. Esta configuración deberá definirse **con los mismos campos que la configuración original**.


**Cómo acceder**
Expand Down