J'aimerais pouvoir afficher au choix, dans ma webapp, les actualités ou les "coups de cœur" en cliquant sur un lien et ça sans recharger ma page et sans que l'état des autres composants ne soient impactés (mon formulaire de saisie ou ma liste de livres).
Pour cela il existe dans Angular le composant nRoute
et nous allons voir comment nous en servir.
Nous avons besoin d'une nouvelle dépendance : angular-route.min.js
Donc modifiez votre fichier bower.json
de la manière suivante:
{
"name": "books",
"version": "0.0.0",
"dependencies": {
"angular" : "1.2.16",
"angular-resource" : "1.2.16",
"angular-route": "1.2.16",
"uikit" : "2.6.0"
}
}
Et lancez la commande bower update
pour télécharger dans votre projet angular-route
.
Ensuite dans la page index.html
ajoutez en bas de page avec les autres définitions de script, la définition suivante:
<script src="bower_components/angular-route/angular-route.min.js"></script>
Un routeur dans Angular est un composant qui est capable de détecter la modification d'une url par une saisie dans la zone d'url du navigateur ou par le biais d'un click sur un lien et de déclencher une action en fonction et mettre à jour le chargement d'une vue par le biais de la directive ng-view
. C'est à dire, si vous avez dans votre page html un tag comme celui-ci <div ng-view></div>
, le routeur pourra non seulement déclencher une action, mais aussi charger un template à l'intérieur de ce tag (de la même manière que la directive ng-include
). Pas clair?
Ok, rien de mieux qu'un exemple, alors.
Modifions index.html
comme suit:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>livres</title>
<link rel="stylesheet" href="bower_components/uikit/dist/css/uikit.almost-flat.min.css" />
</head>
<body ng-app="booksApp" style="padding: 10px">
Insérez la liste ci-dessous :
<div>
<ul class="uk-breadcrumb">
<li><a href="#/actualites">Actualités</a></li>
<li><a href="#/coupsdecoeur">Coup de coeur</a></li>
</ul>
</div>
Remarquez de quelle manière sont renseignées les attributs href
: on fait précéder l'url par #/
.
<div class="uk-container uk-container-center">
<div class="uk-grid" ng-controller="MainCtrl">
<div class="uk-width-4-10">
<div class="uk-panel" ng-include="'books/bookForm.html'" onload="whenFormIsLoaded()"></div>
</div>
<div class="uk-width-6-10">
<div class="uk-panel" ng-include="'books/booksList.html'" onload="whenListIsLoaded()"></div>
</div>
</div>
Ajoutez cette balise:
<div ng-view></div>
Et le reste ne change pas:
</div>
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/angular-resource/angular-resource.min.js"></script>
<script src="bower_components/angular-route/angular-route.min.js"></script>
<script src="js/main.js"></script>
</body>
</html>
ngRoute
Commençons par ajouter la dépendance ngRoute
à notre module principal
var booksApp = angular.module("booksApp", ["ngResource", "ngRoute"]);
Puis définissons notre routeur et son fonctionnement avec la méthode config
de notre module principal en lui passant en paramètre $routeProvider
:
booksApp.config(["$routeProvider",
function($routeProvider) {
$routeProvider.
when('/actualites', {
templateUrl: "templates/actualites.html",
controller: "ActualitesCtrl"
})
.when('/coupsdecoeur', {
templateUrl: "templates/coupsdecoeur.html",
controller: "CoupsDeCoeurCtrl"
})
.otherwise({
redirectTo: "/"
})
}
]);
Que venons nous de faire? A chaque fois que je cliquerais sur <a href="#/actualites">Actualités</a>
le fichier "templates/actualites.html"
sera chargé dans <div ng-view></div>
et le contrôleur "ActualitesCtrl"
sera appelé.
De la même manière, lorsque je cliquerais sur <a href="#/coupsdecoeur">Coup de coeur</a>
le fichier "templates/coupsdecoeur.html"
sera chargé dans <div ng-view></div>
et le contrôleur "CoupsDeCoeurCtrl"
sera appelé.
A noter: si dans la barre d'url du navigateur vous saisissez http://localhost:3000/#/actualites ou http://localhost:3000/#/coupsdecoeur, les actions décrites ci-dessus seront déclenchées de la même façon (sans rechargement de page), ce qui vous permet de bookmarquer l'état de votre webapp (par exemple, avoir un lien direct sur l'état qui affiche les actualités).
Créez un sous-répertoire templates
avec 2 fichiers actualites.html
et coupsdecoeur.html
:
votre_projet/
├── public/
| ├── bower_components/ /* angular est par ici */
| ├── js/
| | └── main.js
| ├── books/
| | ├── bookForm.html
| | └── booksList.html
| ├── templates/
| | ├── actualites.html
| | └── coupsdecoeur.html
| └── index.html
|
Avec les contenus suivants:
actualites.html
<h2>{% raw %}{{title}}{% endraw %}</h2>
<ul ng-repeat="new in news">
<li>{% raw %}{{new.title}}{% endraw %}</li>
</ul>
coupsdecoeur.html
<h2>{% raw %}{{title}}{% endraw %}</h2>
<ul ng-repeat="item in items">
<li>{% raw %}{{item.title}}{% endraw %}</li>
</ul>
Et enfin, ...
Donc dans /public/js/main.js
, ajoutons les contrôleurs suivants
ActualitesCtrl
var ActualitesCtrl = booksApp.controller("ActualitesCtrl", function($scope) {
$scope.title = "Actualités";
$scope.news = [
{ title:"Un nouveau Stephen King à venir?" }
, { title:"Le Kindle paperwhite est juste une turie" }
, { title:"Le format epub 3 en détail" }
];
});
CoupsDeCoeurCtrl
var CoupsDeCoeurCtrl = booksApp.controller("CoupsDeCoeurCtrl", function($scope) {
$scope.title = "Coups de Coeur";
$scope.items = [
{ title:"Game of Thrones" }
, { title:"Bilbo le Hobbit" }
, { title:"Le sorcier de TerreMère" }
];
});
Vous pouvez maintenant tester votre webapp:
Il est tout à fait possible de passer des paramètres via l'url. Par exemple, ajoutez la route suivante au routeur:
.when('/addition/:a/:b', {
templateUrl: "templates/addition.html",
controller: "AdditionCtrl"
})
Créez le contrôleur AdditionCtrl
:
var AdditionCtrl = booksApp.controller("AdditionCtrl", function($scope, $routeParams) {
$scope.result = parseInt($routeParams['a']) + parseInt($routeParams['b']);
});
Créez rapidement un template templates/addition.html
:
<h1>{% raw %}{{result}}{% endraw %}</h1>
Maintenant vous pouvez faire des additions de cette manière : http://localhost:3000/#/addition/5/60
Alors je trouve quelques limitations à ce routeur, j'aurais aimé pouvoir utiliser plusieurs routeurs et plusieurs ng-view
, mais je pense qu'il faut que j'apprenne encore à l'utiliser. J'ai vu qu'il existait des directives angular dans des projets externes qui le permettait. Nous le verrons peut-être plus tard.