Publié le

Utiliser des Custom Elements Angular 7 dans une application Spring Boot

Auteurs

Spring Boot devient de plus en plus intéressant pour le développement et la configuration des applications basées sur Spring.
De son côté, Angular s’impose comme l’un des frameworks JavaScript les plus appréciés.

Il est donc très pertinent de combiner les deux pour créer des applications puissantes.
Dans ce contexte, je vais te montrer comment utiliser l’une des fonctionnalités d’Angular, Angular Elements, et l’intégrer dans ton application Spring Boot.


Création du projet avec Spring Initializr

Spring Initializr est un outil qui permet de générer rapidement des projets Spring Boot et d’ajouter des configurations par défaut.

Créons notre projet :

👉 Accéder à Spring Initializr

Choisissez :

  • Project : Maven Project
  • Language : Java
  • Spring Boot : (dernière version stable)
  • Dependencies : Spring Web, Thymeleaf (optionnel), et toute autre dépendance nécessaire

Ensuite, cliquez sur Generate pour télécharger le projet.


Étape suivante

Une fois le projet créé, nous allons préparer l’intégration d’Angular Elements dans l’application Spring Boot. custom-elts1

Génération du projet Maven avec Spring Boot

Dans la capture d’écran ci-dessus, nous indiquons simplement :
« Je veux que tu génères la structure d’un projet Maven avec Java et Spring Boot ».

Choix des dépendances

Dans la section Dependencies, tu peux sélectionner les packages dont tu as besoin.
Dans notre cas, la seule dépendance nécessaire pour l’instant est Web.

En choisissant cette dépendance, nous ajoutons le package spring-boot-starter-web, qui est le starter permettant de créer des applications web (y compris RESTful) avec Spring MVC.

Ce starter inclut également Tomcat, via le package spring-boot-starter-tomcat, qui est utilisé comme conteneur de servlets par défaut.


Génération du projet

En cliquant sur Generate Project, un fichier .zip sera téléchargé, contenant notre application prête à être importée.


Importation et configuration

Après avoir importé le projet dans ton IDE :

  • Ouvre le fichier application.properties
  • Modifie le port par défaut de l’application web afin d’éviter les conflits avec une autre application qui utiliserait le même port.
server.port line= 8181
Pour l’instant, après avoir démarré l’application,
l’URL suivante renvoie une erreur : [localhost](http://localhost: 8181/) custom-elt2

La vue n’est pas encore définie

C’est normal que l’URL affiche une erreur car aucune vue n’a encore été définie.

Ajouter un endpoint web

Nous avons créé notre application Spring Boot en spécifiant que nous voulions un packaging WAR.

Pour pouvoir déployer ce WAR plus tard et afficher le contenu web, vous devez créer un dossier webapp dans src/main.

Spring MVC utilise le Tomcat intégré pour exécuter les applications.
Lors du déploiement, Tomcat recherche le fichier index.html dans src/main/webapp.

Étape 1 – Créer index.html

Créez un fichier index.html dans src/main/webapp avec un message simple :

<!DOCTYPE html>
<html lang="fr">
  <head>
    <meta charset="UTF-8" />
    <title>Accueil</title>
  </head>
  <body>
    <h1>Bienvenue sur le site !</h1>
  </body>
</html>

Après avoir redémarré l’application, l’URL suivante affichera le message :

http://localhost:8181/

Créer l’application Angular

Nous allons utiliser Angular CLI pour créer notre application Angular. Angular CLI est une interface en ligne de commande qui permet de générer un projet structuré et d’initialiser les dépendances ainsi que les interconnexions entre les différents modules.

Pré-requis

Pour utiliser Angular CLI, vous devez installer Node.js. npm est le gestionnaire de packages inclus dans Node.js.

Exemple de versions installées :

  • Node.js v11.8.0

  • npm 6.5.0

  • Angular CLI 7.3.1

Étape 2 – Créer le projet Angular

Pour éviter de créer un nouveau dossier, vous pouvez utiliser le répertoire webapp où se trouve déjà le fichier index.html.

Ouvrez un terminal, placez-vous dans le dossier src/main et exécutez la commande :

ng new webapp

Ne pas ajouter le routing ni le CSS pour l’instant. Vous pourrez le faire plus tard si vous souhaitez aller plus loin avec l’application.

Hiérarchie du projet

Après cette étape, vous devriez avoir la hiérarchie suivante pour le projet Angular : custom-elt3

Réorganisation des dossiers et fichiers

Pour intégrer complètement Angular dans votre projet Spring Boot,
il est préférable de réorganiser les dossiers et fichiers afin de séparer configuration et vues.


Étapes à suivre

  1. Supprimer le dossier node_modules.
    Il sera régénéré plus tard à la racine du projet lorsque nous déplacerons package.json.

  2. Déplacer les fichiers de configuration au même niveau que pom.xml.
    L’objectif est de conserver la configuration Angular et Maven côte à côte pour un accès plus simple.


Fichiers à déplacer depuis webapp :

  • angular.json
  • package-lock.json
  • package.json
  • tsconfig.json
  • tslint.json
  • README.md

Fichiers à déplacer depuis webapp/src :

  • karma.conf.js
  • tsconfig.app.json
  • tsconfig.spec.json
  • e2e/protractor.conf.js
  • e2e/tsconfig.e2e.json

⚠️ Vous remarquerez qu’il y a deux fichiers tslint.json.
Celui dans webapp/src ajoute des règles au fichier principal déjà transféré.
👉 Ajoutez ces règles dans le fichier à la racine, puis supprimez celui de src.

Le fichier README.md contient des commandes exemples pour utiliser Angular CLI.


  1. Déplacer ensuite le contenu de webapp/src directement dans webapp, puis supprimer le dossier src devenu vide.
    Cela permet d’éviter la confusion entre les multiples dossiers src du projet.

  2. Remplacer le fichier index.html créé précédemment par celui généré automatiquement par Angular.


Hiérarchie finale du projet

Après ces ajustements, la hiérarchie du projet devrait ressembler à ceci :

custom-elt4

Mise à jour des fichiers de configuration

Maintenant, il faut modifier chaque fichier de configuration afin que les chemins pointent vers les bons répertoires.
👉 Vous pouvez trouver les fichiers corrigés sur GitHub.


Installation des dépendances

Depuis la racine du projet, exécutez la commande suivante pour régénérer le dossier node_modules :

npm install

Lancer l’application Angular

Pour tester l’application Angular, exécutez la commande :

ng serve

L’application sera alors accessible à l’adresse suivante http://localhost:4200/

custom-elt5

Gérer Angular dans votre application Spring

Dans le fichier pom.xml, il est nécessaire de définir :

  • les versions de Node et npm,
  • ainsi que les commandes qui seront utilisées pour exécuter Angular.

Pour cela, nous allons utiliser le plugin frontend-maven-plugin.
Ce plugin permet d’installer Node, npm et de gérer des outils comme Grunt, Karma, Gulp, etc.


Ajouter la configuration suivante dans votre pom.xml :

<plugins>
	<plugin>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-maven-plugin</artifactId>
	</plugin>
	<plugin>
		<groupId>com.github.eirslett</groupId>
		<artifactId>frontend-maven-plugin</artifactId>
		<version>1.7.5</version>
		<configuration>
			<nodeVersion>v11.8.0</nodeVersion>
			<npmVersion>6.5.0</npmVersion>
		</configuration>
		<executions>
			<execution>
				<id>install node and npm</id>
				<goals>
    			<goal>install-node-and-npm</goal>
				</goals>
			</execution>
			<execution>
				<id>npm install</id>
				<goals>
		  		<goal>npm</goal>
				</goals>
			</execution>
			<execution>
				<id>npm run build</id>
				<goals>
					<goal>npm</goal>
				</goals>
				<phase>generate-resources</phase>
				<configuration>
			  	<arguments>run build:prod</arguments>
				</configuration>
			</execution>
		</executions>
	</plugin>
</plugins>

Intégration du build Angular avec Maven

Avec ce plugin, nous indiquons à Maven que chaque fois que la commande :

mvn install

est exécutée :

  • Node et npm doivent être installés localement,

  • puis le projet Angular doit être construit.

👉 Cela génère automatiquement le dossier node_modules à la racine du projet.

Définir le script de build Angular

Dans le fichier package.json, ajoutez la ligne suivante dans la section scripts :

"build:prod": "ng build --prod --build-optimizer"

Ce script permet d’optimiser le build en production.

Si vous souhaitez exécuter d’autres commandes Angular avec Maven, vous pouvez simplement les ajouter dans ce même bloc.

custom-elt6

Génération du WAR

Avec le build, un fichier WAR est généré contenant à la fois :

  • la partie Java (Spring Boot),
  • et le build Angular.

Votre application Spring Boot reste disponible à l’adresse :

Intégrer les composants Angular dans votre application Spring Boot

Lorsqu’un projet Angular est compilé, les composants sont générés dans des fichiers JavaScript (js) et mis en forme avec des fichiers CSS.

👉 Pour les utiliser dans votre application, il faudrait normalement intégrer un grand nombre de fichiers séparés.

Pour simplifier ce processus, nous avons besoin d’un outil capable de fusionner et minifier ces fichiers avant de les inclure dans l’application.
C’est exactement le rôle de Gulp !


Qu’est-ce que Gulp ?

Gulp est un outil d’automatisation simple à comprendre et à utiliser.
En plus de fusionner et minifier les fichiers, il peut également :

  • compresser des images,
  • compiler des fichiers LESS,
  • et bien plus encore.

👉 Si vous souhaitez en savoir plus, vous pouvez visiter le site officiel de Gulp.


Utiliser Gulp avec Spring Tool Suite

Spring Tool Suite embarque déjà Gulp par défaut, ce qui permet de l’exécuter facilement.
Cependant, il est nécessaire d’installer Gulp ainsi que certains packages supplémentaires pour :

  • concaténer les fichiers,
  • renommer les fichiers.

Installation des packages Gulp

Exécutez les commandes suivantes depuis la racine du projet :

npm install --save-dev gulp
npm install --save-dev gulp-concat
npm install --save-dev gulp-rename

Création du fichier de configuration Gulp

Maintenant, créons un fichier de configuration nommé gulpfile.js
à la racine du projet.

const gulp = require('gulp')
const concat = require('gulp-concat')
const rename = require('gulp-rename')

gulp.task('prepare-js', function () {
  return gulp
    .src(['target/webapp/*.js'])
    .pipe(concat('angular.js'))
    .pipe(gulp.dest('src/main/webapp/dist'))
})

gulp.task('prepare-css', function () {
  return gulp
    .src(['target/webapp/styles.*.css'])
    .pipe(rename('styles.css'))
    .pipe(gulp.dest('src/main/webapp/dist'))
})
gulp.task('build', gulp.parallel('prepare-js', 'prepare-css'))

Intégrer Gulp dans Maven

Comme vous le voyez, un fichier Gulp est simple à lire et à comprendre.
En résumé, nous y déclarons les modules que nous voulons utiliser avec Gulp, nous définissons des tâches et nous les exécutons en mode parallèle.

Ensuite, ajoutez la commande gulp build dans Maven, afin que la prochaine fois que nous exécuterons l’application, les fichiers angular.js et style.css soient générés automatiquement.

Rappelez-vous du plugin frontend-maven-plugin que nous avons ajouté précédemment :
il gère également l’exécution de Gulp.

Ajoutez donc l’exécution suivante dans le fichier pom.xml.

<execution>
  <id>gulp build</id>
  <goals>
    <goal>gulp</goal>
  </goals>
  <phase>generate-resources</phase>
  <configuration>
    <arguments>build</arguments>
  </configuration>
</execution>

Si vous exécutez la commande maven:clean install, vos fichiers doivent être générés dans
src/main/webapp/dist.

Maintenant que la partie configuration est en place, nous pouvons nous concentrer sur Angular Elements.

Créer le custom element

Les Angular Elements sont des composants Angular empaquetés en tant que custom elements, natifs des navigateurs web tels que Chrome, Firefox, Opera, Safari, etc.
La liste complète des navigateurs supportés est disponible ici.

Ainsi, les Angular Elements peuvent être utilisés comme des éléments HTML dans n’importe quel framework ou application web.

Ce qui rend cette approche encore plus intéressante, c’est qu’Angular gère :

  • les attributs,
  • les bindings,
  • les hooks du cycle de vie des Angular Elements.

Commençons par inclure le package @angular/elements pour pouvoir créer des custom elements.

npm install @angular/elements --save

Compatibilité des navigateurs

Les Custom Elements ne sont pas toujours supportés par les navigateurs anciens.
Pour éviter les erreurs, nous utilisons des polyfills afin d’assurer la compatibilité descendante.

ng add @angular/elements --name=webapp

Structure du projet

Le dossier webapp fait référence à src/main/webapp.
C’est là que votre application Angular est stockée.


Création des Custom Elements

Nous sommes maintenant prêts à créer nos custom elements.

Commençons par créer un module widget qui contiendra tous les widgets que nous utiliserons dans notre application web.

Rendez-vous dans le dossier src/main/webapp/app et tapez la commande suivante :

ng g module widget

Création d’un composant Rating

Maintenant, créons un composant rating à l’intérieur du module widget :

cd widget
ng g component rating

Modifier widget.module.ts pour transformer le composant Rating en Custom Element

Éditez le fichier widget.module.ts afin de rendre le composant Rating utilisable comme custom element :

import { NgModule, Injector } from '@angular/core'
import { CommonModule } from '@angular/common'
import { RatingComponent } from './rating/rating.component'
import { createCustomElement } from '@angular/elements'

@NgModule({
  declarations: [RatingComponent],
  imports: [CommonModule],
  entryComponents: [RatingComponent],
})
export class WidgetModule {
  constructor(private injector: Injector) {
    const ratingElement = createCustomElement(RatingComponent, { injector: this.injector })
    customElements.define('app-rating', ratingElement)
  }
}

Ajouter le module Widget dans AppModule

Ajoutez le WidgetModule dans le tableau imports de AppModule.


Bootstrap des Angular Elements

Les Angular Elements se bootstrapent automatiquement, c’est-à-dire qu’ils démarrent lorsqu’ils sont ajoutés au DOM et se détruisent lorsqu’ils sont retirés.
Nous n’avons donc pas besoin de la méthode bootstrap par défaut.

  • Supprimez le tableau bootstrap dans app.module.ts.
  • Nous allons bootstraper manuellement nos éléments.

L’interface DoBootstrap est exactement ce dont nous avons besoin, car elle permet d’ajouter la méthode ngDoBootstrap.

Notre fichier final app.module.ts devrait ressembler à ceci :

import { BrowserModule } from '@angular/platform-browser'
import { NgModule, DoBootstrap } from '@angular/core'

import { WidgetModule } from './widget/widget.module'

@NgModule({
  imports: [BrowserModule, WidgetModule],
})
export class AppModule implements DoBootstrap {
  ngDoBootstrap() {}
}

Nettoyage de AppComponent

Comme nous n’utilisons plus AppComponent, nous pouvons :

  • supprimer tous les fichiers app.component.*
  • et retirer le tableau declarations correspondant dans app.module.ts.

Composant Rating

Le composant Rating sera une boîte de notation basique dans notre application web.

  • Il prendra en entrée un nombre entre 1 et 5
  • et colorera autant d’étoiles correspondant à cette valeur.
custom-elt7
  • rating.component.ts
import { Component, Input } from '@angular/core'

@Component({
  selector: 'app-rating',
  templateUrl: './rating.component.html',
  styleUrls: ['./rating.component.css'],
})
export class RatingComponent {
  @Input() rate: number
}
  • rating.component.html
<fieldset class="rating-rate">
  <input type="radio" value="5" [checked]="rate==5" />
  <label title="Rocks!">5 stars</label>

  <input type="radio" value="4" [checked]="rate==4" />
  <label title="Pretty good">4 stars</label>

  <input type="radio" value="3" [checked]="rate==3" />
  <label title="Not bad">3 stars</label>

  <input type="radio" value="2" [checked]="rate==2" />
  <label title="Kinda bad">2 stars</label>

  <input type="radio" value="1" [checked]="rate==1" />
  <label title="Sucks big time">1 star</label>
</fieldset>
  • rating.component.css
/***************************
Pulls the stars container to the left
***************************/
.rating-rate {
  float: left;
  border: none;
}
/***************************
Hides the radio buttons
***************************/
.rating-rate:not(:checked) > input {
  position: absolute;
  top: -9999px;
  clip: rect(0, 0, 0, 0);
}
/***************************
Default stars styles
***************************/
.rating-rate:not(:checked) > label {
  float: right;
  width: 1em;
  padding: 0.1em;
  overflow: hidden;
  white-space: nowrap;
  font-size: 100%;
  line-height: 1.2;
  color: #ddd;
}
/***************************
Adds the star symbol to the labels
***************************/
.rating-rate:not(:checked) > label:before {
  content: '★ ';
}
/***************************
Colour for the applied rating stars
***************************/
.rating-rate > input:checked ~ label {
  color: #f70;
}

Utiliser le custom element dans Spring Boot

Pour utiliser le custom element, créez un fichier index.html et placez-le dans : src/main/resources/static

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Jangular</title>
    <link rel="stylesheet" type="text/css" href="http://localhost:8181/dist/styles.css" />
  </head>
  <body>
    <p>Welcome to jangular !</p>
    <span style="float:left;padding-top:8px;">The application is rated </span>
    <app-rating rate="5"></app-rating>

    <script src="http://localhost:8181/dist/angular.js"></script>
  </body>
</html>

Lancer l’application

Exécutez la commande suivante pour construire le projet et générer les fichiers Angular :

mvn clean install

Puis lancez votre application Spring Boot.

Si vous ouvrez l’URL suivante dans votre navigateur :http://localhost:8181/ vous obtiendrez l’erreur suivante dans l’inspecteur du navigateu

custom-elt8

Utilisation de Zone.js

Zone.js est utilisé par Angular pour gérer l’exécution des tâches, les stack traces, etc.
Vous pouvez trouver plus d’informations sur zone.js ici.

  • Dans une application Angular, zone.js est inclus automatiquement.
  • Dans une application non-Angular, vous devez l’ajouter manuellement.

Importer Zone.js

Ajoutez la ligne suivante dans main.ts pour importer zone.js :

import 'zone.js'

Relancer le build et démarrer l’application

Exécutez à nouveau la commande :

mvn clean install

Remarque

Vous pourriez rencontrer l’erreur suivante en vous connectant à : http://localhost:8181/

Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.

Cela signifie que votre navigateur ne supporte pas ES5.
Éditez le fichier tsconfig.json et définissez la cible sur ES2015 au lieu de ES5.


Voilà ! ✅

Vous disposez maintenant d’une application Spring Boot utilisant un Angular Element.