% -*- mode: latex; -*- mustache tags:  
\ifx\wholebook\relax\else

	\documentclass[10pt,twoside,english]{root/support/latex/sbabook/sbabook}
  \usepackage{import}
  \subimport{root/support/latex/}{common.tex}

  \hypersetup{pdfinfo = {
    Title = {Construction d'une interface Web en Seaside pour TinyBlog},
    Author = {},
    Keywords = {pharo, smalltalk}}}
  \begin{document}
\fi

\chapter{Construction d'une interface Web en Seaside pour TinyBlog}\section{Correction semaine pr\'{e}c\'{e}dente}
Vous pouvez charger la correction de la semaine pr\'{e}c\'{e}dente en ex\'{e}cutant le code suivant:

\begin{displaycode}{smalltalk}
Gofer new 
   smalltalkhubUser: 'PharoMooc' project: 'TinyBlog';
   package: 'ConfigurationOfTinyBlog';
   load. 
#ConfigurationOfTinyBlog asClass loadWeek3Correction
\end{displaycode}

Assurez vous que votre serveur Teapot est bien stopp\'{e}.

\begin{displaycode}{smalltalk}
	TBTeapotWebApp stop
\end{displaycode}
\section{Une interface Web pour TinyBlog en Seaside}
Le travail pr\'{e}sent\'{e} dans la suite est ind\'{e}pendant de celui sur Voyage et sur la base de donn\'{e}es MongoDB.

Nous commen\c{c}ons par d\'{e}finir une interface telle que les utilisateurs la verrons. Dans un prochain chapitre nous d\'{e}velopperons une interface d'administration que le possesseur du blog utilisera.
Nous allons d\'{e}finir des composants Seaside \url{http://www.seaside.st} dont l'ouvrage de r\'{e}f\'{e}rence est disponible en ligne \`{a} \url{http://book.seaside.st}.


\begin{figure}

\begin{center}
\includegraphics[width=0.5\textwidth]{figures/RunningSeaside.png}\caption{Lancer le serveur.\label{RunningSeaside}}\end{center}
\end{figure}

\section{D\'{e}marrer Seaside}
Il existe deux fa\c{c}ons pour d\'{e}marrer Seaside. La premi\`{e}re consiste \`{a} ex\'{e}cuter le code suivant :

\begin{displaycode}{smalltalk}
ZnZincServerAdaptor startOn: 8080.
\end{displaycode}

La deuxi\`{e}me fa\c{c}on est graphique via l'outil Seaside Control Panel (World Menu\textgreater{}Tools\textgreater{}Seaside Control Panel).
Dans le menu contextuel de cet outil (clic droit), cliquez sur \symbol{34}add adaptor...\symbol{34} pour ajoutez un serveur \textcode{ZnZincServerAdaptor}, puis d\'{e}finissez le port (e.g. 8080) sur lequel le serveur doit fonctionner (comme illustr\'{e} dans la figure \ref{RunningSeaside}). En ouvrant un navigateur web \`{a} l'URL \url{http://localhost:8080}, vous devez voir s'afficher la page d'accueil de Seaside comme sur la figure \ref{SeasideWebStart}.


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/SeasideWebStart.png}\caption{V\'{e}rification que Seaside fonctionne.\label{SeasideWebStart}}\end{center}
\end{figure}

\section{Point d'entr\'{e}e de l'application}
Cr\'{e}ez la classe \textcode{TBApplicationRootComponent} qui est le point d'entr\'{e}e de l'application. Elle sert \`{a} l'initialisation de l'application.

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBApplicationRootComponent
   instanceVariableNames: ''
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

Nous d\'{e}clarons l'application au serveur Seaside, en d\'{e}finissant cot\'{e} classe, dans le protocole \textcode{'initialization'} la m\'{e}thode \textcode{initialize} suivante. On en profite pour int\'{e}grer les d\'{e}pendances du framework Bootstrap (les fichiers css et js seront stock\'{e}s dans l'application).

\begin{displaycode}{smalltalk}
TBApplicationRootComponent class >> initialize
   "self initialize"
   | app |
   app := WAAdmin register: self asApplicationAt: 'TinyBlog'.
   app
      addLibrary: JQDeploymentLibrary;
      addLibrary: JQUiDeploymentLibrary;
      addLibrary: TBSDeploymentLibrary
\end{displaycode}

Ex\'{e}cuter \textcode{TBApplicationRootComponent initialize} pour forcer l'ex\'{e}cution de la m\'{e}thode \textcode{initialize}. En effet les m\'{e}thodes \textcode{initialize} de classe ne sont automatiquement ex\'{e}cut\'{e}es que l'on du chargement de la classe. Ici nous venons juste de la d\'{e}finir et donc il est n\'{e}cessaire de l'ex\'{e}cuter pour en voir les b\'{e}n\'{e}fices.

Les m\'{e}thodes de classe \textcode{initialize} sont invoqu\'{e}es automatiquement lors du chargement de la classe.

Ajoutons \'{e}galement la m\'{e}thode \textcode{canBeRoot} afin de pr\'{e}ciser que la classe \textcode{TBApplicationRootComponent} n'est pas qu'un simple composant Seaside mais qu'elle repr\'{e}sente notre application Web.
Elle sera donc instanci\'{e}e d\`{e}s qu'un utilisateur se connecte sur l'application.

\begin{displaycode}{smalltalk}
TBApplicationRootComponent class >> canBeRoot
   ^ true
\end{displaycode}


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/BrowseApplications.png}\caption{TinyBlog est bien enregistr\'{e}e.\label{BrowseApplications}}\end{center}
\end{figure}


Une connexion sur le serveur Seaside (\symbol{34}Browse the applications installed in your image\symbol{34}) permet de v\'{e}rifier que l'application TinyBlog est bien enregistr\'{e}e comme le montre la figure \ref{BrowseApplications}.
\section{Premier rendu simple }
Ajoutons maintenant une m\'{e}thode d'instance \textcode{renderContentOn:} dans le protocole \textcode{rendering} afin de v\'{e}rifier que notre application r\'{e}pond bien.

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> renderContentOn: html
   html text: 'TinyBlog'
\end{displaycode}

En se connectant avec un navigateur sur \url{http://localhost:8080/TinyBlog}, la page affich\'{e}e doit \^{e}tre similaire \`{a} celle sur la figure \ref{EmptyPage}.


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/TinyBlog-EmptyPage2.png}\caption{Une page quasi vide mais servie par Seaside.\label{EmptyPage}}\end{center}
\end{figure}


Ajoutons maintenant des informations dans l'ent\^{e}te de la page HTML afin que TinyBlog ait un titre et soit une application HTML5. 

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> updateRoot: anHtmlRoot
   super updateRoot: anHtmlRoot.
   anHtmlRoot beHtml5.
   anHtmlRoot title: 'TinyBlog'.
\end{displaycode}

Le composant \textcode{TBApplicationRootComponent} est le composant principal de l'application, il ne fait que du rendu graphique limit\'{e}.
Dans le futur, il contiendra des composants et les affichera.
Par exemple, les composants principaux de l'application permettant l'affichage des posts pour les lecteurs du blog mais \'{e}galement des composants pour administrer le blog et ses posts. 

Pour cela, nous avons d\'{e}cid\'{e} que le composant \textcode{TBApplicationRootComponent} contiendra des composants h\'{e}ritant tous de la classe abstraite \textcode{TBScreenComponent} que nous allons d\'{e}finir dans le prochain chapitre. 
\section{Composants visuels pour TinyBlog}
Nous sommes maintenant pr\^{e}ts \`{a} d\'{e}finir les composants visuels de notre application Web.
Les premiers chapitres de \url{http://book.seaside.st} peuvent vous y aider et compl\'{e}ter efficacement ce tutoriel.

La figure \ref{ComponentOverview} montre les diff\'{e}rents composants que nous allons d\'{e}velopper et ou ils se situent. 


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/ComponentOverview.pdf}\caption{Les composants composant l'application TinyBlog.\label{ComponentOverview}}\end{center}
\end{figure}

\subsection{Le composant TBScreenComponent}
Le composant \textcode{TBApplicationRootComponent} contiendra des composants sous-classes de la classe abstraite \textcode{TBScreenComponent}. Cette classe nous permet de factoriser les comportements que nous souhaitons partager entre tous nos composants. 

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBScreenComponent
   instanceVariableNames: ''
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

Les diff\'{e}rents composants d'interface de TinyBlog auront besoin d'acc\'{e}der aux r\`{e}gles m\'{e}tier de l'application. Dans le protocole 'accessing', cr\'{e}ons une m\'{e}thode \textcode{blog} qui retourne une instance de \textcode{TBBlog} (ici notre singleton).

\begin{displaycode}{smalltalk}
TBScreenComponent >> blog
   "Return the current blog in the future we will ask the 
   session to return the blog of the currently logged user."
   ^ TBBlog current
\end{displaycode}

En inspectant l'objet blog retourn\'{e} par \textcode{TBBlog current}, v\'{e}rifier qu'il contient bien des posts. Si ce n'est pas le cas, ex\'{e}cuter \textcode{createDemoPost}.

Dans le futur, lorsque nous g\'{e}rerons les utilisateurs et le fait qu'un utilisateurs puisse avoir plusieurs blogs nous modifierons cette m\'{e}thode pour utiliser des informations stock\'{e}es dans la session active (Voir \textcode{TBSession} plus loin). 


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/SeasideBootstrap.png}\caption{Acc\`{e}s \`{a} la biblioth\`{e}que Bootstrap.\label{bootstrap}}\end{center}
\end{figure}

\section{Bootstrap for Seaside}
La biblioth\`{e}que Bootstrap est totalement accessible depuis Seaside comme nous allons le montrer.
Pour parcourir les nombreux exemples, cliquer sur le lien \textbf{bootstrap} dans la liste des applications servies par Seaside ou pointer votre navigateur sur le lien \url{http://localhost:8080/bootstrap}. Vous devez obtenir l'\'{e}cran de la figure \ref{bootstrap}.

Cliquer sur le lien \textbf{Examples} au bas de la page et vous pouvez ainsi voir les \'{e}l\'{e}ments graphiques ainsi que le code pour les obtenir comme montr\'{e} par la figure \ref{examplebootstrap}. 


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/TBSAlert.png}\caption{Comprendre un \'{e}l\'{e}ment et son code en Bootstrap.\label{examplebootstrap}}\end{center}
\end{figure}

\subsection{Bootstrap}
Le repository pour le source et la documentation est 
\url{http://smalltalkhub.com/#!/~TorstenBergmann/Bootstrap}.
Une d\'{e}mo en ligne est disponible \`{a} l'adresse : \url{http://pharo.pharocloud.com/bootstrap}. Cette biblioth\`{e}que a d\'{e}j\`{a} \'{e}t\'{e} charg\'{e}e dans l'image PharoWeb utilis\'{e}e dans ce tutoriel.
\section{D\'{e}finition du composant TBHeaderComponent}
Profitons de ce composant pour ins\'{e}rer dans la partie sup\'{e}rieure de chaque composant, l'instance d'un composant repr\'{e}sentant l'ent\^{e}te de l'application.

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBHeaderComponent
   instanceVariableNames: ''
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

Le protocole 'rendering' contient la m\'{e}thode \textcode{renderContentOn:} charg\'{e}e d'afficher l'ent\^{e}te.

\begin{displaycode}{smalltalk}
TBHeaderComponent >> renderContentOn: html
   html tbsNavbar beDefault with: [
      html tbsNavbarBrand
         url: '#';
         with: 'TinyBlog' ]
\end{displaycode}

L'ent\^{e}te  (header) est affich\'{e}e \`{a} l'aide d'une barre de navigation Bootstrap (voir la figure \ref{navBlog})

Par d\'{e}faut dans une barre de navigation Bootstrap, il y a un lien sur tbsNavbarBrand qui est ici inutile (sur le titre de l'application). Ici nous l'initialisons avec une ancre \textcode{'\#'} de fa\c{c}on \`{a} ce que si l'utilisateur clique sur le titre, il ne se passe rien. En g\'{e}n\'{e}ral, cliquer sur le titre de l'application permet de revenir \`{a} la page de d\'{e}part du site. 
\subsubsection{Am\'{e}liorations possibles}
Le nom du blog devrait \^{e}tre param\'{e}trable \`{a} l'aide d'une variable d'instance dans la classe \textcode{TBBlog}
et le header pourrait afficher ce titre.
\section{Utilisation du header}
Il n'est pas souhaitable d'instancier syst\'{e}matiquement le composant \`{a} chaque fois qu'un composant est appel\'{e}. Cr\'{e}ons une variable d'instance \textcode{header} dans \textcode{TBScreenComponent} que nous initialisons.

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBScreenComponent
   instanceVariableNames: 'header'
   classVariableNames: ''
   package: 'TinyBlog'
\end{displaycode}

Cr\'{e}ons une m\'{e}thode \textcode{initialize} dans le protocole 'initialize-release':

\begin{displaycode}{smalltalk}
TBScreenComponent >> initialize
   super initialize.
   header := TBHeaderComponent new.
\end{displaycode}


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/navBlog.png}\caption{TinyBlog avec une barre de navigation.\label{navBlog}}\end{center}
\end{figure}

\subsection{Relation composite-composant}
En Seaside, les sous-composants d'un composant doivent \^{e}tre retourn\'{e}s par le composite en r\'{e}ponse au message \textcode{children}. D\'{e}finissons que l'instance du composant \textcode{TBHeaderComponent} est un enfant de \textcode{TBScreenComponent} dans la hi\'{e}rarchie des composants Seaside (et non entre classes Pharo). Nous faisons cela en sp\'{e}cialisant la m\'{e}thode \textcode{children}.

\begin{displaycode}{smalltalk}
TBScreenComponent >> children
   ^ OrderedCollection with: header
\end{displaycode}

Affichons maintenant le composant dans la m\'{e}thode \textcode{renderContentOn:} (protocole 'rendering'):

\begin{displaycode}{smalltalk}
TBScreenComponent >> renderContentOn: html
   html render: header
\end{displaycode}
\section{Utilisation du composant Screen}
Bien que le composant \textcode{TBScreenComponent} n'ait pas vocation \`{a} \^{e}tre utilis\'{e} directement, nous allons l'utiliser de mani\`{e}re temporaire pendant que nous d\'{e}veloppons les autres composants. 
Nous ajoutons \textcode{main} comme variable d'instance dans la classe \textcode{TBApplicationRootComponent}. Nous l'initialisons dans la m\'{e}thode \textcode{initialize} suivante. 

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> initialize
   super initialize.
   main := TBScreenComponent new. 
\end{displaycode}

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> renderContentOn: html
   html render: main
\end{displaycode}

Nous d\'{e}clarons aussi la relation de contenu en retournant main parmi les enfants de \textcode{TBApplicationRootComponent}.

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> children
   ^ { main }
\end{displaycode}

Si vous faites un rafraichissement de l'application dans votre navigateur web vous devez voir la Figure \ref{navBlog}.
\subsubsection{Am\'{e}lioration possibles}
Le nom du blog doit \^{e}tre particularisable en utilisant par exemple une variable d'instance de la classe \textcode{TBBlog}
et l'ent\^{e}te (header) pour afficher ce titre. 
\section{Liste des posts}
Nous allons afficher la liste des posts - ce qui reste d'ailleurs le but d'un blog. Ici nous parlons de l'acc\`{e}s public offert aux lecteurs du blog. Dans le futur, nous  proposerons une interface d'administration des posts.

Cr\'{e}ons un composant \textcode{TBPostsListComponent} qui h\'{e}rite de \textcode{TBScreenComponent}:

\begin{displaycode}{smalltalk}
TBScreenComponent subclass: #TBPostsListComponent
   instanceVariableNames: ''
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

Ajoutons une m\'{e}thode \textcode{renderContentOn:} (protocole rendering) provisoire pour tester l'avancement de notre application (voir Figure \ref{elementary}).

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   html text: 'Blog Posts here !!!'
\end{displaycode}

Nous pouvons maintenant dire au composant de l'application d'utiliser ce composant. Pour cela nous modifions-la ainsi: 

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> initialize
   super initialize.
   main := TBPostsListComponent new. 
\end{displaycode}

Editer cette m\'{e}thode n'est pas une bonne pratique. Nous ajoutons une m\'{e}thode setter qui nous permettra
de changer dynamiquement de composant dans le futur tout en gardant le composant actuel pour une initialisation par d\'{e}faut. 

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> main: aComponent
   main := aComponent
\end{displaycode}


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/ElementaryListPost.png}\caption{TinyBlog avec une liste de posts plutot \'{e}l\'{e}mentaire.\label{elementary}}\end{center}
\end{figure}

\section{Le composant post}
Nous allons maintenant d\'{e}finir le composant \textcode{TBPostComponent} qui affiche le contenu d'un post.

Chaque post du blog sera repr\'{e}sent\'{e} visuellement par une instance de  \textcode{TBPostComponent} qui affiche le titre, la date et le contenu d'un post.

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBPostComponent
   instanceVariableNames: 'post'
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostComponent >> initialize
      super initialize.
      post := TBPost new.
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostComponent >> title
   ^ post title
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostComponent >> text
   ^ post text
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostComponent >> date
   ^ post date
\end{displaycode}

Ajoutons la m\'{e}thode \textcode{renderContentOn:} qui d\'{e}finit l'affichage du post.

\begin{displaycode}{smalltalk}
TBPostComponent >> renderContentOn: html
   html heading level: 2; with: self title.
   html heading level: 6; with: self date.
   html text: self text
\end{displaycode}
\subsubsection{A propos des formulaires}
Dans le chapitre et celui sur l'interface d'administration et qui utilise Magritte , nous montrerons qu'il est rare de d\'{e}finir un composant de mani\`{e}re aussi manuelle. En effet, Magritte permet de d\'{e}crire les donn\'{e}es manipul\'{e}es et offre ensuite la possibilit\'{e} de g\'{e}n\'{e}rer automatiquement des composants Seaside. Le code \'{e}quivalent serait comme suit: 

\begin{displaycode}{smalltalk}
TBPostComponent >> renderContentOn: html
   html render: post asComponent
\end{displaycode}
\section{Afficher les posts}
Maintenant nous pouvons afficher des posts pr\'{e}sents dans la base.

Il ne reste plus qu'\`{a} modifier la m\'{e}thode \textcode{TBPostsListComponent \textgreater{}\textgreater{} renderContentOn:} pour afficher l'ensemble des blogs visibles pr\'{e}sents dans la base.

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   self blog allVisibleBlogPosts do: [ :p |
      html render: (TBPostComponent new post: p) ]
\end{displaycode}

Rafraichissez la page de votre navigateur et vous devez obtenir l'erreur suivante: 

\textcode{Not Found /TinyBlog}
\section{Debugger les erreurs }
Par d\'{e}faut, lorsqu'une erreur se produit dans une application, Seaside retourne une page  HTML contenant un message d'erreur g\'{e}n\'{e}rique. Vous pouvez changer ce message mais le plus pratique pendant le d\'{e}veloppement de l'application est de configurer Seaside pour qu'il ouvre un debugger dans Pharo. Pour cela, ex\'{e}cuter le code suivant :

\begin{displaycode}{smalltalk}
(WAAdmin defaultDispatcher handlerAt: 'TinyBlog') 
    exceptionHandler: WADebugErrorHandler
\end{displaycode}

Rafraichissez la page de votre navigateur et vous devez obtenir un debugger c\^{o}t\'{e} Pharo.
L'analyse de la pile d'appels montre qu'il manque la m\'{e}thode suivante :

\begin{displaycode}{smalltalk}
TBPostComponent >> post: aPost
	post := aPost
\end{displaycode}

Rafraichissez \`{a} nouveau la page de votre navigateur et vous devez obtenir la Figure \ref{better}.


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/betterListPosts.png}\caption{TinyBlog avec une liste de posts.\label{better}}\end{center}
\end{figure}

\section{Affichage de la liste des posts avec Bootstrap}
Nous allons utiliser Bootstrap pour rendre la liste un peu plus jolie en utilisant un container. 

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   html tbsContainer: [ 
   self blog allVisibleBlogPosts do: [ :p |
      html render: (TBPostComponent new post: p) ] ]
\end{displaycode}

Rafraichissez la page et vous devez obtenir la Figure \ref{container}.

\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/ContainerList.png}\caption{TinyBlog avec une liste de posts \'{e}l\'{e}mentaire.\label{container}}\end{center}
\end{figure}

\section{Affichage des posts par cat\'{e}gorie}
Les posts sont class\'{e}s par cat\'{e}gorie. Par d\'{e}faut, si aucune cat\'{e}gorie n'a \'{e}t\'{e} pr\'{e}cis\'{e}e, ils sont rang\'{e}s dans une cat\'{e}gorie sp\'{e}ciale d\'{e}nomm\'{e}e \symbol{34}Unclassified\symbol{34}.

Nous allons cr\'{e}er un composant pour g\'{e}rer une liste de cat\'{e}gories nomm\'{e}e: \textcode{TBCategoriesComponent}.
\subsection{Definition d'un composant pour les cat\'{e}gories}
Nous avons besoin d'un composant qui affiche la liste des cat\'{e}gories pr\'{e}sentes dans la base et permet d'en s\'{e}lectionner une. Ce composant devra donc avoir la possibilit\'{e} de communiquer avec le composant \textcode{TBPostsListComponent} afin de lui communiquer la cat\'{e}gorie courante.

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBCategoriesComponent
   instanceVariableNames: 'categories postsList'
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> categories
   ^ categories
\end{displaycode}

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> categories: aCollection
   categories := aCollection
\end{displaycode}

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> postsList: aComponent
      postsList := aComponent
\end{displaycode}

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> postsList
   ^ postsList
\end{displaycode}

Nous d\'{e}finissons aussi une m\'{e}thode de cr\'{e}ation au niveau classe.

\begin{displaycode}{smalltalk}
TBCategoriesComponent class >> categories: aCollectionOfCategories postsList: aTBScreen
   ^ self new categories: aCollectionOfCategories; postsList: aTBScreen 
\end{displaycode}

La m\'{e}thode \textcode{selectCategory:} (protocole 'action') communique au composant \textcode{TBPostsListComponent} la nouvelle cat\'{e}gorie courante.

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> selectCategory: aCategory
   postsList currentCategory: aCategory
\end{displaycode}

Nous avons donc besoin d'ajouter une variable d'instance pour stocker la cat\'{e}gorie courante dans \textcode{TBPostsListComponent}.

\begin{displaycode}{smalltalk}
TBScreenComponent subclass: #TBPostsListComponent
   instanceVariableNames: 'currentCategory'
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

\begin{displaycode}{smalltalk}
TBScreenComponent >> currentCategory
   ^ currentCategory

TBScreenComponent >> currentCategory: anObject
   currentCategory := anObject
\end{displaycode}
\section{Rendu des cat\'{e}gories}
Nous pouvons maintenant ajouter une m\'{e}thode (protocole 'rendering') pour afficher les cat\'{e}gories sur la page. En particulier pour chaque cat\'{e}gorie nous d\'{e}finissons le fait que cliquer sur la cat\'{e}gorie la s\'{e}lectionne comme la cat\'{e}gorie courante.

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> renderCategoryLinkOn: html with: aCategory
   html tbsLinkifyListGroupItem
      callback: [ self selectCategory: aCategory ];
      with: aCategory
\end{displaycode}

Reste maintenant \`{a} \'{e}crire la m\'{e}thode de rendu du composant: On it\`{e}re sur toutes les cat\'{e}gories et on les affiche

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> renderContentOn: html
   html tbsListGroup: [
      html tbsListGroupItem
         with: [  html strong: 'Categories' ].
      categories do: [ :cat | 
         self renderCategoryLinkOn: html with: cat ]]
\end{displaycode}

Nous avons presque fini mais il faut encore afficher la liste des cat\'{e}gories et mettre \`{a} jour la liste des posts en fonction de la cat\'{e}gorie courante. 
\section{Mise \`{a} jour des Posts}
Pour cela, modifions la m\'{e}thode de rendu du composant \textcode{TBPostsListComponent}.

La m\'{e}thode \textcode{readSelectedPosts} r\'{e}cup\`{e}re dans la base les posts \`{a} afficher. Si elle vaut \textcode{nil}, l'utilisateur n'a pas encore s\'{e}lectionn\'{e} une cat\'{e}gorie et l'ensemble des posts visibles de la base est affich\'{e}. Si elle contient une valeur autre que \textcode{nil}, l'utilisateur a s\'{e}lectionn\'{e} une cat\'{e}gorie et l'application affiche alors la liste des posts attach\'{e}s \`{a} la cat\'{e}gorie.

\begin{displaycode}{smalltalk}
TBPostsListComponent >> readSelectedPosts
   ^ self currentCategory
      ifNil: [ self blog allVisibleBlogPosts ]
      ifNotNil: [ self blog allVisibleBlogPostsFromCategory: self currentCategory ]
\end{displaycode}

Nous pouvons maintenant modifier la m\'{e}thode charg\'{e}e du rendu de la liste des posts:

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   html render: (TBCategoriesComponent
               categories: (self blog allCategories)
               postsList: self).
   html tbsContainer: [ 
      self readSelectedPosts do: [ :p |
         html render: (TBPostComponent new post: p) ] ]
\end{displaycode}

Une instance du composant \textcode{TBCategoriesComponent} est ajout\'{e}e sur la page et permet de s\'{e}lectionner la cat\'{e}gorie courante (voir la figure \ref{ugly}).


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/categoriesUgly.png}\caption{Cat\'{e}gories afin de s\'{e}lectionner les posts.\label{ugly}}\end{center}
\end{figure}

\section{Look et agencement}
Nous allons maintenant agencer le composant \textcode{TBPostsListComponent} en utilisant une mise en place d'un 'responsive design' pour la liste des posts. Cela veut dire que le style CSS va adapter les composants \`{a} l'espace disponible.

 Les composants sont plac\'{e}s dans un container Bootstrap puis agenc\'{e}s sur une ligne avec deux colonnes. La dimension des colonnes est d\'{e}termin\'{e}e en fonction de la r\'{e}solution (viewport) du terminal utilis\'{e}. Les 12 colonnes de Bootstrap sont r\'{e}parties entre la liste des cat\'{e}gories et la liste des posts. Dans le cas d'une r\'{e}solution faible, la liste des cat\'{e}gories est plac\'{e}e au dessus de la liste des posts (chaque \'{e}l\'{e}ment occupant 100\% de la largeur du container).

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   html tbsContainer: [
      html tbsRow showGrid;
         with: [
            html tbsColumn
               extraSmallSize: 12;
               smallSize: 2;
               mediumSize:  4;
               with: [
                  html render: (TBCategoriesComponent
                    categories: (self blog allCategories)
                    postsList: self) ].
      html tbsColumn
               extraSmallSize: 12;
               smallSize: 10;
               mediumSize: 8;
               with: [
         self readSelectedPosts do: [ :p |
             html render: (TBPostComponent new post: p) ] ] ] ]
\end{displaycode}

Vous devez obtenir une application proche de celle repr\'{e}sent\'{e}e figure \ref{nicer}.


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/NicerCategories.png}\caption{Avec un meilleur agencement.\label{nicer}}\end{center}
\end{figure}


Lorsqu'on s\'{e}lectionne une cat\'{e}gorie, la liste des posts est bien mise \`{a} jour.
Toutefois, l'entr\'{e}e courante dans la liste des cat\'{e}gories n'est pas s\'{e}lectionn\'{e}e.
Pour cela, on modifie la m\'{e}thode suivante :

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> renderCategoryLinkOn: html with: aCategory
   html tbsLinkifyListGroupItem
      class: 'active' if: aCategory = self postsList currentCategory ;
      callback: [ self selectCategory: aCategory ]; 
      with: aCategory
\end{displaycode}

Bien que le code fonctionne, on ne doit pas laisser la m\'{e}thode \textcode{TBPostsListComponent \textgreater{}\textgreater{} renderContentOn: html} dans un tel \'{e}tat. Elle est bien trop longue et difficilement r\'{e}utilisable. 
Proposer une solution. 
\section{Notre solution: Plein de petites m\'{e}thodes}
\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   html
      tbsContainer: [ 
         html tbsRow
            showGrid;
            with: [ self renderCategoryColumnOn: html.
                  self renderPostColumnOn: html ] ]
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderCategoryColumnOn: html
   html tbsColumn
      extraSmallSize: 12;
      smallSize: 2;
      mediumSize: 4;
      with: [ self basicRenderCategoriesOn: html ]
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostsListComponent >> basicRenderCategoriesOn: html
   ^ html render: (TBCategoriesComponent 
         categories: self blog allCategories 
         postsList: self)
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderPostColumnOn: html
   html tbsColumn
         extraSmallSize: 12;
         smallSize: 10;
         mediumSize: 8;
         with: [ self basicRenderPostsOn: html ] 
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostsListComponent >> basicRenderPostsOn: html
   ^ self readSelectedPosts do: [ :p | 
        html render: (TBPostComponent new post: p) ]
\end{displaycode}

Nous voici pr\^{e}ts \`{a} d\'{e}finir la partie administrative de l'application.


\begin{figure}

\begin{center}
\includegraphics[width=0.85\textwidth]{figures/finalPublicWebPage.png}\caption{Final TinyBlog Public UI.\label{final}}\end{center}
\end{figure}

\section{Futures \'{e}volutions}
A titre d'exercice, vous pouvez :

\begin{itemize}
\item trier les cat\'{e}gories par ordre alphab\'{e}tique
\item ajouter un lien nomm\'{e} 'All' dans la liste des cat\'{e}gories permettant d'afficher toutes les posts visible quelque soit leur cat\'{e}gorie
\end{itemize}

L'application finale devrait ressembler \`{a} la figure \ref{final}.


\ifx\wholebook\relax\else
\end{document}
\fi