Compartir a través de


Este artículo proviene de un motor de traducción automática.

HTML5

Creación de una aplicación web empresarial en JavaScript

Frank Prößdorf

Dariusz Parys

 

Microsoft está poniendo mucho esfuerzo en promoción HTML5 y JavaScript como clave para los desarrolladores de Windows, y hay un buen número de marcos y las bibliotecas de alta calidad para la creación de aplicaciones de producción. En este artículo, crearemos una aplicación básica orientada al negocio que puede servir como punto de partida para cavar más profundo en las opciones existentes y permita que experimenta divertido codificación calidad puede ser JavaScript.

La aplicación catálogo de productos y dividirlas en categorías y productos y categorías pueden crear, actualizar y eliminar (CRUD), como se muestra en figura 1. Además de estas operaciones CRUD típicas, la aplicación controlará otras tareas estándar: internacionalización, validación de control de entrada y el teclado de la aplicación. Uno de los aspectos más importantes de la aplicación es que usará almacenamiento local de HTML5 para permitir la edición sin conexión. No entraremos en detalles de todo este aquí, pero encontrará el código completo para esta aplicación de ejemplo en codeplex.

The Products Overview List
Figura 1 en la lista de información general de productos

Introducción

¿Cómo tiene que hacer acerca de cómo escribir una aplicación de JavaScript orientada al negocio? Una forma de reconocido prestigio es con una estructura de Model-View-Controller (MVC). Esto ha sido empleado con éxito en marcos como Ruby on Rails, Django o ASP.NET MVC. MVC permite una estructura estricta y la separación de las preocupaciones de la aplicación, como la vista y la lógica empresarial. Esto es especialmente importante con JavaScript porque es realmente fácil escribir código confuso, haciendo todo en un solo lugar. A menudo hay que recordar no hacerlo y para probar y escribir código limpio, legibilidad y reutilización. Hay varios marcos construidos especialmente para una estructura MVC, Backbone.js, Eyeballs.js y Sammy.js.

Para esta aplicación de ejemplo se utiliza Sammy.js, principalmente porque ya se sabe, pero también porque es pequeño y bien escrito, probado y hace todo lo que necesita para empezar. No se proporciona con una estructura MVC implícita pero le permite crear fácilmente en su base. Es la única dependencia que tiene actualmente jQuery y que es una biblioteca que utilizamos para la manipulación de DOM de todas formas. La estructura de directorios con que inició el siguiente aspecto:

- public
  - js
      app.js
    + controllers
    + models
    + helpers
    + views
  + templates
  - vendor
    - sammy
        sammy.js
    - jquery
        jquery.js

Ponemos todos los archivos de plantilla que se pueden representar mediante el código JavaScript en el directorio de plantillas y todo el código de JavaScript pertinentes para representar las plantillas en el directorio de vistas.

El archivo de aplicación

Creamos el Sammy.js real se inicializan la aplicación en app.js—here se cargan los controladores y sus rutas (consulte figura 2). Tendemos a espacio de nombres de todas las variables (controladores, modelos etc.) se crea. En este caso, elegimos llamar a este karhu de espacio de nombres, el nombre de la empresa cuyos productos nos estamos catalogación.

Figura 2 Karhu.app

karhu.app = $.sammy(function() {
  this.element_selector = '#main';
  this.use(Sammy.Mustache, 'mustache');
  this.use(Sammy.NestedParams);
  this.use(Sammy.JSON); 
  this.helpers(karhu.ApplicationHelper);
  this.helpers({ store: karhu.config.store });
  karhu.Products(this);
});
$(function() {
  karhu.app.run('#/products');
});

El primer paso es cargar como plug-ins bigote, que es un motor de representación de la plantilla.A continuación, inicializar ayudantes (karhu.ApplicationHelper) y los controladores (karhu.Productos).Una vez que se define la aplicación y se cargan todos los elementos de DOM, puede ejecutar la aplicación y directo a la ruta inicial, que es el índice de todos los productos.

Pruebas de escritura

Antes de que muestra cómo el controlador de producto funciona y muestra todos los productos, deseamos ir brevemente en cómo puede aumentar considerablemente la calidad de las aplicaciones de JavaScript a través de pruebas.A medida que avanzábamos acerca del desarrollo de la aplicación de ejemplo, antes de cada paso importante que primero escribimos una prueba de aceptación para asegurarse de que el código funcionaría.También evita que las regresiones, todo lo escrito antes también sigue funcionando correctamente garantizando.Para códigos más complejos, escribimos pruebas unitarias y tratar de cubrir la mayoría de los casos que pueden producirse cuando se ejecuta el código.Una de las formas más legibles y más fáciles de escribir pruebas de aceptación es utilizar Carpincho con selenio, aunque una vez headless exploradores como PhantomJS están disponibles como controladores de Carpincho, tendrá probablemente sentido usar éstos en lugar de selenio, ya que son mucho más rápidos.

Para nuestro primer escenario (figura 3), vamos a probar si podemos ver una lista de productos.

Figura 3 una escenario de pruebas

Feature: Products
  In order to know which products I have
  As a user
  I want to see a list of products
  Scenario: list products
    Given a category "Trees" with the description "Plants"
      And a product "Oak" with the description "Brown"
                                         and the price "232.00€"
                                         that is valid to "12/20/2027"
                                         and belongs to the category "Trees"
      And a product "Birch" with the description "White"
                                         and the price "115.75€"
                                         that is valid to "03/01/2019"
                                         and belongs to the category "Trees"
    When I go to the start page
    Then I should see "Trees"
      And I should see "Oak"
      And I should see "Brown"
      And I should see "232.00€"
      And I should see "12/20/2027"
      And I should see "Birch"

Para las pruebas unitarias, hay muchas posibilidades diferentes. Se utiliza para trabajar con jspec, porque es similar a rspec de Ruby, que hemos utilizado antes. Ahora jspec quedó obsoleta en favor de jazmín, por lo que hemos utilizado que aquí. Funciona bastante bien y incluye una tarea de inclinación que le permite ejecutar fácilmente junto con las pruebas de aceptación. Aquí es una de las pruebas unitarias para la aplicación de ejemplo parece:

describe("Product", function() {
  describe("attachCategory", function() {
    it("should assign itself its category", function() {
      var categories = [{id: 1, name: 'Papiere'}, {id: 2, name: 'Baeume'}];
      var attributes = {id: 1, name: 'Fichte', category_id: 2};
      var product = new karhu.Product(attributes, categories);
      expect(product.category.
name).toEqual('Baeume');
    });   
  });
});

Definición de controladores

Una vez que terminamos el escenario comenzamos escribiendo el controlador, que es muy sencillo en primer lugar:

karhu.Products = function(app) {
  app.get('#/products', function(context) {
    context.get('/categories', {}, function(categories) {
      context.get('/products', {}, function(products) {
        products = products.map(function(product) { return new karhu.Product(
          product, categories); });
        context.partial('templates/products/index.mustache', {products: products});
      });
    });
  });
};

En este momento hay sólo una ruta definida, que es un GET en la ruta de productos / #.La devolución de llamada se ejecutará una vez que cambia el valor de hash de la ubicación en la dirección URL a /products. Por lo que si agrega la ruta a la URL (por ejemplo, http://localhost:4567/index.html#/products), se ejecutará la devolución de llamada adjunto. Lo mismo ocurrirá cuando se inicia la aplicación por primera vez, porque en app.js se ha definido que la ruta inicial apunta a la misma ruta.

Dentro de la ruta se recuperan las categorías y productos a través de complementos que no sólo una básica AJAX solicitud GET a nuestro back-end. Una vez que recupere estos datos, se asignan a los objetos de JavaScript y después representan esos objetos dentro de la plantilla de index.mustache. Esto representará en el < div id = "main" > Etiqueta HTML, que se define como el element_selector de raíz en el archivo app.js.

Definición de modelos

Tenemos que asignar los datos a los objetos de JavaScript, por lo que podemos asociar los productos con la categoría que pertenecen y representan el nombre de la categoría junto con el producto, que tiene este aspecto:

karhu.Product = function(attributes, categories) {
  _.extend(this, attributes);
  attachCategory(this, categories);
  function attachCategory(product, categories) {
    product.category = _.find(categories, function(category) {
      return parseInt(category.id, 10) === parseInt(product.category_id, 10);
    });
  }
};

Ampliamos el objeto con todos los atributos del producto y, a continuación, asignamos la categoría del producto para el objeto. Mantenemos attachCategory dentro de la clausura que una función privada. Tenga en cuenta en este código el uso de las funciones de carácter de subrayado, que son proporcionados por Underscore.js. Esta biblioteca define aplicaciones auxiliares para enumerables y le ayuda a escribir código conciso y fácil de leer.

Figura 4 muestra cómo aparecerán el modelo para el usuario.

Interacting with the Product Model in the Web App
Figura 4 interactuando con el modelo de producto en la aplicación Web

Procesamiento de plantillas

Con el modelo anterior, no necesitamos un objeto de la capa de vista adicional porque la lógica de procesamiento es muy básica, que simplemente recorre en iteración los objetos de los productos que creamos y muestra los atributos de cada uno, incluyendo el nombre de categoría que habíamos adjuntado previamente. La plantilla de bigote libre de lógica que se procesará parece que se muestra en figura 5.

Figura 5 la plantilla de bigote

<h2>Products</h2>
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Description</th>
      <th>Price</th>
      <th>Valid To</th>
      <th>Category</th>
    </tr>
  </thead>
  <tbody>
    {{#products}}
      <tr>
        <td>{{name}}</td>
        <td>{{description}}</td>
        <td>{{unit_price}}</td>
        <td>{{valid_to}}</td>
        <td>{{#category}}{{name}}{{/category}}</td>
      </tr>
    {{/products}}
  </tbody>
</table>

El resultado se muestra en figura 6.

Rendered HTML Output from the Mustache Template
Figura 6 representa la salida HTML de la plantilla de bigote

Mover el código del controlador específicos del modelo en el modelo

Es una cuestión de gustos dada cuánto responsabilidad un controlador y la cantidad que se se refactorizan en el modelo de código. Si desea escribir el código en figura 5 de manera más centrada en el modelo, podría hacer algo como figura 7.

Figura 7 un enfoque centrado en el modelo más

Controlador

karhu.Products = function(app) {
  app.get('#/products', function(context) {
    karhu.Product.all(function(products) {
      context.partial('templates/products/index.mustache', {products: products});
    });
  });
};

Modelo

karhu.Product.all = function(callback) {
  karhu.backend.get('/categories', {}, function(categories) {
    karhu.backend.get('/products', function(products) {
      products = products.map(function(product) { return new karhu.Product(product, categories); });
      callback(products);
    });
  });
};

Tareas estándar

Hay una serie de tareas que son muy comunes al desarrollo Web y se repetirá cuando se trabaja en aplicaciones de JavaScript. Queremos explicar cómo abordar las tareas y resolver los problemas que surgen. Como de costumbre, hay varias maneras de enfocar un problema.

Autenticación mayoría de las aplicaciones, incluyendo agrega seguridad básica haciendo que el usuario inicie sesión en. Porque no tiene estado HTTP, deberá volver a enviar la autenticación con cada solicitud. Puede se encargan de hacerlo por guardar un símbolo (token) cuando el usuario inicia una sesión en primer lugar y, a continuación, utilizando ese símbolo (token) para cada solicitud a partir de entonces. Lo que decidí hacerlo fue guardar un símbolo (token) en el almacenamiento local una vez que el usuario hubiera iniciado correctamente y, a continuación, enviar ese símbolo (token) como un encabezado adjunto a la XMLHttpRequest. El código para ello se muestra en figura 8. Este código se coloca en un modelo de back-end utilizado por los servicios de ayuda que se ha mencionado anteriormente.

Figura 8 guardar un símbolo (token) cuando un usuario inicia sesión

this.get = function(url, data, success, error) {
  sendRequest('get', url, data, success, error);
};
function authenticate(xhr) {
  var token = '';
  if(karhu.token) {
    token = karhu.token;
  } else if(karhu.user && karhu.password) {
    token = SHA256(karhu.user + karhu.password);
  }
  karhu.token = token;
  xhr.setRequestHeader("X-Karhu-Authentication", 'user="' + karhu.user + '", token="' + karhu.token + '"');
};
function sendRequest(verb, url, data, success, error) {
  $.ajax({
    url: url,
    data: data,
    type: verb,
    beforeSend: function(xhr) {
      authenticate(xhr);
    },
    success: function(result) {
      success(result);
    }
  });
}

Figura 9 muestra un token de usuario guardada.

X-Karhu-Authentication Included in an HTTP Request
Figura 9-Karhu-autenticación incluida en una solicitud HTTP

Si el usuario sólo conectado, tiene un nombre de usuario y una contraseña disponible. Si el usuario conectado anteriormente, tiene un símbolo (token) guardada. En cualquier caso, se adjunta la combinación de símbolo (token) o usuario y contraseña como un encabezado y si la solicitud es correcta, sabrá que el usuario se autentica correctamente. De lo contrario el back-end sólo devolverá un error. Este enfoque era relativamente sencilla de implementar y el único problema que encontramos fue que el código se convirtió en algo complejo y no se puede leer. Para solucionar este problema, hemos refactorizado los ayudantes en un modelo independiente. La abstracción de las solicitudes en un modelo de back-end es bastante común, como, por ejemplo, con la biblioteca de Backbone.js sea una parte fundamental de la biblioteca. Código de autenticación a menudo es único para cada aplicación y siempre depende de los servicios de fondo y lo que espera el front-end para enviar.

Internacionalización (I18n) la internacionalización es una tarea común para aplicaciones Web, y jquery.global.js a menudo se utiliza para realizar en aplicaciones de JavaScript. Esta biblioteca proporciona métodos para aplicar formato de números y fechas y le permite traducir cadenas mediante un diccionario para la región actual. Una vez que cargue este diccionario, que es un objeto de JavaScript simple con claves y valores traducidos, lo único que debe prestar atención a es formato de números y fechas. Un lugar razonable para hacer que está en los modelos antes de representar los objetos a las plantillas. En el modelo de producto sería algo así:

var valid_to = Date.parse(product.valid_to);
product.valid_to = $.global.format(valid_to, "d");

Figura 10 muestra el alemán como el idioma de visualización.

Switching the Language to German
Figura 10 cambio de idioma alemán

Validación de una de las ventajas del desarrollo en JavaScript es que puede dar la información de usuario en tiempo real. Tiene sentido utilizar este potencial para validar los datos antes de enviarla al servidor. Tenga en cuenta que es necesario validar los datos en el back-end porque puede haber solicitudes que no utilizan el front-end. La biblioteca jQuery jquery.validate.js a menudo se utiliza para la validación. Proporciona un conjunto de reglas en un formulario y muestra los errores en los campos de entrada apropiados si el contenido no coincide con las reglas. Tiene sentido para estructurar las reglas de validación en los modelos ya tenemos, por lo que cada modelo tiene una función de validaciones que devuelve las reglas. Aquí es el aspecto que podría la validación de nuestro modelo de categoría:

karhu.Category = function() {
  this.validations = function() {
    return {
      rules: {
        'category[name]': {
          required: true,
          maxlength: 100
        }
      }
    };
  };
};

Figura 11 muestra cómo podría mostrar un error.

A Validation Error on Creation of a New Category
Error de validación de la figura 11 una sobre la creación de una nueva categoría

Validación va más allá. Queda prohibida la navegación de formas no enviadas. El usuario debe envíe datos válidos o cancelar proactivamente la entrada de datos (consulte figura 12).

A Red Flash Notification Warns User of Unsubmitted Form Data
Figura ticpd notificación Flash rojo avisa al usuario de no enviados los datos de formulario

En la caché de objetos de edición sin conexión los usuarios a veces necesitará trabajar sin conexión. Lo que permite esto es la parte más compleja y central de la aplicación. Todos los objetos deben almacenar en caché con antelación para que una vez que la aplicación está sin conexión puede ser correctamente ordenados, paginar y filtrados. Debe haber una cola para todas las acciones que tiene lugar antes de que se almacenan en caché los objetos, para que dichas acciones se pueden aplicar a los objetos en cuanto que están en la caché. También debe haber una cola de segunda se rellena una vez que hemos hecho sin conexión, para que cuando vuelva a conectarse, todo lo que se ha hecho sin conexión puede modificarse a través de fondo. Figura 13 muestra la aplicación sin conexión.

The Application Response When Taken Offline
Figura 13 la respuesta de la aplicación cuando se ponen sin conexión

Hay una serie de cuestiones que deben abordarse además de la caché ya complicada y la cola de mensajes. Por ejemplo, cuando se crea un objeto sin conexión, que no se actualizado o eliminado sin más código porque no tiene un id. Hemos trabajado alrededor que por ahora al simplemente no permitir esas acciones para los objetos creados mientras sin conexión. Por ese mismo motivo, las categorías se crean mientras sin conexión no se puede utilizar para crear productos. No muestro simplemente esas categorías en la lista de categorías disponibles para la creación de un producto. Estos tipos de problemas pueden resolverse si trabaja con identificadores temporales y reorganizar la cola sin conexión.

Además, la plantilla y parciales disponibles deban almacenar en caché. Esto puede hacerse a través de un manifiesto de la caché tal como se define en HTML5 si el grupo de explorador de destino lo admite, o simplemente a través de cargar los parciales y ponerlos en almacenamiento local. Esto es bastante sencillo con Sammy.js y aspecto similar al siguiente:

context.load('templates/products/index.mustache', {cache: true});

Integración en Windows

Internet Explorer 9 es ideal para ejecutar aplicaciones de HTML5. Además, ofrece a las aplicaciones de Web la capacidad de integrar en la barra de tareas de Windows 7, mejorar la aplicación con la posibilidad de mostrar notificaciones, integrar la exploración y ofrecer compatibilidad con listas de salto de forma nativa. La integración de las listas Jump List es sencilla, en su forma más sencilla de formularios sólo una declaración de atributos de etiqueta meta. Esto es exactamente el enfoque utilizado en Karhu, lo que facilita a los usuarios tener acceso a lo que necesitan. Puede dirigir el salto de la lista de tareas para ir a las vistas, agregar productos, agregar categorías, introducción de productos y categorías información general (consulte figura 14). El código siguiente muestra cómo declarar una tarea de la lista de salto simple:

<meta name="msapplication-task"
      content="name=Products;
      action-uri=#/products;
      icon-uri=images/karhu.ico" />

Jump List Tasks
Figura 14 Salto de la lista de tareas

Puede obtener información acerca de fijación y la integración de Windows 7 en Crear mi sitio cubrió a, que tiene ideas adicionales para aplicaciones Web, como las notificaciones de explorador y jumplists dinámico mediante JavaScript.  Es fácil agregar más funcionalidad a su aplicación Web con sólo un poco esfuerzo. Otros puntos de partida para obtener una mejor comprensión del asunto son los Referencia del lenguaje JavaScript MSDN y la documentación de las bibliotecas antes mencionadas y el marco.

En resumen

En este momento, hemos implementado todos los requisitos básicos para la aplicación de ejemplo. Con el tiempo suficiente, nos podríamos también se ocupan de los problemas mencionados anteriormente. Tareas como autenticación, internacionalización y controlar la lógica de negocios deben codificarse independiente de los marcos y las bibliotecas, que son simplemente un punto de partida.

Si usted siempre escribe pruebas y preste atención a la estructura de la aplicación, escribir aplicaciones de JavaScript de listo para la producción que pueden seguir evolucionando es, en nuestra opinión, no sólo es posible pero una meta merece la pena invertir en. Es fácil comenzar, pero es importante que mantenga buscando una base de código limpio y refactorización si es necesario. Si se cumplen estos requisitos, JavaScript le ofrece la oportunidad de escribir aplicaciones muy elegantes y fácil de mantener.

Frank Prößdorf es un desarrollador Web y cofundador de freelance NotJustHosting que ama trabajar con Ruby y JavaScript. Su pasión es descubrir y jugar con las nuevas tecnologías. Periódicamente admite software de código abierto como está muy contento de la oportunidad de aportar y compartir ideas y el código.  Además de su trabajo, le gusta viajar, navegar y jugar al tenis.  Conozca Prößdorf a través de su github perfil o su sitio Web.

Dariusz Parys es un evangelista de desarrollador en Alemania de Microsoft, haciendo de desarrollo Web con un fuerte enfoque en próximas tecnologías. Actualmente está escribiendo un montón de JavaScript y HTML5. Puede ponerse en contacto con Parys a través de su blog downtocode.net

Gracias al siguiente experto técnico para la revisión de este artículo: Aaron Quint