Gana hasta un 30% más con tu trabajo, obtén el control de tu carrera y lleva una vida más feliz, placentera y saludable.
Mario Raúl Pérez, Servicios de Capacitación Profesional.

Arquitectura MVC con ExtJS 4

Las grandes aplicaciones del lado del cliente han sido siempre difíciles de escribir, difícil de organizar y difícil de mantener. Tienden a crecer rápidamente sin control a medida que agrega más funcionalidad y desarrolladores a un proyecto. Ext JS 4 viene con una nueva arquitectura de aplicación que no sólo organiza su código, sino que tambien reduce la cantidad de codigo que se debe escribir.

Nuestra arquitectura de la aplicación sigue un patrón como el MVC- con modelos y controladores que están siendo introducidos por primera vez. Hay muchas arquitecturas MVC, la mayoría de los cuales son ligeramente diferentes entre sí. A continuación mostramos cómo se define la nuestra:

  • Un modelo es una colección de campos y sus datos (por ejemplo, un modelo de usuario con nombre de usuario y las contraseñas). Los Modelos saben cómo se persisten a través del paquete de datos, y puede estar vinculado a otros modelos a través de asociaciones. Los Modelos trabajan de forma muy similar a la clase Record de Ext JS 3, y se utilizan normalmente con Stores para presentar los datos en las grillas y otros componentes.

  • Vista es cualquier tipo de componente – grids, trees and panels son todos ellos vistas.

  • Controladores son lugares especiales para poner todo el código que hace que su aplicación funcione – ya sea el renderizado de vistas, creación de instancias de Modelos, o cualquier otra lógica de aplicación.

En esta guía vamos a crear una aplicación muy sencilla que gestiona los datos del usuario. Al final usted sabrá cómo juntar aplicaciones sencillas, usando la nueva arquitectura de aplicación de Ext. JS 4.

La arquitectura de la aplicación trata tanto de proporcionar la estructura y consistencia, tanto para las clases del framework como las de su aplicación. Seguir las convenciones tiene una serie de ventajas importantes:

  • Cada aplicación funciona de la misma manera por lo que sólo tiene que aprenderlo una vez.
  • Es fácil compartir código entre aplicaciones, ya que todas trabajan de la misma manera.
  • Usted puede utilizar nuestras herramientas de construcción para crear versiones de sus aplicaciones optimizadas para su uso en producción.

Estructura de archivos

Las aplicaciones Ext JS 4 siguen una estructura de directorio unificado que es el mismo para cada aplicación. Por favor, revise la Guía de introducción para una explicación detallada sobre la estructura básica de los archivos en una aplicación. En el diseño MVC, todas las clases se colocan en la carpeta app , que contiene a su vez sub-carpetas para generar el espacio de nombres de los modelos, vistas, controladores y almacenes(stores). Así es como la estructura de carpetas para nuestra simple aplicación de ejemplo se verá cuando haya terminado:

Estructura de directorios aplicacion MVC en ExtJS

En este ejemplo, se encapsula toda la aplicación dentro de una carpeta llamada ‘ account_manager . Los archivos más importantes de la Ext JS 4 SDK están incluidas en la carpeta ext-4.0. Por lo tanto el contenido de nuestro index.html es la siguiente:

<html>
<head>
    <title>Account Manager</title>

    <link rel="stylesheet" type="text/css" href="ext-4.0/resources/css/ext-all.css">

    <script type="text/javascript" src="ext-4.0/ext-debug.js"></script>

    <script type="text/javascript" src="app.js"></script>
</head>
<body></body>
</html>

Crear la aplicación en app.js

Cada aplicación Ext. JS 4 se inicia con una instancia de la clase Application . La instancia de Application contiene la configuración global de su aplicación (por ejemplo, el nombre de la aplicación), así como mantiene referencias a todos los modelos, vistas y controladores utilizados por la aplicación. Una aplicación también contiene una función de lanzamiento, que se ejecuta automáticamente cuando todo está cargado.

Vamos a crear una aplicación de Account Manager sencilla que nos ayudará a gestionar las cuentas de usuario. En primer lugar tenemos que elegir un espacio de nombres global para esta aplicación. Todos las aplicaciones Ext JS 4 sólo se debe utilizar una variable global, con todas las clases de la aplicación anidadas dentro de ella. Por lo general, queremos un nombre de variable global corta, en este caso vamos a utilizar “AM”:

Ext.application({
    name: 'AM',

    appFolder: 'app',

    launch: function() {
        Ext.create('Ext.container.Viewport', {
            layout: 'fit',
            items: [
                {
                    xtype: 'panel',
                    title: 'Users',
                    html : 'List of users will go here'
                }
            ]
        });
    }
});

Hay algunas cosas que pasan aquí. En primer lugar, invoca Ext.application para crear una nueva instancia de clase de aplicación, a la que pasamos el nombre de ” AM “. Esto configura automáticamente una variable global AM por nosotros, y registra el espacio de nombres a Ext.Loader , con la ruta correspondiente de ‘ app ‘ configurado a través de la opción de configuración appFolder . También proporciona una función de lanzamiento simple que sólo crea una Viewport que contiene un único panel que se llenara la pantalla.

Definir un Controlador

Los controladores son como el pegamento que une todas las partes de la aplicación entre si. Todo lo que realmente hacen es escuchar los eventos (por lo general de vistas) y realizar algunas acciones cuando lleguen. Continuando con nuestra aplicación de Account Manager, vamos a crear un controlador. Cree un archivo llamado app/controller/Users.js y agregue el siguiente código:

Ext.define('AM.controller.Users', {
    extend: 'Ext.app.Controller',

    init: function() {
        console.log('Initialized Users! This happens before the Application launch function is called');
    }
});

Ahora agregamos nuestro recientemente creado controlador Users a la configuración de la aplicación en app.js:

Ext.application({
    ...

    controllers: [
        'Users'
    ],

    ...
});

Cuando cargamos nuestra aplicación visitando index.html dentro de un navegador, el controlador Users se carga automáticamente (ya que se especifica en la definición de aplicación más arriba), y su la init se llama justo antes de la función launch de la aplicación.

La funcion init es un buen lugar para establecer cómo el controlador interactúa con la vista, y normalmente se utiliza en conjunción con otra función de Controller – control . La función control hace que sea fácil de escuchar a los eventos de las clases de sus vistas y realizar acciones con una función de controlador. Vamos a actualizar nuestro controlador Users para decirnos cuando el panel se renderiza:

Ext.define('AM.controller.Users', {
    extend: 'Ext.app.Controller',

    init: function() {
        this.control({
            'viewport > panel': {
                render: this.onPanelRendered
            }
        });
    },

    onPanelRendered: function() {
        console.log('The panel was rendered');
    }
});

Hemos actualizado la función init para usar this.control para establecer los oyentes en las vistas de nuestra aplicación. La función control utiliza el nuevo motor de ComponentQuery para obtener rápida y fácilmente las referencias a los componentes de la página. Si usted no está familiarizado con ComponentQuery, asegúrese de revisar la documentación de ComponentQuery para una explicación completa. En resumen, nos permite pasar de selector similar al CSS que buscara todos los componentes correspondientes en la página.

En nuestra función de inicio por encima de nosotros especificamos 'viewport > panel' , que se traduce en “me encuentra cada panel que es un hijo directo de una ventana”. A continuación, suministramos un objeto que asigna nombres de eventos (solo render en este caso) a las funciones de controlador. El efecto general es que cada vez que cualquiera de los componentes que coincide con nuestro selector dispara un evento render, nuestra función onPanelRendered es llamada.

Cuando ejecutamos nuestra aplicación ahora podemos ver lo siguiente:

No es exactamente la aplicación más emocionante de la historia, pero demuestra lo fácil que es empezar con el código de organización. Permitamonos continuar un poco mas con la aplicación agregandole una grilla.

Definir una Vista

Hasta ahora nuestra aplicación sólo ha sido unas pocas líneas y sólo esta contenida en dos archivos – app.js y app/controller/Users.js. Ahora que queremos agregar una grilla que muestre todos los usuarios de nuestro sistema, es el momento de organizar nuestra lógica un poco mejor y empezar a usar vistas.

Una vista no es más que un componente, por lo general se define como una subclase de un componente de Ext JS. Vamos a crear nuestra grilla de usuarios ahora creando un nuevo archivo llamado app/view/user/List.js y poniendo lo siguiente en él:

Ext.define('AM.view.user.List' ,{
    extend: 'Ext.grid.Panel',
    alias : 'widget.userlist',

    title : 'All Users',

    initComponent: function() {
        this.store = {
            fields: ['name', 'email'],
            data  : [
                {name: 'Ed',    email: 'ed@sencha.com'},
                {name: 'Tommy', email: 'tommy@sencha.com'}
            ]
        };

        this.columns = [
            {header: 'Name',  dataIndex: 'name',  flex: 1},
            {header: 'Email', dataIndex: 'email', flex: 1}
        ];

        this.callParent(arguments);
    }
});

Nuestra clase de Vista no es más que una clase normal. En este caso nos toca ampliar el componente de la grilla y configurar un alias para poder usarlo como un xtype (más sobre esto en un momento). También pasamos en el la configuración del almacén y las columnas que la grilla debe hacer.

A continuación tenemos que añadir esta vista a nuestro de nuestro controlador de usuarios. Debido a que establecimos un alias con el formato especial ‘widget.’ , ahora podemos usar “userlist” como un xtype, al igual que  se había usado ‘panel’ previamente.

Ext.define('AM.controller.Users', {
    extend: 'Ext.app.Controller',

    views: [
        'user.List'
    ],

    init: ...

    onPanelRendered: ...
});

Y luego se renderizan dentro de la ventana principal, modificando el método de lanzamiento en app.js:

Ext.application({
    ...

    launch: function() {
        Ext.create('Ext.container.Viewport', {
            layout: 'fit',
            items: {
                xtype: 'userlist'
            }
        });
    }
});

Lo único a destacar aquí es que hemos especificado “user.List ‘dentro de la matriz vistas. Esto le dice a la aplicación que debe cargar el archivo de forma automática de manera que podamos usarlo cuando ejecutemos. La aplicación utiliza el nuevo sistema de carga dinámica de Ext JS 4 para obtener automáticamente el archivo desde el servidor. Esto es lo que veremos cuando recargemos la página ahora:

Controlar la grilla

Observe que nuestra función onPanelRendered todavía está siendo llamada. Esto se debe a nuestra clase de la grilla sigue coincidiendo con el selector de “viewport> panel ‘. La razón de esto es que nuestra clase extiende Grid, que a su vez extiende Panel.

Por el momento, los oyentes que agregamos al de selección en realidad se llaman para cada Panel o o Subclase de Panel que sea un hijo directo de el viewport, así que vamos a ajustar un poco mas con nuestra nueva xtype. Mientras estamos con esto, vamos a escuchar en su lugar los hacer doble clic en las filas de la grilla de modo que más tarde se pueda editar el usuario:

Ext.define('AM.controller.Users', {
    extend: 'Ext.app.Controller',

    views: [
        'user.List'
    ],

    init: function() {
        this.control({
            'userlist': {
                itemdblclick: this.editUser
            }
        });
    },

    editUser: function(grid, record) {
        console.log('Double clicked on ' + record.get('name'));
    }
});

Observe que hemos cambiado el de selección ComponentQuery (simplemente ‘userlist’), el nombre del evento (a ‘itemdblclick’) y el nombre de función de controlador (a ‘editUser’). Por ahora sólo estamos registrando el nombre del usuario que se hace doble clic:

Registrarlo en la consola está muy bien, pero realmente queremos editar nuestros usuarios. Vamos a hacerlo ahora, comenzando con una nueva vista en el archivo app/view/user/Edit.js:

Ext.define('AM.view.user.Edit', {
    extend: 'Ext.window.Window',
    alias : 'widget.useredit',

    title : 'Edit User',
    layout: 'fit',
    autoShow: true,

    initComponent: function() {
        this.items = [
            {
                xtype: 'form',
                items: [
                    {
                        xtype: 'textfield',
                        name : 'name',
                        fieldLabel: 'Name'
                    },
                    {
                        xtype: 'textfield',
                        name : 'email',
                        fieldLabel: 'Email'
                    }
                ]
            }
        ];

        this.buttons = [
            {
                text: 'Save',
                action: 'save'
            },
            {
                text: 'Cancel',
                scope: this,
                handler: this.close
            }
        ];

        this.callParent(arguments);
    }
});

Una vez más estamos a definiendo una subclase de un componente existente – esta vez Ext.window.Window . Una vez más hemos utilizado initComponent para especificar los complejos elementos items y buttons Hemos utilizado una distribución “fit” y el formulario como un elemento único, que contiene los campos para editar el nombre y la dirección de correo electrónico. Finalmente hemos creado dos botones, uno que sólo cierra la ventana, y el otro que se utilizará para guardar los cambios.

Todo lo que tenemos que hacer ahora es agregar la vista en el controlador, renderizarla y cargar el usuario en ella:

Ext.define(‘AM.controller.Users’, {

    extend: 'Ext.app.Controller',

    views: [
        'user.List',
        'user.Edit'
    ],

    init: ...

    editUser: function(grid, record) {
        var view = Ext.widget('useredit');

        view.down('form').loadRecord(record);
    }
});

Definir un Modelo y un Almacén(Store)

Ahora que tenemos nuestro formulario de edición es casi la hora para empezar a editar los usuarios y guardar esos cambios. Antes de hacerlo, sin embargo, tenemos que refactorizar nuestro código un poco.

Por el momento en que el componente AM.view.user.List crea un Store en linea. Esto funciona bien, pero nos gustaría poder hacer referencia a esa tienda en otras partes de la aplicación para poder actualizar los datos en ella. Vamos a empezar por sacar el store fuera en su propio archivo – app/store/Users.js:

Ext.define('AM.store.Users', {
    extend: 'Ext.data.Store',
    fields: ['name', 'email'],
    data: [
        {name: 'Ed',    email: 'ed@sencha.com'},
        {name: 'Tommy', email: 'tommy@sencha.com'}
    ]
});

Ahora sólo tendremos que hacer dos pequeños cambios – en primer lugar le pediremos a nuestro controlador de Users incluir este store cuando se carga:

Ext.define('AM.controller.Users', {
    extend: 'Ext.app.Controller',
    stores: [
        'Users'
    ],
    ...
});

entonces actualizaremos app/view/user/List.js para simplificar las referencias al Store usando un id:

Ext.define('AM.view.user.List' ,{
    extend: 'Ext.grid.Panel',
    alias : 'widget.userlist',

    //we no longer define the Users store in the `initComponent` method
    store: 'Users',

    ...
});

 

Mediante la inclusión de los Stores que hemos definido en nuestro controlador Users, estos son cargados automáticamente y se les da un store id, lo que les hace muy fácil de referenciar en nuestra vista (con sólo configurar store: 'Users' en este caso).

Por el momento acabamos de definir nuestros campos ('name' and 'email')  en línea en el Store. Esto funciona bastante bien, pero en Ext JS 4 tenemos una clase Ext.data.Model poderosa que nos gustaría aprovechar en lo que se refiere a la edición de nuestros usuarios. Vamos a terminar esta sección con la refactorización de el Store para utilizar un modelo que vamos a poner en app/model/User.js:
 
 

Ext.define('AM.model.User', {
    extend: 'Ext.data.Model',
    fields: ['name', 'email']
});

Eso es todo lo que tenemos que hacer para definir nuestro modelo, ahora sólo tendremos que actualizar nuestro Store para hacer referencia al nombre del modelo en lugar de indicar los campos en linea, y también pedirle al controlador de Users obtener una referencia al modelo :

//El controlador Users nos asegurara que el modelo User es incluido en la página y esta disponible para nuestra aplicación
Ext.define('AM.controller.Users', {
    extend: 'Ext.app.Controller',
    stores: ['Users'],
    models: ['User'],
    ...
});

// ahora referenciamos a el modelo en vez de definir los campos en linea
Ext.define('AM.store.Users', {
    extend: 'Ext.data.Store',
    model: 'AM.model.User',

    data: [
        {name: 'Ed',    email: 'ed@sencha.com'},
        {name: 'Tommy', email: 'tommy@sencha.com'}
    ]
});

Nuestra refactorización hará más fácil la próxima sección, pero no debería haber afectado el comportamiento actual de la aplicación. Si recargamos la página ahora y hacemos doble clic en una fila, vemos que la ventana de edición de usuario sigue apareciendo como se esperaba. Ahora es el momento de terminar la funcionalidad de edición:

Guardar los datos con el Modelo

Ahora que tenemos a nuestra grilla cargada con los datos de los usuarios y se abre una ventana de edición cuando se hace doble clic en cada fila, nos gustaría guardar los cambios que realice el usuario. La ventana de edición de usuario que se definido anteriormente contiene un formulario (con los campos de nombre y correo electrónico), y un botón de guardar. En primer lugar vamos a actualizar la función init nuestro controlador para escuchar los clics al botón Guardar:

Ext.define('AM.controller.Users', {
    init: function() {
        this.control({
            'viewport > userlist': {
                itemdblclick: this.editUser
            },
            'useredit button[action=save]': {
                click: this.updateUser
            }
        });
    },

    updateUser: function(button) {
        console.log('clicked the Save button');
    }
});

Esta vez hemos añadido un segundo selector ComponentQuery a nuestro llamada a this.control – 'useredit button[action=save]'. Esto funciona de la misma manera que el primer selector – que utiliza el xtype 'useredit' que hemos definido anteriormente para enfocarse en la ventana de edición de usuario, y luego busca los botones con la acción del 'save'dentro de esa ventana. Cuando definimos nuestra ventana de edición de usuarios pasamos {action: 'save'} el botón de guardar, lo cual nos da una manera fácil de apuntar a ese botón.

Podemos verificar que la función updateUser sea llamada cuando se haga clic en el botón Guardar:

Ahora que hemos visto a nuestro manejador esté conectado correctamente a evento click del botón Guardar, vamos a llenar en la lógica real de la función UpdateUser. En esta función es necesario para obtener los datos del formulario, actualizar nuestros usuarios con ella y luego guardar ese regreso al Store usuarios que hemos creado anteriormente. Vamos a ver cómo podemos hacer lo siguiente:

updateUser: function(button) {
    var win    = button.up('window'),
        form   = win.down('form'),
        record = form.getRecord(),
        values = form.getValues();

    record.set(values);
    win.close();
}

Vamos a descomponer lo que está pasando aquí. Nuestro evento click nos dio una referencia al botón al que el usuario hizo clic, pero lo que realmente queremos es el acceso al formulario que contiene los datos y la propia ventana. Para obtener las cosas con rapidez sólo tendremos que utilizar ComponentQuery nuevamente, en primer lugar con button.up('window') para conseguir una referencia a la ventana de edición de usuario, entonces usamos win.down('form') para obtener el formulario.

Después de eso, simplemente debemos recuperar el registro que actualmente se encuentra cargado en del formulario y actualizarlo con lo que el usuario ha escrito en el formulario. Por ultimo, cerramos la ventana para llamar la atención de nuevo a la grilla. Esto es lo que vemos cuando ejecutamos nuestra aplicación, cambie el campo de nombre a 'Ed Spencer' y haga clic en Guardar:

Guardar en el servidor

Es muy fácil. Vamos a terminar esto ahora haciendolo interactuar con nuestro servidor. Por el momento estamos harcodeando dos registros de ejemplo en el Store usuario, por lo que vamos a empezar leyendolos via AJAX en su lugar:

Ext.define('AM.store.Users', {
    extend: 'Ext.data.Store',
    model: 'AM.model.User',
    autoLoad: true,

    proxy: {
        type: 'ajax',
        url: 'data/users.json',
        reader: {
            type: 'json',
            root: 'users',
            successProperty: 'success'
        }
    }
});

Aquí hemos eliminado la propiedad 'data' y lo reemplazamos con un proxy. Los proxys son la forma de cargar y guardar datos de un almacén(Store) o un Modelo en Ext JS 4. Hay Proxys de AJAX, JSON-P y HTML5 localStorage, entre otros. Aquí hemos utilizado un simple Proxy de AJAX, lo que le hemos dicho a cargar los datos desde la url 'data/users.json'.

También adjunta un lector para el proxy. El lector es responsable de la decodificación de la respuesta del servidor en un formato que el Store pueda entender. Esta vez hemos utilizado un lector de JSON, y especificado las configuraciones de root y successProperty (ver la documentación Json Reader para más información sobre las configuraciones). Por último vamos a crear nuestro fichero data/users.json y pegar los datos anteriores en el:

{
    success: true,
    users: [
        {id: 1, name: 'Ed',    email: 'ed@sencha.com'},
        {id: 2, name: 'Tommy', email: 'tommy@sencha.com'}
    ]
}

El único cambio que hicimos al Store es establecer autoLoad a true, lo que significa que el Store le pedirá a su proxy cargar esos datos de inmediato. Si se actualiza la página ahora vamos a ver el mismo resultado que antes, excepto que ahora ya no estamos harcodeando los datos en nuestra aplicación.

Lo último que deseamos hacer aquí es enviar nuestros cambios de vuelta al servidor. Para este ejemplo, sólo estamos utilizando archivos estáticos JSON en el servidor por lo que no verá ningún cambio de base de datos, pero al menos podemos comprobar que todo está conectado correctamente en conjunto. En primer lugar vamos a hacer un pequeño cambio en nuestro nuevo proxy para decirle que envíe actualizaciones a una dirección URL diferente:

proxy: {
    type: 'ajax',
    api: {
        read: 'data/users.json',
        update: 'data/updateUsers.json'
    },
    reader: {
        type: 'json',
        root: 'users',
        successProperty: 'success'
    }
}

Todavía estamos leyendo los datos del users.json, pero las actualizaciones serán enviadas a updateUsers.json. Esto es sólo para poder devolver una respuesta ficticia para saber que las cosas están funcionando. El archivo updateUsers.json contiene sólo {"success": true}. El único cambio que tenemos que hacer es indicar a nuestro Store sincronizarse después de la edición, lo cual hacemos mediante la adición de una línea más dentro de la función updateUser, que ahora se ve así:

updateUser: function(button) {
    var win    = button.up('window'),
        form   = win.down('form'),
        record = form.getRecord(),
        values = form.getValues();

    record.set(values);
    win.close();
    this.getUsersStore().sync();
}

Ahora podemos desplazarnos a través de nuestro ejemplo completo y asegurarnos de que todo funciona. Vamos a editar una fila, pulsar el botón Guardar y ver que la solicitud se envía correctamente a updateUser.json

Nota adicional: Si van a realizar el script php para actualizar los datos en la base, necesitaran saber antes como recibir los parametros enviados via JSON en PHP

Despliege

La reciente introducción Sencha SDK Tools (descargar aquí) hace que la implementación de cualquier aplicación Ext JS 4  sea más fácil que nunca.  Las herramientas que le permite generar un manifiesto de todas las dependencias en la forma de un archivo JSB3 (formato de archivo JSBuilder), o crear un compilado minimo de lo que su aplicación necesita en cuestión de minutos.

Por favor, consulte la guía de introducción para obtener instrucciones detalladas.

Resumen:

Hemos creado una aplicación muy sencilla que gestiona los datos del usuario y envía los cambios al servidor. Empezamos simple y poco a poco rediseñado nuestro código para hacerlo más limpio y más organizado. En este punto, es fácil agregar más funcionalidad a nuestra aplicación sin necesidad de crear código espagueti. El código fuente completo para esta aplicación se puede encontrar en la descarga de Ext JS SDK 4, dentro de la carpeta examples/app/simple.

ExtJS 4.0 Documentation from Sencha , versión en español por Mario Pérez.

Siguientes pasos: capacitese con expertos!

Nuevas Caracteristicas de ExtJS 4

Sistema de educación continua con acceso instantáneo a los contenidos.

 

Ext JS 4 se está convirtiendo rápidamente en el Framework de mayor adopción para Aplicaciones de Internet Enriquecidas en el mundo del desarrollo web. Se estima que es usado por 2 millones de desarrolladores en el mundo entero y permiten crear aplicaciones web enriquecidas multiplataforma construidas sobre la base de estándares web.

Formulario de consultas cursos.

Podemos proveerle del entrenamiento y capacitación que necesita!