X. Création d'une zone de saisie personnalisée▲
Dans le chapitre précédent, nous avons créé une interface d'administration pour notre application yabe, et préparé la section "Mes billets" (My posts). Cette page va afficher, pour un auteur donné, l'ensemble de ses billets, avec la possibilité de les modifier ou d'en écrire de nouveaux.
Nous pourrions partir du module CRUD pour créer cette page, mais nous décidons ici de partir de zéro, car nous avons besoin de la rendre très personnalisable.
X-A. Lister les billets de l'auteur▲
Notre première étape consiste à récupérer la liste des billets écrits par l'utilisateur actuellement connecté. Améliorons donc notre action Admin.index :
public
static
void
index
(
) {
List<
Post>
posts =
Post.find
(
"author.email"
, Security.connected
(
)).fetch
(
);
render
(
posts);
}
Complétons également le template /yabe/app/views/Admin/index.html :
#{extends 'admin.html' /}
<h3>Welcome ${user}, <span>you have written ${posts.size() ?: 'no'} ${posts.pluralize('post', 'posts')} so far</span></h3>
#{list items:posts, as:'post'}
<p class
=
"post ${post_parity}"
>
<a href
=
"#"
>
${post.title}</a>
</p>
#{/list}
<p id
=
"newPost"
>
<a href
=
"#"
><span>+</span>
write a new post</a>
</p>
Notre premier écran est désormais disponible :
X-B. La page d'écriture d'un billet▲
Nous allons construire un formulaire destiné à l'écriture d'un nouveau billet. Généralement, pour un formulaire donné, nous avons deux actions : l'une pour afficher le formulaire, l'autre pour gérer sa soumission. Créons ainsi les actions Admin.form et Admin.save qui vont gérer ces deux actions.
Tout d'abord modifions le fichier /yabe/conf/routes :
GET /admin/new Admin.form
POST /admin/new Admin.save
Dans la classe Admin.java, ajoutons les méthodes form() et save() :
public
static
void
form
(
) {
render
(
);
}
public
static
void
save
(
) {
// Not implemented yet
}
Bien sûr, créons le template /yabe/app/views/Admin/form.html :
#{extends 'admin.html' /}
<h3>Write, <span>a new post</span></h3>
#{form @save()}
#{ifErrors}
<p class
=
"error"
>
Please correct these errors.
</p>
#{/ifErrors}
<p>
#{field 'title'}
<label>Post title:</label>
<input type
=
"text"
name
=
"${field.name}"
value
=
"${post?.title}"
class
=
"${field.errorClass}"
/>
#{/field}
</p>
<p>
#{field 'content'}
<label>Write here:</label>
<textarea name
=
"${field.name}"
class
=
"${field.errorClass}"
>
${post?.content}</textarea>
#{/field}
</p>
<p>
#{field 'tags'}
<label>Enter some tags:</label>
<input type
=
"text"
size
=
"50"
name
=
"${field.name}"
value
=
"${post?.tags?.join(' ')}"
/>
#{/field}
</p>
<p>
<input type
=
"submit"
value
=
"Publish this post to the blog"
/>
</p>
#{/form}
Editons le fichier /yabe/app/views/Admin/index.html pour lier le bouton "Ecrire un nouveau billet" (Write a new post) à ce nouveau formulaire :
Vérifions le résultat :
Nous avons terminé la déclaration de l'action Admin.save pour gérer correctement la soumission du formulaire. Celle-ci va créer un nouvel objet Post, convertir la liste des tags en une liste d'objets Tag, valider les champs, puis finalement sauvegarder le tout. En cas de problème, cette action réaffichera le formulaire ainsi que les messages d'erreur.
public
static
void
save
(
String title, String content, String tags) {
// Create post
User author =
User.find
(
"byEmail"
, Security.connected
(
)).first
(
);
Post post =
new
Post
(
author, title, content);
// Set tags list
for
(
String tag : tags.split
(
"
\\
s+"
)) {
if
(
tag.trim
(
).length
(
) >
0
) {
post.tags.add
(
Tag.findOrCreateByName
(
tag));
}
}
// Validate
validation.valid
(
post);
if
(
validation.hasErrors
(
)) {
render
(
"@form"
, post);
}
// Save
post.save
(
);
index
(
);
}
Ici, nous utilisons render("@form") comme un raccourci de render("Admin/form.html"). Cette forme d'écriture indique juste à Play d'utiliser le template par défaut pour cette action.
Nous pouvons tester...
X-C. Réutilisation pour l'édition d'un billet▲
Nous avons réalisé un formulaire HTML ainsi que l'action Java permettant d'écrire un nouveau billet. Nous voulons maintenant ajouter la fonctionnalité d'édition d'un billet. Réutiliser le code existant s'avère relativement facile et ne demandera que quelques modifications.
Tout d'abord, modifions l'action Admin.form pour récupérer un billet existant :
public
static
void
form
(
Long id) {
if
(
id !=
null
) {
Post post =
Post.findById
(
id);
render
(
post);
}
render
(
);
}
Comme vous pouvez le voir, nous avons rendu le paramètre id optionnel. De cette façon, nous n'afficherons le Post que si ce paramètre est renseigné. Nous pouvons lier la liste des billets de l'écran principal à la page d'édition. Editons le template /yabe/app/views/Admin/index.html :
#{extends 'admin.html' /}
<h3>Welcome ${user}, <span>you have written ${posts.size() ?: 'no'} ${posts.pluralize('post', 'posts')} so far</span></h3>
#{list items:posts, as:'post'}
<p class
=
"post ${post_parity}"
>
<a href
=
"@{Admin.form(post.id)}"
>
${post.title}</a>
</p>
#{/list}
<p id
=
"newPost"
>
<a href
=
"@{form()}"
><span>+</span>
write a new post</a>
</p>
Cela parait facile, mais nous avons toutefois un petit problème ici. Si nous regardons l'URL générée par le Router pour ces liens, nous voyons quelque chose comme ceci :
/admin/new?id=3
Cela fonctionne, mais ce n'est pas très joli ! Nous définissons donc un nouveau type de route lorsque l'attribut id est soumis :
GET /admin/myPosts/{id} Admin.form
GET /admin/new Admin.form
Constatez que nous définissons cette route avant l'ancienne, de telle façon à ce qu'elle ait une priorité plus forte. Cela signifie que si une id est soumise, Play privilégiera cette première route. Dans le cas contraire, ce sera la seconde route qui sera utilisée.
Rafraichissons la page de "Mes billets", et nous devrions voir une URL plus propre maintenant.
Il nous faut modifier le template /yabe/app/views/Admin/form.html :
#{extends 'admin.html' /}
#{ifnot post?.id}
<h3>Write, <span>a new post</span></h3>
#{/ifnot}
#{else}
<h3>Edit, <span>this post</span></h3>
#{/else}
#{form @save(post?.id)}
#{ifErrors}
<p class
=
"error"
>
Please correct these errors.
</p>
#{/ifErrors}
<p>
#{field 'title'}
<label>Post title:</label>
<input type
=
"text"
name
=
"${field.name}"
value
=
"${post?.title}"
class
=
"${field.errorClass}"
/>
#{/field}
</p>
<p>
#{field 'content'}
<label>Write here:</label>
<textarea name
=
"${field.name}"
class
=
"${field.errorClass}"
>
${post?.content}</textarea>
#{/field}
</p>
<p>
#{field 'tags'}
<label>Enter some tags:</label>
<input type
=
"text"
size
=
"50"
name
=
"${field.name}"
value
=
"${post?.tags?.join(' ')}"
/>
#{/field}
</p>
<p>
<input type
=
"submit"
value
=
"Publish this post to the blog"
/>
</p>
#{/form}
Nous avons ici mis à jour l'action de destination du formulaire afin d'ajouter l'identifiant du billet comme premier paramètre, si celui-ci existe. Ainsi, si le billet a effectivement un identifiant (ce qui signifie qu'il s'agit d'un billet déjà existant), alors il sera envoyé à l'action Admin.save.
Nous pouvons désormais modifier un petit peu la méthode save() pour gérer aussi bien la création que l'édition d'un billet :
public
static
void
save
(
Long id, String title, String content, String tags) {
Post post;
if
(
id ==
null
) {
// Create post
User author =
User.find
(
"byEmail"
, Security.connected
(
)).first
(
);
post =
new
Post
(
author, title, content);
}
else
{
// Retrieve post
post =
Post.findById
(
id);
// Edit
post.title =
title;
post.content =
content;
post.tags.clear
(
);
}
// Set tags list
for
(
String tag : tags.split
(
"
\\
s+"
)) {
if
(
tag.trim
(
).length
(
) >
0
) {
post.tags.add
(
Tag.findOrCreateByName
(
tag));
}
}
// Validate
validation.valid
(
post);
if
(
validation.hasErrors
(
)) {
render
(
"@form"
, post);
}
// Save
post.save
(
);
index
(
);
}
Pour améliorer l'URL nous pouvons utiliser la même astuce que précédemment, en ajoutant une route qui sera privilégiée si un id est fourni :
POST /admin/myPosts/{id} Admin.save
POST /admin/new Admin.save
Voilà qui est terminé ! Nous utilisons désormais la même action pour créer de nouveaux billets, et éditer les existants. Notre travail sur l'interface d'administration est achevé !