Angular2 : introduction

Mise à jour pour la version rc1 !

Franchement je n’ai jamais aimé AngularJS, je le trouve lourd, laborieux et inutilement complexe. Je préfère de loin une librairie comme Vue.js pour laquelle j’ai écrit quelques articles. Mais l’arrivée de la version 2 me semble prometteuse. L’ensemble reste lourd et d’une mise en oeuvre pas franchement intuitive mais on va dire que le fond est bon et mérite qu’on s’y intéresse.

Angular 2 est actuellement en version beta, donc on peut commencer à regarder de près comment il est conçu sans trop de risque de voir tout changer dans les mois à venir. Il me semble qu’il a acquis à présent une bonne stabilité. D’autre part il est déjà bien documenté. Le présent article est largement inspiré de la page de démarrage en 5mn de la documentation à laquelle il vaut mieux se référer pour avoir du code à jour.

L’environnement de développement

On ne se lance pas dans Angular 2 sans préparation ! On a besoin de mettre en place une infrastructure spécifique pour pouvoir développer une application.

node et npm

Déjà on a besoin de node.js et de son système de packages npm. En gros node permet de faire tourner du JavaScript en dehors d’un navigateur, par exemple sur un serveur, mais également sur votre PC, Mac ou autre. Je ne vais pas m’étendre ici sur node parce que ce n’est pas le sujet, on va se contenter de l’utiliser. Donc si vous voulez développer avec Angular 2 il faut commencer par installer node et npm.

TypeScript

Vous pouvez très bien vous contenter du langage JavaScript pour développer avec Angular 2 mais il a été pensé pour utiliser de préférence TypeScript. Si vous ne connaissez pas ce langage je lui ai accordé quelques articles sur ce site. J’utiliserai systématiquement TypeScript dans les articles consacrés à Angular 2.

Un dossier pour le projet et pour l’application

Evidemment comme vous êtes bien organisé chaque projet trouvera sa place dans un dossier spécifique. Cela va sans dire mais encore mieux en le disant.

Dans ce dossier de projet on va créer un sous-dossier pour accueillir l’application qu’on veut réaliser :

img042

Pour le moment rien de bien compliqué…

La compilation de TypeScript

TypeScript a besoin d’être compilé pour être transformé en langage JavaScript compréhensible pour les navigateurs. Et évidemment on va avoir des options pour cette compilation. On va mettre ces options de compilation dans un fichier tsconfig.json.

Voici le code préconisé :

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  },
  "exclude": [
    "node_modules",
    "typings/main",
    "typings/main.d.ts"
  ]
}

Faisons un peu le point sur les principales options pour savoir ce qu’on demande au compilateur :

  • target : ici on vise la version de JavaScript. Comme a priori tous les navigateurs supportent maintenant l’ES5 on vise cette version. Si vous n’êtes pas trop sûr de quoi il s’agit je vous conseille de lire quelques explications ici.
  • module : TypeScript utilise la notion de module pour isoler le code des différents composants. Plusieurs systèmes sont supportés : commonjs, umd, amd, systemjs… c’est ce dernier qui est utilisé par défaut par les concepteurs d’Angular2 et on va aussi l’utiliser.
  • moduleResolution : indique de quelle manière les modules doivent être résolus, ici on précise qu’on utilise node.
  • sourceMap : Angular a besoin du mappage du code, on va donc générer les fichiers .map.
  • removecomments : enlève tous les commentaires sauf ceux des copyrights.
  • noImplicitAny : envoie une erreur en cas d’utilisation d’un « any » implicite, donc on doit bien typer les variables.

Avec l’option exclude on indique quels fichiers ne doivent pas être pris en compte dans la compilation.

Voilà, maintenant notre TypeScript sera compilé comme on le veut.

TypeScript a besoin d’être aidé

Dans une application on peut être amené à utiliser des librairies JavaScript qui étendent les possibilités du langage de telle manière que le compilateur de TypeScript peut se trouver devant des difficultés (à commencer par Angular lui-même d’ailleurs !). Pour ces librairies il faut créer un fichier spécifique pour que le compilateur puisse travailler correctement. On crée ainsi des « typings », des fichiers avec le suffixe .d.ts. Moralité : on ne pourra pas utiliser une librairie qui ne propose pas un fichier .d.ts !

Lorsque les fichiers .d.ts se trouvent dans la librairie pas de souci, mais parfois il faut aller les chercher ailleurs et là il faut indiquer où. On crée pour cela un fichier typings.json.

Voici le code préconisé :

{
  "ambientDependencies": {
    "core-js": "registry:dt/core-js#0.0.0+20160317120654",
    "jasmine": "registry:dt/jasmine#2.2.0+20160412134438",
    "node": "registry:dt/node#4.0.0+20160509154515"
  }
}

Le chargement des librairies

On a besoin également de dire quelles librairies on veut charger. Pour ça on va créer un fichier package.json avec ce code :

{
  "name": "mon_projet",
  "version": "1.0.0",
  "scripts": {
    "start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
    "lite": "lite-server",
    "postinstall": "typings install",
    "tsc": "tsc",
    "tsc:w": "tsc -w",
    "typings": "typings"
  },
  "license": "ISC",
  "dependencies": {
    "@angular/common":  "2.0.0-rc.1",
    "@angular/compiler":  "2.0.0-rc.1",
    "@angular/core":  "2.0.0-rc.1",
    "@angular/http":  "2.0.0-rc.1",
    "@angular/platform-browser":  "2.0.0-rc.1",
    "@angular/platform-browser-dynamic":  "2.0.0-rc.1",
    "@angular/router":  "2.0.0-rc.1",
    "@angular/router-deprecated":  "2.0.0-rc.1",
    "@angular/upgrade":  "2.0.0-rc.1",
    "systemjs": "0.19.27",
    "core-js": "^2.4.0",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.6",
    "zone.js": "^0.6.12",
    "angular2-in-memory-web-api": "0.0.9",
    "bootstrap": "^3.3.6"
  },
  "devDependencies": {
    "concurrently": "^2.0.0",
    "lite-server": "^2.2.0",
    "typescript": "^1.8.10",
    "typings":"^0.8.1"
  }
}

Je ne vais pas pour le moment entrer dans le détail de tout ça.

On trouve des scripts utilitaires dans scripts, les dépendances dans dependencies, et celles spécifiques au développement dans devDependencies.

Pour se retrouver avec tout ça de façon effective il faut demander à npm de les installer :

npm install

Il faut attendre un petit moment pour que tout se charge. Il est probable que vous rencontriez quelques alertes mais n’en tenez pas compte. Lorsque c’est terminé vous devriez avoir ceci :

img043

Dans node_modules on va trouver toutes les librairies qu’on a demandées :

img044

Et dans typings des fichiers d.ts.

Notre environnement est maintenant en place. On peut en faire une copie pour les prochains projets. C’est quand même lourd tout ça mais bon…

L’application

Maintenant qu’on a notre environnement de développement il n’y a plus qu’à créer une application…

Les modules

Même si ce n’est pas obligatoire et qu’on peut organiser son code comme on veut il est préconisé de créer des modules, chacun devant accomplir une tâche spécifique. Fondamentalement un module contient du code isolé du reste. Il peut recevoir des informations (Import) et aussi en donner (Export : classe, fonction ou valeur). On peut se représenter ainsi un module :

img014

Un composant (component)

Un composant (en fait un module qui renvoie un composant) est la brique de base d’une application Angular2. Il est chargé de gérer une portion de la page HTML. Pour le faire on va lui prévoir un template. D’autre part il aura une logique pour contrôler l’apparence et le comportement de la portion de page qui lui correspond. Pour finir il faudra déterminer de quoi il aura besoin pour assurer la tâche qui lui incombe.

Une application a besoin d’au moins un composant, on va donc en créer un très simple, qu’on va évidemment placer dans le dossier de l’application (mon_application) et qu’on va nommer app.component.ts :

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

@Component({
    selector: 'mon-composant',
    template: '<h1>Je suis un composant et je contrôle ce titre</h1>'
})
export class AppComponent { }

On trouve dans ce code les 3 parties que je vous citais ci-dessus :

  • Les besoins du composant : une application Angular2 est modulaire (notre composant est un module), le code de chaque module est donc isolé du reste, c’est bien pour éviter les conflits de nommage, mais ça implique aussi de prendre des mesures spéciales pour que les modules communiquent. On utilise import pour spécifier ce que l’on veut utiliser. Ici on dit qu’on veut utiliser Component qui se trouve dans la librairie Angular2 (@angular/core), ce qui est le minimum pour un composant !
  • La liaison avec la page HTML : avec @Component on dit à Angular comment le composant doit être créé :
    • selector : le sélecteur pour positionner le composant sur la page
    • template : ce qu’il faut afficher sur la page
  • Le comportement : on finit en créant une classe pour accueillir le comportement du composant. Avec export on permet à un autre module d’utiliser ce que produit ce composant. Dans notre cas la classe est vide parce que notre composant ne fait rien de spécial et n’a rien à offrir.

img015

Mais pour le moment notre application ne va pas encore fonctionner, on a encore besoin d’une chose.

Démarrage de l’application

On va créer un module chargé de démarrer l’application, on va l’appeler main.ts :

img045

Avec ce code :

import { bootstrap }    from '@angular/platform-browser-dynamic';
import { AppComponent } from './app.component';

bootstrap(AppComponent);

On voit ici qu’on a deux importations :

  • Démarrage de la plateforme : comme on va être dans un contexte de navigateur on va utiliser platform-browser-dynamic.
  • Utilisation du composant qu’on a créé.

Ensuite on démarre avec ce composant.

Remarquez que c’est un module qui n’exporte rien mais se contente de faire une action.

La page HTML

Une autre chose qu’il nous faut est la page HTML qu’on va appeler de façon classique index.html :

img046

Avec ce code :

<html>
  <head>
    <title>Mon Projet</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">

    <script src="node_modules/core-js/client/shim.min.js"></script>
    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/reflect-metadata/Reflect.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>

    <script src="systemjs.config.js"></script>
    <script>
      System.import('app').catch(function(err){ console.error(err); });
    </script>
  </head>

  <body>
    <mon-composant>Chargement...</mon-composant>
  </body>
</html>

On a :

  • le chargement des librairies,
  • la configuration du chargement des modules pour systemjs,
  • le chargement du module,
  • la mise en place du composant.

Configuration de systemjs

Il faut aussi configurer systemjs, on crée un fichier de configuration systemjs.config.js :

img047

Avec ce code :

/**
 * System configuration for Angular 2 samples
 * Adjust as necessary for your application needs.
 */
(function(global) {
  // map tells the System loader where to look for things
  var map = {
    'app':                        'app', // 'dist',
    '@angular':                   'node_modules/@angular',
    'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
    'rxjs':                       'node_modules/rxjs'
  };
  // packages tells the System loader how to load when no filename and/or no extension
  var packages = {
    'app':                        { main: 'main.js',  defaultExtension: 'js' },
    'rxjs':                       { defaultExtension: 'js' },
    'angular2-in-memory-web-api': { defaultExtension: 'js' },
  };
  var ngPackageNames = [
    'common',
    'compiler',
    'core',
    'http',
    'platform-browser',
    'platform-browser-dynamic',
    'router',
    'router-deprecated',
    'upgrade',
  ];
  // Add package entries for angular packages
  ngPackageNames.forEach(function(pkgName) {
    packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };
  });
  var config = {
    map: map,
    packages: packages
  }
  System.config(config);
})(this);

La compilation et le lancement

Il ne nous reste plus qu’à compiler le code TypeScript :

npm start

La compilation a lieu et on a la génération des fichiers JavaScript :

img009

Mais pas seulement ! Regardez cette ligne dans package.json :

"start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",

On lance la compilation avec tsc, en mode watch avec w (ça veut dire que si un fichier source change la compilation se relance automatiquement), et on lance un serveur léger lite-server pour node. Au final on a le navigateur qui affiche ceci :

img010

Bon, c’est bien du boulot pour afficher un titre ! Mais maintenant on a toute l’intendance en place pour faire beaucoup mieux !

Vous pouvez vérifier que la compilation se déclenche dès que vous faites une modification en changeant par exemple le texte du titre dans le composant.

Plunker

Une autre façon de vous lancer dans Angular2 sans installer d’intendance en local est d’utiliser Plunker :

img011

C’est une plateforme en ligne bien pratique qui n’est pas exclusivement dédiée à Angular mais aussi à Bootstrap, JQuery, jQueryUI, React, Backbone, YUI, KendoUI.

Pour démarrer un projet Angular2 avec TypeScript c’est ici dans l’éditeur :

img012

Vous avez alors tout de suite une trame de base fonctionnelle.

Dans le prochain article on utilisera ces bases pour commencer à construire quelque chose !

Laisser un commentaire