L’ANFR a mis en place une charte des bonnes pratiques engageant les professionnels de la réception TV. Afin de faciliter la signature de cette charte, elle a décidé de proposer aux antennistes un formulaire web de saisie permettant de recenser leurs informations et faciliter la génération du document d’engagement.
Dans ce billet, nous présentons la démarche, le rendu obtenu puis un aperçu de la plateforme technique de projet.
Point de vue utilisateur
Les utilisateurs du formulaire sont des antennistes. Pour favoriser l’adhésion de ces derniers à la charte, nous avons mis l’accent sur trois éléments clés :
- l’utilisation d’une charte graphique connue
- un site en responsive design
- un guidage précis sur la procédure à suivre
Charte graphique connue
Nous avons repris la charte graphique que les antennistes connaissent bien puisqu’elle a été mise en place sur le site http://www.recevoirlatnt.fr. Ci-dessous, le formulaire “MES TRAVAUX” de recevoir la TNT :
Le formulaire à remplir est présenté en point d’entrée. Le choix du framework présenté après a été déterminant pour obtenir un rendu en cohérence avec l’existant :
Responsive design
Le site est responsive design. Par exemple lorsque la largeur n’est pas suffisante pour une présentation sur deux colonnes, une présentation verticale est préférée :
Guidage de l’utilisateur
L’antenniste est guidé lors de la saisie.
Par exemple, les données saisies sont contraintes (caractère obligatoire, SIRET sur 14 caractères, numéro de téléphone bien formé, etc.) :
Les communes sélectionnables sont pré-chargées à partir du code postal. Pour cela, nous utilisons un web service de GeoAPI pour récupérer les informations cohérentes (code postal ➙ liste de communes) :
Lorsque le code postal est associé à une seule commune, cette dernière est automatiquement sélectionnée :
Le composant suivant impose la saisie d’au moins 5 éléments et indique l’effort restant à fournir :
Le composant présenté ci-dessous permet de renseigner les émetteurs couvrant la zone d’intervention de l’antenniste. L’utilisateur peut à la fois saisir librement les noms des émetteurs et sélectionner les émetteurs proposés au fur et à mesure de la frappe. Lorsque l’utilisateur a sélectionné un émetteur du référentiel, le lien est conservé pour un post-traitement du dossier :
Avant de passer à la suite, l’antenniste vérifie les informations qu’il a saisies, accepte la charte (qu’il a lue, bien sûr) et valide :
Pour finir, un document est généré au format pdf. L’utilisateur l’imprime et le signe avant de le retourner à l’ANFR :
Exemple de fichier pdf généré :
Point de vue technique
Serveur Java
On retrouve un serveur de données PostgreSQL et une API de services HTTP basée sur une stack Jooq, Java, JaxRS / RESTEasy, Jetty. Pour plus de détails, je vous invite à consulter l’article Mise en place d’une API HTTP REST qui présente une solution proche.
Les documents sont produits à partir d’un modèle odt que l’ANFR peut modifier avec un outil bureautique. XDocReport est utilisé avec le langage de balises Freemarker.
Client Angular
Le client est réalisé en Angular version 4 avec typescript.
Angular est un framework de présentation (côté navigateur) qui permet de construire des applications complexes. Parmi les concepts d’Angular, on retrouve notamment les composants, les Property bindings et l’injection de dépendances.
Typescript est un sur-ensemble de javascript ES6 avec un typage des variables, des fonctions, des classes et interfaces, une gestion de modules, etc. Au final, les scripts typescript sont transpilés en javascript de manière à ce qu’ils puissent être interprétés directement par un navigateur web.
Les exemples simplifiés de code présentés ci-dessous donnent un aperçu de l’esprit de la gestion des composants dans Angular.
L’unique page du projet :
~/projets/anfr/antenniste-cbp/repo/client/src/index.html :
1 2 3 4 5 6 7 8 |
<!doctype html> <html> <head><!-- balise title, feuilles de style, etc.--></head> <body> <app-root>Chargement...</app-root> </body> </html> |
Module app
~/projets/anfr/antenniste-cbp/repo/client/src/app/app.module.ts :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// … Gestion des dépendances. Exemple d’utilisation d’un autre module, d’un service et d’un composant import { Ng2CompleterModule } from 'ng2-completer'; import { EmetteurService } from './service/emetteur.service'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent, //... ], imports: [ Ng2CompleterModule, //... ], providers: [ LinkService, //... ], bootstrap: [ AppComponent ] }) export class AppModule { } |
Composant app
~/projets/anfr/antenniste-cbp/repo/client/src/app/app.component.ts :
1 2 3 4 5 6 7 8 9 10 |
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { } |
Le composant viendra s’inscrire dans app-root (cf index.html) et utilisera le modèle de présentation app.component.html avec la feuille de style app.component.css.
~/projets/anfr/antenniste-cbp/repo/client/src/app/app.component.html :
1 2 3 4 |
<header id="header"></header> <view id="view"></view> <footer id="footer"></footer> |
On reproduit le même fonctionnement que pour l’index avec header, view et footer.
Composant header (entête)
~/projets/anfr/antenniste-cbp/repo/client/src/app/header/header.component.ts :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import { Component, OnInit } from '@angular/core'; import { LinkService } from '../service/link.service'; @Component({ selector: 'header', templateUrl: './header.component.html', styleUrls: ['./header.component.css'] }) export class HeaderComponent implements OnInit { ministereLink: string; ministereName: string; constructor(private linkService: LinkService) { this.linkService = linkService; } ngOnInit() { this.linkService.getLinks().then((l) => { this.ministereLink = l.ministere; this.ministereName = l.ministereNom; }); } } |
Ici, le composant utilise le service LinkService pour charger des données. Le service est injecté via le constructeur. Les données sont chargées de manière asynchrone et lorsqu’elles sont prêtes (then), nous renseignons les variables ministere*.
~/projets/anfr/antenniste-cbp/repo/client/src/app/header/header.component.html
1 2 3 4 5 6 |
<div id="content-header" class="row"> <div id="logos-partenaires" class="col w50 txtright"> <a href="{{this.ministereLink}}" target="_blank"><img src="assets/images/logo-marianne.png" alt="{{this.ministereName}}" class="partenaires mrs"> </a> </div> </div> |
Les variables ministere* sont utilisées directement dans le modèle (“réactivité”).
Service de données links
~/projets/anfr/antenniste-cbp/repo/client/src/app/service/link.service.ts :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import { Injectable } from '@angular/core'; import { Headers, Http, URLSearchParams } from '@angular/http'; import 'rxjs/add/operator/toPromise'; @Injectable() export class LinkService { private linkUrl = '/api/links'; constructor(private http: Http) { } getLinks(): Promise<any> { return this.http.get(this.linkUrl) .toPromise() .then(response => response.json().data as any) .catch(this.handleError); } private handleError(error: any): Promise<any> { console.error('An error occurred', error); return Promise.reject(error.message || error); } } |
Le service de données LinkService charge les données à partir d’un appel HTTP GET au web service /api/links.
Environnement de développement
Dans la phase de développement, on démarre les serveurs et le client simplement :
Démarrage du serveur de données :
1 2 3 4 5 6 7 8 |
docker-compose -f ~/projets/anfr/antenniste-cbp/repo/docker/docker-compose.yml up Starting acbp_postgres Attaching to acbp_postgres acbp_postgres | LOG: database system was shut down at 2017-07-17 15:46:08 UTC acbp_postgres | LOG: MultiXact member wraparound protections are now enabled acbp_postgres | LOG: database system is ready to accept connections acbp_postgres | LOG: autovacuum launcher started |
Démarrage du serveur web :
1 2 3 4 5 6 7 |
mvn clean test -Prunserver -f ~/projets/anfr/antenniste-cbp/repo/pom.xml [INFO] Scanning for projects... [INFO] Reactor Build Order … 15:46:24.689 [fr.anfr.acbp.WebApp.main()] INFO org.eclipse.jetty.server.ServerConnector - Started ServerConnector@2d5486d6{HTTP/1.1,[http/1.1]}{0.0.0.0:8881} 15:46:24.690 [fr.anfr.acbp.WebApp.main()] INFO org.eclipse.jetty.server.Server - Started @5101ms |
Démarrage du client web :
1 2 3 4 5 6 7 |
cd ~/projets/anfr/antenniste-cbp/repo/client && yarn install && yarn start ... $ ng serve --proxy proxy.conf.json --host 0.0.0.0 ** NG Live Development Server is listening on 0.0.0.0:4200, open your browser on http://localhost:4200/ ** … webpack: Compiled successfully. |
Test sur l’URL http://localhost:4200/ :
Livraison / installation, mise à jour
De manière à simplifier la gestion par l’administrateur système/réseau, nous livrons un paquet système qui comporte à la fois le serveur et le client minifié / packagé. Ainsi, il suffit d’une instruction pour installer ou mettre à jour le serveur.
Un script se charge du packaging de l’ensemble :
1 |
~/projets/anfr/antenniste-cbp/repo/dist/packaging.sh |
Installation du paquet système :
1 |
rpm -i acbp-1.0.noarch.rpm |
Mise à jour du paquet système :
1 |
rpm -Uvh acbp-1.1.noarch.rpm |
Le site est accessible ici : https://charte-antennistes.anfr.fr.
Laisser un commentaire