08. Gestion des saisons et épisodes
Nous verrons qu'à ce stade, la gestion des saisons et des épisodes est très simple grâce à Angular. C’est pourquoi, nous en profiterons pour aborder la création d’une directive personnalisée pour afficher les épisodes.
Saisons et épidodes dans la vue
Plutôt qu'un long discours, regardons les modifications à apporter à la vue d’édition :
<form name="showForm" ng-submit="saveShow(showForm)" novalidate>
<hr />
<h2>Show properties</h2>
<div class="form-horizontal">
<!-- [formulaire titre et année] -->
<h2>
Seasons
<!-- bouton pour ajouter/enlever une saison -->
<buton class="btn btn-sm btn-success" ng-click="editCtrl.addSeason()">Add</buton>
<buton class="btn btn-sm btn-danger" ng-click="editCtrl.removeSeason()">Remove</buton>
</h2>
<blockquote>
<!-- boucle sur les saisons -->
<div ng-repeat="season in editCtrl.show.seasons">
<h3>
Season: {{season.number}}
<!-- bouton pour ajouter/enlever un épisode -->
<buton class="btn btn-sm btn-default" ng-click="editCtrl.addEpisode(season)"><span class="glyphicon glyphicon-plus"></span></buton>
<buton class="btn btn-sm btn-default" ng-click="editCtrl.removeEpisode(season)"><span class="glyphicon glyphicon-minus"></span></buton>
</h3>
<!-- boucle sur les épisodes qui utilise la directive 'episode' -->
<div ng-repeat="episode in season.episodes" episode="episode"></div>
</div>
</blockquote>
<!-- [formulaire bouton de sauvegarde] -->
</div>
</form>
Enrichissement du controller
Ajoutons maintenant dans le controller les évènements appelés depuis la vue:
// src/modules/show/controllers/ShowEditController.js
'use strict';
app.controller('ShowEditController', ['$location', '$routeParams', 'showService', function($location, $routeParams, showService) {
var vm = this;
// [...]
vm.addSeason = function () {
if (!vm.show.seasons) {
vm.show.seasons = [{ number: 1 }];
} else {
vm.show.seasons.push({ number: vm.show.seasons.length + 1 });
}
};
vm.removeSeason = function () {
if (vm.show.seasons && vm.show.seasons.length) {
vm.show.seasons.pop();
}
};
vm.addEpisode = function (season) {
if (!season.episodes) {
season.episodes = [{ number: 1 }];
} else {
season.episodes.push({ number: season.episodes.length + 1 });
}
}
vm.removeEpisode = function (season) {
if (season.episodes && season.episodes.length) {
season.episodes.pop();
}
}
}
]);
Directive personnalisée
Pour terminer, nous allons créer une directive permettant d'afficher le détail d'un épisode. Tout d'abord créons un dossier directive dans le module show et ajoutons un fichier episode-directive.html qui sera la partie vue de notre directive.
<!-- src/modules/show/directives/episode-directive.html -->
<div style="width:100px;display:inline-block;margin-right:5px"
class="alert text-center"
ng-class="{'alert-success' : episode.view, 'alert-danger' : !episode.view}"
ng-click="toogleView()">
{{episode.number}}
</div>
En plus de la propriété number, l'objet épisode contient une propriété view qui indique si l'épisode a été visionné ou non. Grâce à la directive ng-class nous afficherons soit du rouge soit du vert en fonction de si l'épisode a été regardé. Au click sur l'épisode, par l'intermédiaire de la fonction toogleView() nous changerons cette propriété.
Créons maintenant la directive dans un nouveau fichier JS episodeDirective.js. Pour cela, il convient d' utiliser la méthode directive() qui prend en paramètre un nom et une fonction qui retourne un objet avec plusieurs propriétés spécifiques.
// src/modules/show/directives/episodeDirective.js
'use strict';
app.directive('episode', function () {
return {
templateUrl: 'modules/show/directives/episode-directive.html',
restrict: 'A',
replace : true,
scope: {
episode: '='
},
link: function (scope, el, attrs) {
scope.toogleView = function () {
scope.episode.view = !scope.episode.view;
};
}
};
});
Les propriétés les plus communes pour une directive sont:
- templateUrl et template pour respectivement soit un lien vers la partie vue ou directement le HTML de la vue en inline
- restrict permet de définir comment il est possible d'utiliser la directive dans le HTML. Il existe 4 manières:
- A pour attibut comme nous le faisons depuis le début :
<div episode></div>
- C pour class:
<div class="episode"></div>
- E pour element:
<episode></episode>
- M pour commentaire:
<!-- directive: episode -->
- A pour attibut comme nous le faisons depuis le début :
- replace indique que la balise où est déclarée la directive sera remplacée par le contenu de la directive
- scope si cette propriété n'est pas présente, le scope sera hérité du parent. Autrement, le scope de la directive sera isolé afin de la rendre indépendante du parent. Dans ce cas, il faut présicer un mapping entre le scope parent et la directive. Il est possible de mapper trois types d'éléments:
- = mappe l'objet: si dans le parent
<div episode="monEpisode"></div>
alors dans la directive scope.episode aura pour valeur l'objet monEpisode - @ mappe la chaine de caractère: si dans le parent
<div episode="monEpisode"></div>
alors dans la directive scope.episode aura pour valeur la chaîne "monEpisode" - & mappe une fonction: si dans le parent
<div episode="action()"></div>
alors scope.episode() appelera la fonction action() du parent
- = mappe l'objet: si dans le parent
- link fonction appelée lors de la création de la directive, lorsqu’on doit faire des manipulations du DOM, par exemple avec jQuery, c’est ici qu’il faut le faire.