Chaque navigateur possède son propre moteur Javascript :
Ceux-ci vérifient les normes ECMAScript (ES7 = ES2016) et la plupart sont compatibles avec WebAssembly.
Leur principe de fonctionnement est décrit dans l’image ci-dessous. Le navigateur transmet le code Javascript au moteur, qui le compile afin d’obtenir du code machine qui est exécuté sur le processeur de votre machine.
Depuis 2009, on peut réaliser ces opérations hors d’un navigateur grâce à Node.
Afin de demander au navigateur d’exécuter du code Javascript, il suffit d’insérer dans le code HTML des balises <script></script>.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>intro à javascript</title>
</head>
<body>
<h1>contenu de la page web</h1>
<!-- insertion d'un code javascript -->
<script>
console.log('toto');
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>intro à javascript</title>
</head>
<body>
<h1>contenu de la page web</h1>
<!-- insertion d'un code javascript -->
<script src="page1.js"></script>
</body>
</html>
Pour installer Node, si vous travaillez sous Windows ou MacOS, allez sur l’URL suivante :
https://nodejs.org/en/download
Si vous travaillez sous Linux, vous pouvez également suivre les instruction de l’URL ci-dessus ou bien installer nodejs via apt, yum, pacman, snap ou n’importe quel autre système de package. Mais attention : il faut une version récente de Node (au moins une version 22). Ce sera nécessaire par la suite pour pouvoir utiliser Angular.
Une fois Node installé, vous devez pouvoir utiliser la commande npm. Dans une console, tapez la commande npm list ou npm.cmd list si vous êtes sous Windows. Cela devrait fonctionner (vous ne devriez pas voir de message d’erreur, juste au pire quelques warnings).
La figure ci-dessous vous présente sur l’échelle du « beurk » les différentes manières de déclarer des variables en Javascript. Important : lisez bien ce qui suit pour déclarer correctement vos variables.
Lorsque vous utilisez la syntaxe Python nom_variable = valeur, vous créez des variables globales, ce qui est franchement à proscrire dans tout programme qui se respecte. À la rigueur, si vous souhaitez réellement créer une variable globale, utilisez global.nom_variable ou window.nom_variable, ce sera au moins explicite. Utiliser le mot-clef var n’est pas mieux car la portée des variables définies ainsi est une portée de fonction, pas de bloc, ce qui est franchement générateur de bug. Je ne veux donc pas voir cela dans vos programmes.
Règle
Les deux manières correctes pour créer des variables sont d’utiliser les mots-clefs const et let, avec une préférence pour const. Ces deux mots-clefs vous permettront de créer des variables locales à l’intérieur des blocs ou, si elles sont déclarés en dehors de blocs, elles auront une portée limitée au fichier dans lequel elles sont déclarées.
Le mot-clef const vous interdira de réaffecter une nouvelle valeur à la variable. Si vous avez absolument besoin de faire des réaffectations, utilisez let.
Les noms de variables autorisés suivent les mêmes règles qu’en Java ou C. En principe, on utilise la notation Camel (par exemple, firstName). Attention : Javascript est sensible à la casse (firstName est différent de FirstName). Comme d’habitude, éviter de créer des variables O (O majuscule) et l (L minuscule) parce que cela ressemble trop aux chiffres 0 et 1.
Javascript est une version « light » de Java. À ce titre, le langage reprend les caractéristiques des types de Java. Notamment, on retrouve les types dits primitifs (string, number, boolean, undefined, null), qui sont copiés par valeur, et les types référence (Object, Array, Function) qui sont copiés par référence.
Il existe deux manières pour déclarer des fonctions. La première consiste à utiliser le mot-clef function :
// les fonctions : function nom_fonc (arguments) { code }, comme en php
function f(arg1, arg2) {
arg1++;
console.log(arg1, arg2);
}
// passage de paramètres :
// par référence pour les types référence, copie par valeur pour les types primitifs
let x = 3;
f(x,x); console.log(x);
f(x); console.log(x);
Les fonctions sont des valeurs comme les autres et peuvent donc être affectées à des variables. Dans ce cas, on utilise souvent une autre manière de les créer, que l’on appelle arrow functions. L’idée consiste à ne pas écrire le mot-clef function ni le nom de la fonction et, plutôt de rajouter un flèche => avant l’accolade ouvrante :
const f = (arg1, arg2) => {
arg1++;
console.log(arg1, arg2);
}
let x = 3;
f(x,x); console.log(x);
f(x); console.log(x);
Cette notation vous sera particulièrement utile pour déclarer des callbacks dans Angular.
Notez que vous pouvez tester les codes ci-dessus en utilisant Node, qui permet d’exécuter sur un desktop du code Javascript :
node function2.js
4 3
3
4 undefined
3
Le code suivant vous montre comment créer des objets en Javascript :
// objets = {}
// création d'un objet :
let guy = {
prenom : 'guy', // couples clef-valeur
nom : 'nipigue' // séparation des propriétés
// par des ','
};
console.log(guy);
// accès aux propriétés : 2 possibilités :
// notation '.' ou '[]'
console.log(guy.nom);
console.log(guy['prenom']);
let field = 'prenom';
console.log(guy[field]);
Après la création d’un objet, on peut rajouter/supprimer dynamiquement des champs/propriétés.
Notez qu’une propriété peut être une fonction. On dira alors que c’est une méthode :
// création d'un objet avec une méthode :
let obj = {
propriete1 : 3,
propriete2 : 5,
// définition d'une méthode avant ES6
display1 : function () {
console.log(this.propriete1);
},
// définition possible (et préférable) depuis ES6
display2 () {
console.log(this.propriete2);
}
};
obj.display1();
obj.display2();
Notez que, quand on veut accéder à la valeur d’une propriété à l’intérieur d’une méthode, on utilise le mot-clef this, comme en Java.
Depuis ES6, on peut également créer des objets en utilisant les mots-clefs class et new, mais cela vous sera moins utile que ce que l’on a vu précédemment (à ceci près qu’Angular créera ses composants en exploitant le mot-clef class) :
// création d'un objet par classe : depuis ES6
class MonObjet {
// on définit explicitement une méthode constructor.
// on ne peut définir qu'un seul constructeur
constructor (val1, val2) {
// déclaration, initialisation des propriétés
this.propriete1 = val1;
this.propriete2 = val2;
}
// pas besoin de mot clef "function" pour les
// méthodes
display () {
console.log(this.propriete1, this.propriete2);
}
}
let obj = new MonObjet(3,4);
obj.display ();
Attention : les objets sont des types référence, comme le montre le code suivant :
// copie de types primitifs : x != y
let x = 4;
let y = x;
y++;
console.log (x,y);
// copie de types référence : x = y
let a = { val : 4 };
let b = a;
b.val = 5;
console.log (a,b);
4 5
{ val: 5 } { val: 5 }
Les tableaux se déclarent comme en Python ou en PHP (versions > 5.4) :
// array = []
// les éléments sont indexés à partir de 0, comme en C, accès avec l'opérateur []
const x = [1, 3, 4];
console.log (x);
x[4] = 5; // avec const, x = ... est interdit, mais pas x[...] = ...
console.log(x); // C'est en fait la référence qui est constante, pas ce qui
// est référencé
// on peut mettre des éléments de types différents dans un même array :
x[5] = 'toto';
console.log(x);
[ 1, 3, 4 ]
[ 1, 3, 4, <1 empty item>, 5 ]
[ 1, 3, 4, <1 empty item>, 5, 'toto' ]
On peut voir ici qu’il peut y avoir des « trous » dans les tableaux. Comme en Python ou en PHP, un tableau peut contenir des éléments de différents types.
À noter que les tableaux sont des objets, ce qui vous permettra d’utiliser leurs méthodes et propriétés comme push, pop, unshift, length, filter, forEach, etc. :
Comme en Python, on peut créer des strings en les encadrant à l’intérieur de quotes ('toto') ou de guillemets ("toto"). Mais il existe une autre manière de créer des chaînes de caractères en Javascript, que l’on appelle des string literals. Ceux-ci sont encadrés par des back quotes (`toto`). L’intérêt de ces string literals, c’est :
que l’on peut les définir sur plusieurs lignes ;
que l’on peut y insérer (entre accolades) des variables ou expressions Javascript, qui sont alors évaluées et dont le résultat fait partie de la chaîne.
// concaténation de strings : +
let x = 4;
let y = 'x vaut : ' + x + '\n\'3 fois\' moins que ' + (3 * x);
console.log(y);
// template literals (backquotes : ``) : depuis ES6
let z = `x vaut : ${x}
'3 fois' moins que ${3 * x}`; // fin du template literal
console.log(z);
x vaut : 4
'3 fois' moins que 12
x vaut : 4
'3 fois' moins que 12
Notez qu’à l’intérieur d’un template literal, tout retour à la ligne fait partie de la chaîne de caractère.
Javascript propose des itérateurs pour parcourir les éléments d’un tableau et les propriétés des objets.
Règle
Pour itérer sur un tableau, utilisez le for (let/const x of …).
Pour itérer sur un objet, utilisez le for (let/const cle in …).
La boucle for … of vous sera particulièrement utile en Angular. Mais je spoile, je spoile…
Voici deux exemples d’illustration :
const tab = [5, 4, 3];
// parcours avec un itérateur
for (const elt of tab) {
console.log(elt);
}
// parcours avec une boucle for à la C
for (let i = 0; i < tab.length; i++) {
console.log(tab[i]);
}
class MonObjet {
constructor (val1, val2) {
this.propriete1 = val1;
this.propriete2 = val2;
}
// affichage des propriétés de l'objet
display () {
// for .. in : parcours des clefs de
// l'objet ou des index pour un tableau
for (let key in this)
console.log(key, this[key]);
}
}
let obj = new MonObjet(3,4);
obj.display ();
console.log('==========================');
const obj2 = {
prop1: 5,
prop2: 6,
prop3: "toto"
};
for (const key in obj2) {
console.log(key, obj2[key]);
}
propriete1 3
propriete2 4
==========================
prop1 5
prop2 6
prop3 toto
Angular est un framework pour construire des applications clientes, autrement dit pour réaliser des front-ends. Il est conçu pour en simplifier la programmation, la maintenance et le débuggage. Pour cela, il va vous imposer de structurer correctement votre application. Notamment, il sépare ce qui concerne la gestion des données (business logic) des affichages. En outre, mais nous ne le verrons pas par manque de temps, il permet une mise en place de tests assez simple.
Angular s’appuie sur :
TypeScript, qui est une extension typée de Javascript, pour la gestion des données;
HTML pour les affichages;
CSS/SCSS/Sass pour le style de l’application.
Une page web est souvent structurée. Par exemple, l’image ci-dessous représente une page constituée d’une barre de navigation, d’un menu et d’une partie dévolue au contenu que l’on souhaite afficher dans cette page. L’idée clef d’Angular est que chaque partie (chaque rectangle) correspond à un composant spécifique. La partie bleue montre que, lorsqu’un composant (le composant principal bleu) est complexe ou lorsqu’il constitué d’une répétition de parties similaires, on peut le décomposer en petits morceaux (des sous-composants) et inclure ceux-ci dans le composant principal en bleu. Outre le fait de simplifier la programmation et le débuggage, cette stratégie de scinder les composants offre l’avantage qu’un même composant peut être réutilisé plusieurs fois.
La structure ci-dessus correspond à l’arbre de composants ci-dessous. Celui-ci est précisément ce qui est rajouté au DOM par Angular pour afficher votre page.
Un composant Angular est, en gros, constitué de 3 fichiers :
un fichier TypeScript contenant une classe avec :
les données du composant,
la logique/le comportement du composant (des méthodes).
un fichier HTML :
contenant le code HTML affiché par le navigateur,
des instructions pour interagir avec le code TypeScript.
un fichier CSS/SCSS/Sass contenant le style propre au composant.
Dans les projets Angular, le répertoire src/app
contient les composants.
Il est à noter qu’Angular crée un composant principal, la racine de l’arborescence de composants, appelé app ou root.
Pour installer Angular. Pour cela, tapez la commande ci-dessous :
sudo npm install -g @angular/cli
Pour créer un nouveau projet Angular, il faut taper la commande ci-dessous et indiquer les caractéristiques de votre projet. Je vous suggère de choisir SCSS pour vos fichiers de style (c’est une extension plus pratique à utiliser que CSS). De plus, dans ce module, on n’utilisera pas de Server-Side Rendering.
ng new nom_de_mon_projet
Cette commande va créer un nouveau répertoire nom_de_mon_projet
et le remplir avec
un squelette d’application et elle va également télécharger des librairies via npm.
Le contenu du répertoire devrait être similaire à celui ci-dessous :
Le répertoire src/app
contient tous les composants. À l’initialisation, il
n’en contient qu’un seul, le composant racine app. Chaque composant est spécifié
par un répertoire du même nom contenant :
le fichier TypeScript du composant (sa classe) .component.ts
,
le fichier HTML .component.html
,
le fichier de style du composant, ici .component.scss
,
un fichier .component.spec.ts
qui permet de réaliser des tests automatiques
du composant.
Le composant app est spécial en ce sens qu’il est racine de votre application et, à ce titre, il contient deux fichiers supplémentaires :
le fichier app.config.ts
, qui contient des instructions de configuration
de l’application. Notamment, la section providers charge des librairies
nécessaires à l’application :
app.config.tsimport { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes) ] };Vous serez amenés à rajouter des providers dans ce fichier, notamment pour pouvoir transférer des informations entre le client et le serveur.
le fichier app.routes.ts
, qui contient les « routes » de l’application :
app.routes.tsimport { Routes } from '@angular/router'; export const routes: Routes = [];Pour l’instant, votre application n’est pas configurée pour avoir plusieurs « pages » (routes), mais l’idée sera de générer une route par page. Par exemple, dans votre forum, vous aurez une route pour la page de login, une pour afficher les cours, une pour les topics, etc.
Dans le répertoire src
, vous pouvez voir un fichier index.html
. C’est le
point d’entrée (le main) de votre application. Ce fichier est simple et instructif.
En gros, il ne contient qu’une balise <app-root></app-root>. Dit autrement, le
DOM ne contient qu’un nœud <app-root>. Cette balise n’existe pas en HTML et c’est
là qu’Angular intervient : Angular sait que celle-ci correspond au composant app.
Dans le DOM, il va donc insérer à la place du <app-root></app-root> tout le code/tous les
nœuds spécifiés dans le composant app. C’est ce qui va remplir toute la page web.
En principe, vous n’aurez jamais à éditer le fichier index.html
.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Frontend</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
Le 2ème fichier intéressant dans le répertoire src
est le fichier
styles.scss
. J’ai dit précédemment que chaque composant possède un fichier
de style. Le fichier styles.scss
a pour but de définir le style « global »
de l’application, autrement dit les propriétés CSS/SCSS communes à tous les
composants. Les fichiers .component.scss
, quant à eux, permettent de
spécifier des styles propres à chaque composant.
Le répertoire racine contient lui-même des fichiers intéressants. Les fichiers
package.json
et package-lock.json
sont utilisés par npm afin d’installer
les packages dont vous avez besoin.
Plus intéressant pour les développeurs, le fichier angular.json
permet de
spécifier comment compiler votre application, quelles options utiliser, etc.
Comme tout fichier json, il encode un objet Javascript.
Il contient par exemple une propriété styles qui définit les fichiers
de styles à inclure dans le fichier index.html
. Si vous souhaitez exploiter
bootstrap, il suffit de rajouter à cette propriété les styles de bootstrap.
Lorsque vous programmez, vous avez besoin de voir ce que fait votre application dans votre navigateur. Pour cela, Angular permet de « servir » votre application via la commande ci-dessous. Celle-ci va compiler votre programme et démarrer un serveur sur lequel votre navigateur pourra aller pour exécuter l’application.
ng serve
Cette commande vous produira un affichage similaire à celui ci-dessous :
Node.js version v23.3.0 detected.
Odd numbered Node.js versions will not enter LTS status and should not be
used for production. For more information, please see
https://nodejs.org/en/about/previous-releases/.
Initial chunk files | Names | Raw size
polyfills.js | polyfills | 90.20 kB |
main.js | main | 18.19 kB |
styles.css | styles | 96 bytes |
| Initial total | 108.49 kB
Application bundle generation complete. [1.584 seconds]
Watch mode enabled. Watching for file changes...
NOTE: Raw file sizes do not reflect development server per-request transformations.
➜ Local: http://localhost:4200/
➜ press h + enter to show help
L’avant dernière ligne indique l’url à utiliser dans votre navigateur pour exécuter votre application. Autrement dit, il faut contacter le serveur se trouvant sur le port 4200 de votre machine (localhost).
Localhost représente votre machine mais cette adresse internet va vous poser
des problèmes avec des navigateurs tels que Chrome. En effet, votre application
frontend sera servie par un serveur sur le port 4200 de votre machine mais les
données qu’elle manipulera proviendront d’un serveur Apache auquel vous accéderez
en http
, pas en https
(autrement dit avec une liaison non sécurisée,
non chiffrée). Certains navigateurs, dont Chrome, vous interdiront de transmettre
des données dans ces conditions, sauf si celles-ci sont échangées entre deux serveurs
de votre machine. Mais comment s’assurer que c’est bien le cas? Utiliser
Localhost n’est pas fiable car on peut affecter à ce nom n’importe quelle adresse
routable sur internet (cf. le fichier /etc/hosts
sous Linux). Aussi, la règle
adoptée par ces navigateurs est la suivante :
Règle
Un serveur est considéré comme local si on communique avec lui via l’adresse IP 127.0.0.1, qui est l’adresse du loopback, une adresse non routable d’internet qui représente votre machine. Les alias de noms de machines ne sont pas pris en compte.
Il faudrait donc qu’Angular serve l’adresse http://127.0.0.1:4200 plutôt que
http://localhost:4200. Le fichier angular.json
décrit précédemment permet
de le faire facilement. Dans ce fichier, recherchez la propriété « serve », qui
spécifie les options du ng serve et rajoutez à la fin de cet objet une nouvelle
propriété options :
{
.......
"serve": {
.............
"defaultConfiguration": "development",
"options": {
"host": "127.0.0.1"
}
},
........
}
Maintenant, si vous faites un ng serve, votre application sera servie sur la machine 127.0.0.1 et cela vous évitera bien des problèmes.
Créez un projet frontend
qui contiendra le frontend Angular de votre forum.
Faites en sorte que votre application soit servie via le port 4200 de la machine d’adresse 127.0.0.1.
Servez votre application et regardez ce qu’elle donne sur un navigateur. Mon conseil sur le choix de ce dernier : utilisez autant que possible Chrome. Évitez firefox car il est vraiment trop permissif et il laissera passer certaines erreurs sans que vous vous en aperceviez.
Dans le fichier src/app/app.component.html
, supprimez toutes les lignes excepté
la dernière (<router-outlet />) qui vous sera utile par la suite. Ajoutez au
dessus une balise <h1></h1> avec un titre et testez votre application. Voilà,
elle est maintenant prête pour accueillir votre forum.
Pour créer un nouveau composant mon-comp, il faut utiliser la commande :
ng generate component mon-comp
ou, en abrégé :
ng g c mon-comp
Cela produira un répertoire mon-comp contenant les fichiers :
mon-comp.component.html
mon-comp.component.scss
mon-comp.component.ts
mon-comp.component.spec.ts
Il n’y a pas grand chose à dire pour l’instant sur les fichiers scss
et html
. Concentrons-nous sur le TypeScript :
import { Component } from '@angular/core';
@Component({
selector: 'app-mon-comp',
imports: [],
templateUrl: './mon-comp.component.html',
styleUrl: './mon-comp.component.scss'
})
export class MonCompComponent {
}
On voit tout en bas que l’on a défini une classe. C’est celle-ci qui contiendra la logique « business » du composant, c’est-à-dire ses données ainsi que des méthodes permettant de les manipuler.
Au dessus de la classe, on a une annotation @Component qui indique à Angular
que la classe qui suit concerne un composant. Dans cette annotation, la
propriété selector indique quelle balise utiliser dans la partie HTML pour
insérer une instance du composant (c’est plus général que ce que je vous dis
ici, mais ce sera suffisant pour faire les TPs. Si vous souhaitez approfondir
vos connaissances en Angular, il faudra vous reporter à la documentation
d’Angular, qui est très bien faite). Ici, si je souhaite insérer deux instances
du composant dans ma page principale, je vais modifier le fichier
app.component.html
de la manière suivante :
<h1>première instance :</h1>
<app-mon-comp />
<h1>deuxième instance :</h1>
<app-mon-comp />
<router-outlet />
La deuxième propriété des @Component concerne les imports. Ici, vous devez importer tous les composants, services, etc. que vous utilisez. C’est vraiment l’équivalent de l'import de Java ou du #include du C. Par conséquent, pour que app puisse utiliser mon-comp, il va falloir rajouter ce dernier dans les imports de app :
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {MonCompComponent} from './mon-comp/mon-comp.component';
@Component({
selector: 'app-root',
imports: [
RouterOutlet,
MonCompComponent
],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
title = 'frontend';
}
Les IDE sont en principe assez intelligents et vont vous proposer de rajouter automatiquement MonCompComponent aux imports de app. En tous cas, c’est ce que fait l’IDE webstorm de JetBrains et c’est bien pratique.
La troisième propriété de @Component indique quel code HTML le composant va utiliser. Ici, en général, on indique le nom du fichier HTML du composant, ce que l’on appelle le template HTML.
Enfin, la dernière propriété indique le fichier de style spécifique que le composant va utiliser.
Créez un composant login qui permettra de se connecter à votre forum. Pour l’instant, contentez-vous de créer le composant et de le visualiser. Vous mettrez les balises <input>, les boutons, etc., plus tard.
bootstrap
est une librairie très populaire, accessible sur l’URL :
Installez-la dans votre frontend via la commande :
npm install bootstrap
La librairie contient notamment des fichiers de style SCSS qui vous simplifieront la vie. La documentation de bootstrap (https://getbootstrap.com/docs/5.3/) vous sera utile pour déterminer les classes bootstrap à utiliser pour obtenir le rendu que vous souhaitez de vos pages.
Comme vu plus haut, dans Angular, si des fichiers de style concernent toute
l’application, on les inclut dans le fichier angular.json
à la racine du frontend.
Dans ce fichier, propriété build, rajoutez les styles de bootstrap. Vous devez
obtenir quelque chose comme :
.....
"styles": [
"src/styles.scss",
"node_modules/bootstrap/scss/bootstrap.scss"
]
......
Vérifiez bien à chaque étape de développement que ng serve compile et ne crée pas d’erreurs.