##4. Gérer les favoris et les catégories de films
- Gérer des clic utilisateurs avec une seule méthode et déterminer quel élément a été cliqué
- Effectuer des filtres sur des listes
- Appliquer une classe CSS en fonction de la valeur d'un attribut du modèle
- Bonus : Déclencher un evénement custom et le gérer dans le composant parent
- Extra Bonus : Stocker des données dans le local storage
Résultat obtenu à la fin de ce chapitre
###Gérer les catégories de films
-
Dans le fichier
posters.html
, ajoutez le code suivant au dessus dudiv id='movies'
:<div id="menu" class="menu"> <div id="all" class="item item-selected" on-click="{{showCategory}}">All</div> <div id="playing" class="item" on-click="{{showCategory}}">Now playing</div> <div id="upcoming" class="item" on-click="{{showCategory}}">Upcoming</div> <div id="favorite" class="item" on-click="{{showCategory}}">Favorites</div> </div>
- Les 4
div
appellent la même méthodeshowCategory
, la différenciation dudiv
utilisé se fera ici via l'id
dudiv
- Nous aurions aussi pu utiliser un attribut
data-menu
et récupérer la valeur demenu
dans ledataset
(cf. user story précédente)
- Les 4
-
Dans la classe
Posters
, créez la méthodeshowCategory
qui répondra au changement de menu.showCategory(Event e, var detail, Element target) { }
-
Dans le fichier
posters.dart
, importez le fichierutils.dart
et dans la méthodeshowCategory
, appelez la fonctionapplySelectedCSS
sur latarget
avec comme prefixeitem
.Vérifiez dans Dartium le changement d'état quand vous cliquez sur un menu.
-
Ajoutez dans la classe
MovieService
la méthodeFuture<List<Movie>> getMovies(String tag);
qui permettra de récupérer les films ayant un tag particulier. -
Implémentez cette nouvelle méthode de l'interface dans votre implémentation en effectuant un filtre sur la liste des films de votre service.
Ecrivez cette méthode sur une seule ligne !
- On effectue un filtre sur
_movies
en vérifiant l'égalité dutag
du film avec letag
passé en paramètre - N'oubliez pas que méthode
where
renvoie unLazyIterable
! - L'égalité entre 2 objets se fait grâce à l'opérateur
==
(il est d'ailleurs possible de surcharger cet opérateur)
- On effectue un filtre sur
-
Dans la méthode
showCategory
de la classePosters
, ajoutez le code suivant qui, en fonction de l'id de l'élément sélectionné, appellera la bonne méthode du service pour mettre à jour la liste des films :switch (target.id) { case "all": moviesService.getAllMovies().then((List<Movie> ms) => movies = ms); break; case "playing": moviesService.getMovies("now_playing").then((List<Movie> ms) => movies = ms); break; case "upcoming": moviesService.getMovies("upcoming").then((List<Movie> ms) => movies = ms); break; case "favorite": // La gestion des favoris est pour plus tard }
- On appelle le service adéquat en fonction de l'id de la
target
- Le service renvoyant une
Future
, c'est dans la callback de celle-ci que l'on peut affecter la liste des films à notre propriétémovies
movies
étant@observable
, sa modification entrainera un rafraichissement du template adéquat
Astuce :
Le code précédent est redondant. La seule partie spécifique est l'appel de la bonne méthode du service, en fonction de l'id de latarget
. La mise à jour effective des films est identique quelque soit le service appelé !Essayez d'améliorer ce code en factorisant le code redondant
Vérifiez dans Dartium que lors d'un clic sur l'un des menus, la liste des films affichés est modifiée.
- On appelle le service adéquat en fonction de l'id de la
###Gérer les favoris
-
Dans le fichier
poster.html
, modifiez lespan
gérant l'icone favori de la façon suivante :<span class="{{ movie.favorite | asFavoriteClass }}">♥</span>
asFavoriteClass
est une fonction attendant unbool
en paramètre et qui renverra uneString
contenant la classe CSS en fonction de la valeur du paramètre- En faisant ainsi, toutes modifications de la valeur de
movie.favorite
sera répercutée sur la vue
-
Dans la classe
Poster
, implémentez le filtre PolymerasFavoriteClass
qui prenne en paramètre unbool
et qui retournefavorite-selected
si le paramètre esttrue
sinon qui retournefavorite
.- Cette méthode est un filtre utilisé dans le point précédent
- Partant d'un
bool
, elle renvoie le nom d'une classe CSS
-
Dans le fichier
poster.html
, modifiez à nouveau lespan
et ajoutez la gestion du clic souris comme vous l'avez déjà fait précédemment. Le nom de la méthode à appeler seraflipFavorite
: -
Dans la classe
Poster
, ajoutez la méthodeflipFavorite
qui répondra au clic sur l'icone favori, et permuttez la valeur demovie.favorite
à chaque appel.Vérifiez dans Dartium qu'un clic sur le bouton favori déclenche la permutation de son état.
Si l'icone ne s'affiche pas, vérifiez bien que l'attribut
favorite
du modèle est bien@observable
et initialisé àfalse
:@observable bool favorite = false;
-
Ajoutez dans
MovieService
la méthode permettant de récupérer la liste des favoris :Future<List<Movie>> getFavorites();
-
Modifiez votre implémentation du service en vous appuyant sur l'implémentation de
getMovies
et en filtrant sur la valeur de l'attributfavorite
deMovie
. -
Modifiez maintenant la méthode
showCategory
en implémentant le casfavorite
et en appelant la méthode du service que vous venez de créer.
A ce stade vous devriez pouvoir ajouter un film dans vos favoris et pouvoir visualiser la liste de vos favoris
## Bonus : enlever un favori depuis la liste des favoris
Si vous ajoutez un film dans vos favoris et que vous visualisez ensuite la liste de vos favoris, le film nouvellement ajouté apparaît. Pourtant si depuis la liste de vos favoris, vous cliquez à nouveau sur l'icone, celui-ci affiche bien son nouvel état MAIS le film n'est pas enlevé de la liste.
-
Dans la méthode
flipFavorite
de la classePoster
, déclenchez grâce à la méthodePolymerElement.dispatchEvent
un evénement personnalisé (CustomEvent
) :flipFavorite(Event e, var detail, Element target) => dispatchEvent(new CustomEvent("movieupdated", detail: movie ..favorite = !movie.favorite));
- On déclenche un événement personnalisé nommé
movieupdated
avec comme détail le film qui a été modifié - Pour pouvoir, sur la même ligne, effectuer le changement de valeur de l'attribut
favorite
du film et passer le film comme détail, on utilise l'opérateur cascade..
- On déclenche un événement personnalisé nommé
-
Dans
posters.html
ajoutez dans le tag<movie-poster>
la gestion de l'evénementon-movieupdated
comme vous l'auriez fait pour l'événementon-click
.<movie-poster movie="{{ m }}" on-movieupdated="{{ movieUpdated }}"></movie-poster>
-
Ajoutez une méthode
movieUpdated
(qui sera appelée lorsque l'événementon-movieupdated
sera déclenché) dans la classePosters
et implémentez le code suivant :movieUpdated(Event e, Movie detail, Element target) { if (favMenu && !detail.favorite) movies = movies.where((Movie m) => m != detail).toList(); }
Concernant
favMenu
, il s'agit d'une propriété de la classePosters
. Faites en sorte que cet attribut ait la valeurtrue
lorsque vous êtes dans le menu des favoris, etfalse
sinon.- Cette méthode est appelée lorsque l'événement
movieupdated
est déclenché.
Astuce :
Il existe la méthoderemove
pour supprimer un élément. Cependant, si on utilise la méthoderemove
le favori n'est pas supprimé de la liste. Ce défaut provient du fait quemovies
est@observable
=> la vue est rafraichie lorsque l'on affecte une nouvelle liste à la propriété MAIS PAS si on change un élément dans la liste (ajout ou suppression).
Pour faire cela, il faudrait modifier la déclaration de la propriété :List<Movie> movies = toObservable(new List());
et ne plus affecter d'autres listes àmovies
mais faire uniquement des ajouts / suppressions sur celle-ci. - Cette méthode est appelée lorsque l'événement
## Extra Bonus : Stocker les films favoris dans le local-storage
-
Dans la classe
Movie
et à la fin du constructeur nomméfromJSON
rajoutez les lignes suivantes :new PathObserver(this, 'favorite').changes.listen((records) { window.localStorage["${id}"] = '{ "fav" : ${records[0].newValue} }'; });
Explications :
Il existe de nombreuses méthodes pour observer (unitairement ou des listes ou ...). Ici on demande à observer les changements de la propriétéfavorite
sur l'instance courante et on s'abonne à ces changements.
Lorsqu'un changement survient, la fonction anonyme est appelée avec en paramètre la liste des observations (dans ce cas-là une seule observation, le changement de valeur defavorite
). Lors d'un changement de valeur, on stocke la nouvelle valeur dans une chaine JSON ayant pour clé l'id
du film.Astuces :
Le fait de stocker une chaine JSON permet d'évoluer facilement et de stocker d'autres choses sans casser les éléments déjà stockés. -
Au dessus du code précédent (toujours dans le constructeur nommé
fromJSON
), ajoutez le code suivant qui va permettre de charger les préférences du favori lors de la création depuis un flux JSON :favorite = false; try { String data = window.localStorage["${id}"]; if (data != null) favorite = JSON.decode(data)["fav"]; } catch(e) { }
- On charge la valeur du favori à partir du local-storage
- On protège le code pour éviter d'un changement de stockage qui provoquerait une erreur et ne permettrait pas à l'application de fonctionner
##Bravo vous venez de terminer le codelab !
Avant de regarder le code du répertoire user-story-final qui implémente d'autres fonctionnalités, merci de remplir le questionnaire qui vous a été envoyé par email.
Merci