Criando WebApp com Angular2, SystemJs e Gulp

7 mar

Fala Galera,

Hoje vamos demostrar como podemos utilizar o Angular2 com SystemJS e Gulp. O objetivo é demostrar como podemos criar um processo de compilação utilizando Gulp um processo de runtime utilizando SystemJs e o Angular2 com Typescript como nossa linguagem de programação front-end.

Setup do Projeto.

Para iniciar devemos escolher nossa IDE de desenvolvimento, eu  escolhi usar o Visual Studio Code mas fique a vontade para utilizar a sua IDE de desenvolvimento favorita. Vamos criar uma nova pasta chamada WebApp e dentro da pasta raiz irei criar um pasta chamada src.

Essa pasta src será responsável por ter o código fonte do projeto.

Configurando as dependências do projeto

Nosso projeto precisa de algumas dependências para funcionar e para fazer o gerenciamento dessas dependências escolhi utilizar o NPM.

Abra um terminal e vá até a raiz do projeto. Na raiz do projeto digite npm init. Este comando irá criar a configuração básica para usarmos o NPM.

Após a configuração do NPM adicione a seguintes dependências conforme exemplo do package.json abaixo:

{
  "name": "angular2-systemjs",
  "version": "1.0.0",
  "description": "Criando uma WebApp com Angular2 e SystemJs",
  "scripts": {
  },
  "author": "Rafael Cruz",
  "license": "MIT",
  "private": true,
  "dependencies": {
    "@angular/common": "^2.4.1",
    "@angular/compiler": "^2.4.1",
    "@angular/core": "^2.4.1",
    "@angular/forms": "^2.4.1",
    "@angular/http": "^2.4.1",
    "@angular/platform-browser": "^2.4.1",
    "@angular/platform-browser-dynamic": "^2.4.1",
    "@angular/router": "^3.4.1",
    "@angular/upgrade": "^2.4.1",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.8",
    "rxjs": "^5.0.2",
    "systemjs": "0.19.40",
    "ts-helpers": "^1.1.1",
    "zone.js": "^0.7.4"
  },
  "devDependencies": {
    "gulp": "3.9.1",
    "gulp-util": "3.0.8",
    "gulp-uglify": "2.0.1",
    "gulp-watch": "4.3.11",
    "gulp-sass": "3.1.0",
    "gulp-copy": "1.0.0",
    "gulp-sourcemaps": "^1.6.0",
    "gulp-tslint": "^6.1.1",
    "gulp-typescript": "^3.1.3",
    "typescript": "~2.0.3",
    "ts-node": "^1.3.0",
    "del": "^2.2.0",
    "typings": "^1.3.3",
    "tslint": "^4.3.0",
    "concurrently": "^2.2.0"
  }
}

Notem que estamos colocando como dependência o Angular2 e o Gulp.

Configurando o Typings

Para que possamos compilar nossos arquivos typescript precisamos colocar dependências globais como nodejs e o core-js. A responsabilidade do Typings é referenciar as definições de tipo e estrutura contida no nodejs e no core-js para que o Typescript consiga compilar corretamenta os arquivos.

Adicione um novo arquivo chamado typings.json, esse arquivo irá conter as dependências globais que iremos utilizar.

{
  "globalDependencies": {
    "core-js": "registry:dt/core-js#0.0.0+20160725163759",
    "node": "registry:dt/node#6.0.0+20160909174046"
  }
}

Para fazer a instalação dessas dependências globais vamos voltar ao package.json e no nó scripts adicione o seguinte comando de pós instalação (postinstall), veja o package.json abaixo como referência:

{
  "name": "angular2-systemjs",
  "version": "1.0.0",
  "description": "Criando uma WebApp com Angular2 e SystemJs",
  "scripts": {
    "postinstall": "typings install"
  },
  "author": "Rafael Cruz",
  "license": "MIT",
  "private": true,
  "dependencies": {
    "@angular/common": "^2.4.1",
    "@angular/compiler": "^2.4.1",
    "@angular/core": "^2.4.1",
    "@angular/forms": "^2.4.1",
    "@angular/http": "^2.4.1",
    "@angular/platform-browser": "^2.4.1",
    "@angular/platform-browser-dynamic": "^2.4.1",
    "@angular/router": "^3.4.1",
    "@angular/upgrade": "^2.4.1",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.8",
    "rxjs": "^5.0.2",
    "systemjs": "0.19.40",
    "ts-helpers": "^1.1.1",
    "zone.js": "^0.7.4"
  },
  "devDependencies": {
    "gulp": "3.9.1",
    "gulp-util": "3.0.8",
    "gulp-uglify": "2.0.1",
    "gulp-watch": "4.3.11",
    "gulp-sass": "3.1.0",
    "gulp-copy": "1.0.0",
    "gulp-sourcemaps": "^1.6.0",
    "gulp-tslint": "^6.1.1",
    "gulp-typescript": "^3.1.3",
    "typescript": "~2.0.3",
    "ts-node": "^1.3.0",
    "del": "^2.2.0",
    "typings": "^1.3.3",
    "tslint": "^4.3.0",
    "concurrently": "^2.2.0"
  }
}

Com o package.json configurado, execute o comando abaixo para fazer instalações das dependências:

  • npm install

Configurando o Typescript

Precisamos agora configurar o Typescript e para isso devemos criar um arquivo chamado tsconfig.json. Esse arquivo é o responsável por configurar o comportamento do Typescript e dizer ao compilador do Typescript (tsc) como ele vai resolver os módulos, quais arquivos serão ignorados, se iremos utilizar decoradores e etc.

Crie um novo arquivo chamado tsconfig.json na raiz do projeto e adicione as seguintes configurações:

{
  "compilerOptions": {
      "baseUrl": "./src",
      "target": "es5",
      "module": "system",
      "moduleResolution": "node",
      "emitDecoratorMetadata": true,
      "experimentalDecorators": true,
      "removeComments": false,
      "noImplicitAny": false
    },
    "exclude": [
        "node_modules",
        "gulpfile.ts"
    ]
}

Note que o nosso baseUrl é aonde ficarão os arquivos typescript a serem compilados.

Configurando o GulpFile.

Iremos utilizar o Gulp como nosso automatizador de tarefas. Ele será o responsável por compilar o typescript, copiar as dependências do projeto e o resultado da compilação para a pasta build e no final levantar nosso servidor web para que possamos testar nossa aplicação.

Crie um novo arquivo chamado GulpFile.ts na raiz do projeto e adicione o código abaixo:

"use strict";

const gulp = require("gulp");
const del = require("del");
const tsc = require("gulp-typescript");
const sourcemaps = require('gulp-sourcemaps');
const tsProject = tsc.createProject("tsconfig.json");
const tslint = require('gulp-tslint');


/**
 * Remove a pasta build
 */
gulp.task('clean', (cb) => {
    return del(["build"], cb);
});


/**
 * Compila o Typescript e criar os sourcemaps
 */
gulp.task("compile", () => {
    let tsResult = gulp.src("src/**/*.ts")
        .pipe(sourcemaps.init())
        .pipe(tsProject());
    return tsResult.js
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest("build"));
});

/**
 * Copia todos os recursos que o projeto necessita como imagens, css e etc
 */
gulp.task("resources", () => {
    return gulp.src(["src/**/*"])
        .pipe(gulp.dest("build"));
});


/**
 * Copia todas as dependências para a pasta lib
 */
gulp.task("libs", () => {
    return gulp.src([
            'core-js/client/shim.min.js',
            'systemjs/dist/system-polyfills.js',
            'systemjs/dist/system.src.js',
            'reflect-metadata/Reflect.js',
            'rxjs/**',
            'zone.js/dist/**',
            '@angular/**',
            '@types/**',
        ], {cwd: "node_modules/**"}) /* Glob required here. */
        .pipe(gulp.dest("build/lib"));
});

/**
 * Cria Watch para mudanças no TypeScript, HTML e css
 */
gulp.task('watch', function () {
    gulp.watch(["src/**/*.ts"], ['compile']).on('change', function (e) {
        console.log('TypeScript file ' + e.path + ' has been changed. Compiling.');
    });
    gulp.watch(["src/**/*.html", "src/**/*.css"], ['resources']).on('change', function (e) {
        console.log('Resource file ' + e.path + ' has been changed. Updating.');
    });
});

/**
 * Compila o projeto
 */
gulp.task("build", ['compile', 'resources', 'libs'], () => {
    console.log("Building the project ...");
});

Devemos agora voltar ao package.json e adicionar os comando que criamos no GulpFile.ts na linha de comando. Abra o arquivo package.json e adicione os comando no nó scripts conforme package.json abaixo:

{
  "name": "angular2-systemjs",
  "version": "1.0.0",
  "description": "Criando uma WebApp com Angular2 e SystemJs",
  "scripts": {
    "clean": "gulp clean",
    "compile": "gulp compile",
    "build": "gulp build",
    "postinstall": "typings install"
  },
  "author": "Rafael Cruz",
  "license": "MIT",
  "private": true,
  "dependencies": {
    "@angular/common": "^2.4.1",
    "@angular/compiler": "^2.4.1",
    "@angular/core": "^2.4.1",
    "@angular/forms": "^2.4.1",
    "@angular/http": "^2.4.1",
    "@angular/platform-browser": "^2.4.1",
    "@angular/platform-browser-dynamic": "^2.4.1",
    "@angular/router": "^3.4.1",
    "@angular/upgrade": "^2.4.1",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.8",
    "rxjs": "^5.0.2",
    "systemjs": "0.19.40",
    "ts-helpers": "^1.1.1",
    "zone.js": "^0.7.4"
  },
  "devDependencies": {
    "gulp": "3.9.1",
    "gulp-util": "3.0.8",
    "gulp-uglify": "2.0.1",
    "gulp-watch": "4.3.11",
    "gulp-sass": "3.1.0",
    "gulp-copy": "1.0.0",
    "gulp-sourcemaps": "^1.6.0",
    "gulp-tslint": "^6.1.1",
    "gulp-typescript": "^3.1.3",
    "typescript": "~2.0.3",
    "ts-node": "^1.3.0",
    "del": "^2.2.0",
    "typings": "^1.3.3",
    "tslint": "^4.3.0",
    "concurrently": "^2.2.0"
  }
}

Os comando que adicionamos no packages.json executam as seguintes tarefas:

  • clean (limpa o diretório build)
  • compile (compila os arquivo typescript sem copiar as dependências)
  • build (compila os arquivos typescript e copia as dependências e os assets)

No terminal execute os comandos abaixos

  • npm run clean
  • npm run build

Se tudo der certo você deverá ver saída de terminal igual a imagem abaixo:

Systemjs

Configurando o SystemJs

Agora que a parte de compilação do nosso projeto está pronta, vamos configurar o SystemJs. O SystemJs é um dynamic module loader e ele é responsável por encontrar os módulos necessários para que a nossa aplicação funcione corretamente.

Ele é totalmente compatível com o Typescript e com o Angular2 mas precisamos configurar como ele irá encontrar as dependências que estamos utilizando.

Qualquer nova dependências que utilizarmos no nosso projeto tem que ser configurado no SystemJs, caso não faça isso, ele dirá que o arquivo não foi encontrado e sua aplicação poderá não funcionar corretamente.

Dentro da pasta src, crie um arquivo chamado systemjs.config.js e adicione o código abaixo:

(function (global) {
    System.config( {
        paths: {
            'npm:':'lib/'
        }, 
        map: {
            app:'app', 

            // angular bundles
            '@angular/core':'npm:@angular/core/bundles/core.umd.js', 
            '@angular/common':'npm:@angular/common/bundles/common.umd.js', 
            '@angular/compiler':'npm:@angular/compiler/bundles/compiler.umd.js', 
            '@angular/platform-browser':'npm:@angular/platform-browser/bundles/platform-browser.umd.js', 
            '@angular/platform-browser-dynamic':'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 
            '@angular/http':'npm:@angular/http/bundles/http.umd.js', 
            '@angular/router':'npm:@angular/router/bundles/router.umd.js', 
            '@angular/forms':'npm:@angular/forms/bundles/forms.umd.js', 

            // other libraries
            'rxjs':'npm:rxjs', 
        }, 
        // packages tells the System loader how to load when no filename and/or no extension
        packages: {
            app: {
                main:'main.js', 
                defaultExtension:'js'
            }, 
            rxjs: {
                defaultExtension:'js', 
                main:'rx.js', 
            }
        }, 
    }); 
})(this); 

 

Criando o WebApp com o AngularJs

Com tudo configurado, basta agora criarmos nosso projeto, para isso precisamos criar nossa página de inicio. Crie um arquivo chamado index.html dentro da pasta src e adicione o código abaixo:

<!DOCTYPE html>
<html>
<head>
  <base href="/">
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />

    <title>WebApp com Angular2 e SystemJs</title>

    <!-- google font libraries -->
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700,800|PT+Sans+Narrow:400,700|PT+Sans:400,700" rel="stylesheet">
    
    
    <script src="lib/core-js/client/shim.min.js"></script>
    <script src="lib/zone.js/dist/zone.js"></script>
    <script src="lib/reflect-metadata/Reflect.js"></script>
    <script src="lib/systemjs/dist/system.src.js"></script>

    <!-- 2. Configure SystemJS -->
    <script src="systemjs.config.js"></script>
    <script>
        System.import('app')
                .then(null, console.error.bind(console));
    </script>

</head>
<body>
  <div id="theme-wrapper">
    <root>
    </root>	
  </div>
</body>
</html>

Agora vamos adicionar os arquivos typescript, ele terão o código da nossa aplicação. Crie uma pasta chamada app dentro de src. Adicione um arquivo chamado main.ts dentro da pasta app, este arquivo será o responsável para iniciar nossa aplicação Angular2.

Adicione o código conforme exemplo abaixo:

///<reference path="../../typings/index.d.ts"/>

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

Agora vamos criar o nosso módulo principal do Angular2. Ele será o ponto de entrada da nossa aplicação. Crie um arquivo chamado app.module.ts dentro da pasta app e adicione o código abaixo:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, LOCALE_ID } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Por fim, vamos criar um componente Angular2, ele será o componente que irá exibir os dados de entrada da página.

Crie um arquivo chamado app.component.ts e adicione o código abaixo:

import { Component } from '@angular/core';

@Component({
  selector: 'root',
  templateUrl: 'app/app.component.html'
})
export class AppComponent {
  public title:String = "Hello Angular 2 application com SystemJS";
}

Como vocês podem notar neste componente estamos utilizando um template. Precisamos criar esse template que nada mais é que o html que iremos exibir o valor da variável title.

Crie um arquivo chamado app.component.html e adicione o código abaixo:

<div>
  {{ title }}
</div>

Pronto nossa aplicação está pronta, basta agora executar o build. Vá ao terminal e execute os comandos abaixo:

  • npm run clean
  • npm run build

Configurando o Lite-Server, um servidor web em NodeJs

Para que possamos testar nossa aplicação, precisamos de um servidor web. Sorte que podemos contar com o lite-server, um mini servidor web feito em nodejs para nos ajudar nesta tarefa. Ele tem diversas funcionalidades interessantes como SyncBrowser, Watcher e etc.

Para utilizarmos o lite-server, precisamos voltar ao package.json e adicionar a sua dependência no nó devDependencies, conforme exemplo abaixo:

"devDependencies": {
   "gulp": "3.9.1",
   "gulp-util": "3.0.8",
   "gulp-uglify": "2.0.1",
   "gulp-watch": "4.3.11",
   "gulp-sass": "3.1.0",
   "gulp-copy": "1.0.0",
   "gulp-sourcemaps": "^1.6.0",
   "gulp-tslint": "^6.1.1",
   "gulp-typescript": "^3.1.3",
   "typescript": "~2.0.3",
   "ts-node": "^1.3.0",
   "del": "^2.2.0",
   "typings": "^1.3.3",
   "tslint": "^4.3.0",
   "concurrently": "^2.2.0",
   "lite-server": "^2.2.2"
   
 }

Execute o comando npm install para instalar o lite-server no nosso projeto. Após a instalação do lite-server, precisamos configurá-lo, para isso crie um novo arquivo chamado bs-config.json na raiz do projeto e adicione o json abaixo:

{
  "port": 4000,
  "files": [
    "build/**/*.{html,htm,css,js,ts,map}"
  ],
  "server": {
    "baseDir": "build"
  }
}

Pronto nosso lite-server está configurado, basta agora executar mas para fazer isso precisamos de mais uma configuração, vamos adicionar mais um comando no package.json, esse novo comando será responsável por levantar o servidor lite-server e executar nossa aplicação.

Abra o package.json e no nó scripts adicione o json conforme exemplo abaixo:

"scripts": {
  "clean": "gulp clean",
  "compile": "gulp compile",
  "build": "gulp build",
  "postinstall": "typings install",
  "start": "concurrent --kill-others \"gulp watch\" \"lite-server\""
},

 

Testando a aplicação

Vamos verificar se tudo está configurado corretamente, no terminal execute os seguintes comandos:

  • npm run clean
  • npm run build
  • npm run start

Se tude der certo você deverá ver a aplicação rodando conforme figura abaixo:

Systemjs1

Para alteramos nossa aplicação não precisamos parar o servidor, basta alterar o arquivo que o próprio watcher irá compilar os arquivos e copiar para a pasta build e fazer o reload da página, assim basta executar a aplicação e fazer as modificações necessárias no projeto.

O código deste exemplo está no meu GitHub através deste link

Espero que tenham gostado deste post e claro fiquem a vontade para comentar.

Abs e até a próxima

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *