Skip to content
This repository was archived by the owner on Jan 18, 2019. It is now read-only.

Latest commit

 

History

History

2-angular2

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Creando una app de Angular 2 con Webpack

Angular 2 es la evolución de uno de los frameworks webs modernos más utilizados. En el caso de esta segunda versión, se optó por realizar el desarrollo utilizando TypeScript, un lenguaje que compila a JavaScript y que brinda mayor seguridad a la hora de crear desarrollos más grandes gracias al manejo de tipos y de clases. Angular 2 busca ser un mejor framework web basandose en todo lo aprendido con su versión anterior. Entre otras cosas apunta a mejorar la performance con respecto a su versión anterior, así como a brindar un conjunto de herramientas más modernas, como por ejemplo, permitiendo utilizarse en dispositivos móviles creando componentes nativos gracias a NativeScript y similares.

Junto con la aparición de frameworks web más modernos, aparecen nuevas necesidades y con ellas, nuevas herramientas. Este es el caso de Webpack, que fue tomando impulso en el último tiempo. Webpack es un bundler de módulos, lo cual implica que junta archivos con sus dependencias y genera archivos estáticos. Para el caso de Angular 2 con TypeScript, también va a servir para compilar el código a JavaScript, así como también minificarlo y otro tipo de tareas similares.

El tutorial oficial de Angular 2 utiliza System.js en lugar de utilizar Webpack, pero como parte de este módulo se verá como crear la misma aplicación utilizando esta otra herramienta, dado que nos va a ayudar a la hora de integrar con ASP.NET Core.

En este módulo vamos a crear una aplicación base con Angular 2 usando Typescript y Webpack basada en el tutorial oficial de Angular 2 que crea un sitio llamado Tour Of Heroes.

Tarea 1: Creando la estructura base

A la hora de crear una aplicación con Angular 2, necesitamos configurar las dependencias por medio de npm, configurar TypeScript (en el caso de usar este lenguaje) junto con las definiciones de los tipos faltantes. En esta tarea se crearan estas dependencias/configuraciones.

  1. Crear una carpeta para desarrollar la aplicación y abrir una terminal/consola en ese directorio.

  2. Inicializar el package.json donde se tendrán las dependencias de npm ejecutando el siguiente comando. El parámetro --yes completa los campos que son requeridos con los valores por defecto.

    npm init --yes
    

    Iniciando el package.json

    Iniciando el package.json

    Nota: El contenido del archivo debería quedar similar al siguiente.

    {
       "name": "tour-of-heroes",
       "version": "1.0.0",
       "description": "",
       "main": "index.js",
       "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1"
       },
       "keywords": [],
       "author": "",
       "license": "ISC"
    }
  3. Agregar las dependencias para Angular en el package.json agregando el siguiente nodo al json.

    "dependencies": {
      "@angular/common": "2.1.0",
      "@angular/compiler": "2.1.0",
      "@angular/core": "2.1.0",
      "@angular/forms": "2.1.0",
      "@angular/platform-browser": "2.1.0",
      "@angular/platform-browser-dynamic": "2.1.0",
      "@angular/router": "3.1.0",
      "@types/node": "6.0.45",
      "bootstrap": "3.3.7",
      "core-js": "2.4.1",
      "event-source-polyfill": "0.0.7",
      "reflect-metadata": "0.1.8",
      "rxjs": "5.0.0-beta.12",
      "zone.js": "0.6.25"
    },

    Nota: Esto es similar a agregar las dependencias con el comando npm install --save. El siguiente comando sería lo mismo que agregar las dependencias a mano en el archivo y aparte ejecutar npm install luego.

  4. Ahora, agregaremos las dependencias de las herramientas que necesitamos para el desarrollo, tales como TypeScript y Webpack. Para esto agregar el siguiente nodo dentro del package.json.

    "devDependencies": {
      "angular2-template-loader": "0.5.0",
      "css-loader": "0.25.0",
      "extract-text-webpack-plugin": "1.0.1",
      "file-loader": "0.9.0",
      "html-loader": "0.4.4",
      "raw-loader": "0.5.1",
      "rimraf": "2.5.4",
      "style-loader": "0.13.1",
      "ts-loader": "0.9.3",
      "typescript": "2.0.3",
      "webpack": "1.13.2",
      "webpack-dev-server": "1.16.2",
      "webpack-merge": "0.14.1"
    }

    Nota 1: Nuevamente, esto es similar a agregar las dependencias con el comando npm install --save-dev (notar la diferencia entre --save y --save-dev). El siguiente comando equivale a agregar las dependencias a mano en el archivo y aparte ejecutar npm install luego.

    Nota 2: En realidad, todas las dependencias en estos casos son dependencias de desarrollo (es decir devDependencies) dado que las herramientas van a copiar lo requerido de nuestras dependencias a los archivos que se utilizarán en producción. Sin embargo, se separan las dependencias para simplificar la comprensión de cuáles son dependencias de la aplicación y cuales de las herramientas.

  5. Por último, vamos a agregar algunos scripts al package.json actualizando el nodo scripts con el siguiente contenido.

    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1",
      "start": "webpack-dev-server --inline --hot --progress --port 8080",
      "build": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"
    },

    Nota: A continuación una breve descripción de cada script.

    • start: Este script ejecutará el server de desarrollo de Webpack en el puerto 8080. Utilizará el archivo webpack.config.js que se creará en la próxima tarea para la configuración de Webpack.

    • build: Este script borra la carpeta dist y ejecuta Webpack utilizando la configuración en config/webpack.prod.js que se creará en la próxima tarea.

  6. Ahora, crear un nuevo archivo llamado tsconfig.json. Este archivo contiene las configuraciones para TypeScript.

  7. Agregar el siguiente contenido al archivo recién creado.

    {
      "compilerOptions": {
        "target": "es5",
        "moduleResolution": "node",
        "sourceMap": true,
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "skipDefaultLibCheck": true,
        "lib": [ "es6", "es7", "dom" ],
        "types": [ "node" ]
      },
      "exclude": [ "bin", "node_modules" ],
      "atom": { "rewriteTsconfig": false }
    }

    Nota: el type node se agregó como dependencia junto a las dependencias de Angular 2 en los pasos anteriores.

    "dependencies": {
      "@types/node": "6.0.45"
    },
  8. Por último, instalar todas las dependencias ejecutando el siguiente comando.

    npm install
    

Tarea 2: Configurando Webpack

En esta tarea se crearán los archivos de configuración de Webpack. Para eso, se tendrán un archivo por para la configuración de desarrollo, otro para la de producción y uno más con la configuración en común.

  1. Crear un archivo con el nombre webpack.config.js. Este tendrá la configuración por defecto de Webpack.

  2. Agregar el siguiente contenido al archivo recién creado. Con esto se usará la configuración de desarrollo siempre se creará en los próximos pasos.

    module.exports = require('./config/webpack.dev.js');
  3. Ahora, crear la carpeta config y crear el archivo helpers.js.

  4. Agregar el siguiente contenido al archivo recién creado, que contiene una función que nos ayuda a resolver el path desde el root de la solución.

    var path = require('path');
    var _root = path.resolve(__dirname, '..');
    
    function root(args) {
      args = Array.prototype.slice.call(arguments, 0);
      return path.join.apply(path, [_root].concat(args));
    }
    
    exports.root = root;
  5. Ahora, agregar el archivo de configuración en común de Webpack. Para esto crear el archivo webpack.common.js dentro de la carpeta config.

  6. Agregar el siguiente contenido al archivo creado en el paso anterior.

    var webpack = require('webpack');
    var ExtractTextPlugin = require('extract-text-webpack-plugin');
    var helpers = require('./helpers');
    
    module.exports = {
      entry: {
        'main': helpers.root('ClientApp', 'main.ts')
      },
    
      resolve: {
        extensions: ['', '.js', '.ts']
      },
    
      module: {
        loaders: [
          {
            test: /\.ts$/,
            include: /ClientApp/,
            loaders: ['ts', 'angular2-template-loader']
          },
          {
            test: /\.html$/,
            loader: 'html'
          },
          {
            test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
            loader: 'file?name=assets/[name].[hash].[ext]'
          },
          {
            test: /\.css$/,
            exclude: helpers.root('ClientApp', 'app'),
            loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
          },
          {
            test: /\.css$/,
            include: helpers.root('ClientApp', 'app'),
            loader: 'raw'
          }
        ]
      }
    };

    Nota: En esta configuración se definen tres items:

    • El punto de entrada (entry): en este caso será el archivo main.ts dentro de la carpeta ClientApp que se creará en la próxima tarea.
    • Los tipos de archivos a resolver (resolve): se buscarán los archivos con las extensiones en el orden de aparición, lo cual implica que para un import de un archivo llamado file, primero se buscará file, luego file.js y por último file.ts.
    • Los módulos, especialmente los loaders (module): cada tipo de archivo es cargado por un módulo especial de webpack. En este punto se definen las regex para detectar el archivo y los loaders a utilizar.
  7. Ahora, crear el archivo webpack.dev.js dentro de la carpeta config, que tendrá las configuraciones propias de desarrollo.

  8. Agregar el siguiente contenido al archivo recién creado.

    var webpackMerge = require('webpack-merge');
    var ExtractTextPlugin = require('extract-text-webpack-plugin');
    var commonConfig = require('./webpack.common.js');
    var helpers = require('./helpers');
    
    module.exports = webpackMerge(commonConfig, {
      devtool: 'cheap-module-eval-source-map',
    
      output: {
        path: helpers.root('dist'),
        publicPath: 'http://localhost:8080/',
        filename: '[name].js'
      },
    
      plugins: [
        new ExtractTextPlugin('[name].css')
      ],
    
      devServer: {
        historyApiFallback: true,
        stats: 'minimal'
      }
    });

    Nota: Este código utiliza webpack-merge para unir la configuración definida anteriormente en el archivo webpack.common.js con al definida en este archivo.

    Esta configuración define los siguientes items:

    • Herramientas de desarrollo (devtool): acá se define el tipo de herramienta que se utilizará para ayudar a debuggear la aplicación creando source maps.
    • El punto de salida (output): en este caso será un archivo llamado igual al definido en el punto de entrada (osea, main) en la carpeta dist y se expondrá como parte del web server que brinda webpack en el puerto 8080.
    • Plugins: acá se define el plugin que va a extraer los estilos al archivo css con nombre igual al punto de entrada (osea, main).
    • Configuraciones propias del server de desarrollo (devServer): el servidor de desarrollo de webpack tiene varias configuraciones disponibles, en este caso configuramos para que soporte crear una Single Page Application (SPA), soportando cualquier tipo de path.
  9. Por último, crear el archivo para las configuraciones de producción llamado webpack.prod.js dentro de la carpeta config.

  10. Agregar el siguiente contenido al archivo recién creado.

    var webpack = require('webpack');
    var webpackMerge = require('webpack-merge');
    var ExtractTextPlugin = require('extract-text-webpack-plugin');
    var commonConfig = require('./webpack.common.js');
    var helpers = require('./helpers');
    
    const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
    
    module.exports = webpackMerge(commonConfig, {
      devtool: 'source-map',
    
      output: {
          path: helpers.root('dist'),
          filename: '[name].js',
          publicPath: '/'
      },
    
      htmlLoader: {
        minimize: false // workaround for ng2
      },
    
      plugins: [
        new webpack.NoErrorsPlugin(),
        new webpack.optimize.DedupePlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin('[name].css'),
        new webpack.DefinePlugin({
          'process.env': {
            'ENV': JSON.stringify(ENV)
          }
        })
      ]
    });

    Nota: Este código es similar al de la configuración de desarrollo, con la diferencia de los plugins que utiliza, realizando tareas típicas como minificación de JavaScript y CSS, entre otras cosas. Aparte, la salida es un archivo físico en el directorio dist.

Tarea 3: Creando la aplicación base

  1. Crear el archivo index.html dentro de la carpeta raíz de la aplicación (donde está el package.json).

    <!DOCTYPE html>
    <html>
      <head>
        <base href="/">
        <title>Tour of Heroes</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="/main.css" />
      </head>
      <body>
        <my-app>Loading...</my-app>
    
        <script src="/main.js" ></script>
      </body>
    </html>

    Nota: El directorio y los nombres de los archivos de JavaScript y CSS fueron definidos en la configuración de Webpack.

  2. Crear una carpeta con el nombre ClientApp.

    Nota: El nombre de esta carpeta fue utilizado en la configuración de Webpack. Si se prefiere utilizar otro, asegurarse de cambiarlo en esa configuración.

  3. Ahora, crear el archivo main.ts dentro de la carpeta ClientApp que es el punto de entrada definido en nuestras configuraciones de Webpack.

  4. Agregar el siguiente contenido al archivo recién creado.

    import './vendor';
    
    import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
    import { enableProdMode } from '@angular/core';
    import { AppModule } from './app/app.module';
    
    import './public/styles.css'
    
    if (process.env.ENV === 'production') {
      enableProdMode();
    }
    
    platformBrowserDynamic().bootstrapModule(AppModule);
  5. Luego, crear el archivo vendor.ts dentro de la carpeta ClientApp. Este archivo tendrá todas las referencias a frameworks y polyfills (funciones que nos completan funcionalidad que no está disponible en el browser) requeridas por nuestra aplicación.

  6. Agregar el siguiente contenido al archivo recién creado.

    // Polyfills
    import 'core-js/es6';
    import 'core-js/es7/reflect';
    import 'event-source-polyfill';
    require('zone.js/dist/zone');
    
    if (process.env.ENV === 'production') {
      // Production
    } else {
      // Development
      Error['stackTraceLimit'] = Infinity;
      require('zone.js/dist/long-stack-trace-zone');
    }
    
    // Angular 2
    import '@angular/platform-browser';
    import '@angular/platform-browser-dynamic';
    import '@angular/core';
    import '@angular/common';
    import '@angular/router';
    
    // RxJS
    import 'rxjs';
    
    // Other vendors for example jQuery, Lodash or Bootstrap
    // You can import js, ts, css, sass, ...
  7. Por último, agregar el contenido de la carpeta assets dentro de la carpeta ClientApp. Estos archivos están basados en el resultado final del tutorial oficial de Angular 2.

    Nota: La carpeta assets contiene dos carpetas, una llamada public y la otra app. La primera, contiene estilos utilizados en la aplicación.

    La carpeta app contiene definiciones de componentes, modelos, servicios y módulos de la aplicación detalladas a continuación:

    • app.component: este componente es el que podríamos denominar layout de la aplicación, incluyendo el título y la barra de navegación.

    • app.module: este archivo, define el módulo con todas sus dependencias. Este módulo, es el utilizado para arrancar la aplicación.

    • app.routing: en este archivo están definidas todas las rutas del lado del cliente.

    • dashboard.component: este componente es el primero que se visualiza que muestra recuadros con algunos héroes.

    • hero: este es el modelo utilizado en todos los componentes y servicios.

    • hero-detail.component: este componente es el detalle de los héroes, que permite su modificación.

    • hero.service: este es el servicio que tiene todas las operaciones sobre los héroes (altas, bajas, modificaciones, etc.)

    • heroes.component: este componente es el listado de héroes.

Tarea 4: Corriendo la aplicación

  1. En la consola/terminal, ejecutar el siguiente comando.

    npm start
    

    Ejecutando la aplicación

    Ejecutando la aplicación

  2. Abrir un navegador e ir a http://localhost:8080.

    Navegando a la aplicación

    Navegando a la aplicación

  3. Jugar con la app un poco, seleccionando un héroe, modificando su nombre, creando nuevos y borrando los existentes.

    Jugando con la aplicación

    Jugando con la aplicación

    Nota: La imagen es del tutorial oficial de Angular 2.

  4. Luego de probar la app, terminar la aplicación en la consola con Ctrl + C.

Conclusiones

Las aplicaciones web modernas soportan crear aplicaciones de notoria complejidad con un costo a la hora de inicializar el desarrollo de la misma.

Luego de terminado este módulo, se cuetan con los conocimientos básicos para crear este tipo de aplicaciones sin problemas. Ahora solo queda consumir los datos desde una API y servir los archivos en producción.