Simplifier la gestion des formulaires

Simplifier la gestion des formulaires

Voici un formulaire généré à l'aide du moteur de template Haml (que j'utilise pour générer le HTML) et des helpers de Rails (des méthodes disponibles dans les vues pour générer du code).

= form_for(@user) do |f|
  %fieldset
    %legend= t('account.character')
    %ul
      %li
        .label
          = f.label :display_name
        .field
          = f.text_field :display_name
          = render_errors(@user, :display_name)
 
  %fieldset
    %legend= t('account.credentials')
    %ul
      %li
        .label
          = f.label :email
        .field
          = f.email_field :email
          = render_errors(@user, :email)
 
      %li
        .label
          = f.label :password
        .field.double
          .left
            = f.password_field :password
          .right
            = f.password_field :password_confirmation
 
          = render_errors(@user, :password)
 
  .actions
    = f.submit t('account.sign_me_up')
    = t('or')
    = link_to t('back_to_home'), root_path

Ici, la variable @user nous est transmise par le contrôleur. Elle contient simplement une instance de la classe User.

Les helpers

form_for

On utilise le helper form_for qui reçoit en argument notre objet user. Le rôle de ce helper est de générer la balise form, avec les attributs appropriés (action, method, etc.) ainsi que deux champs cachés dont nous verront l'utilité.

Ici, le code généré est :

<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline">
    <input name="utf8" type="hidden" value="&#x2713;" />
    <input name="authenticity_token" type="hidden" value="k6WLU1yqhFExPyPb5Iszqe8n04oLBCHayW/Jt4/0Rj0=" />
  </div>
 
  <!-- Reste du formulaire… -->
 
</form>

Tout d'abord, nous avons un champ qui spécifie l'encodage des données du formulaire (cf. HTML <form> accept-charset Attribute).

L'URL d'action est inférée d'après le type d'objets qu'on passe. Ici, notre objet @user est de la classe Users. De plus, il s'agit d'un objet qui n'existe pas en base de données (puisqu'on a juste instancié un nouvel objet User vide), le framework sait donc que le but du formulaire sera de créer notre utilisateur. Et par défaut, la routes pour créer une ressource est d'envoyer une requête POST à l'adresse de la collections de ressources (ici, /users). Nous verrons ce qui change dans un formulaire de modification plus loin.

Comment on peut le voir, en respectant certaines conventions, le frameworks est capable de deviner pas mal de choses ! C'est une des forces de Rails : préférer les conventions sur la configuration tant que c'est possible en fournissant des comportements par défaut. Ainsi, si votre application utilise des pouples, vous aurez sûrement un modèle nommée Octopus, et Rails devinera que la table s'appelle octopi (le pluriel d'octopus).

Le premier champs caché permet d'inclure au moins un caractères. La présence d'un caractère UTF-8 dans le formulaire force le navigateur à respecter ce choix du développeur. IE6 ne respectait pas l'encodage de la page tant qu'on ne le forçait pas en mettant des caractères UTF-8 dans le formulaire.

Le second champ caché est bien plus utile : il contient un token utilisé pour éviter les attaques CSRF : quand l'application reçoit une requête POST, PUT ou DELETE (typiquement, après avoir posté un formulaire), elle vérifie que l'utilisateur est bien passé par la page qui contenait ce formulaire (en comparant la valeur de ce champ caché à cette même valeur stockée en session à chaque chargement de page). Ceci permet d'empêcher de faire faire aux gens des actions non souhaitées : par exemple, les envoyer sur une page Web qui les redirige vers la page de suppression de leur compte.

La méthode form_for expose un objet (que je capture dans la variable f dans cet exemple) à l'intérieur de son bloc. Ici, il s'agit d'un objet qui représente le formulaire et qui dispose à ce titre de plusieurs méthodes que l'on va utiliser.

t

Sous ce diminutif se cache en réalité la méthode translate. Comme son nom l'indique, il permet de traduire un élément selon une clé.

Les systèmes d'internationalisation et de localisation (i18n) intégrés au framework fait l'objet d'un autre chapitre, puisque c'est un atout à part entière des frameworks.

f.label

Ce helper est une méthode de l'objet que nous expose la méthode form_for, que j'ai présenté plus haut. Il prend en argument le nom d'un attribut de l'objet passé dans le formulaire. Ici, l'attribut display_name. Le code généré est le suivant :

<label for="user_display_name">Nom</label>

Rien de compliqué ici, le framework nous mâche une fois de plus le travail en se chargeant de remplir les attributs pour avoir un label qui pointe vers le bon élément de formulaire.

f.text_field, f.email_field, etc.

Comme pour le label, ces helpers sont des méthodes de notre objet exposé par form_for. Leur rôle est toutefois plus intéressant.

<input id="user_display_name" name="user[display_name]" size="30" type="text" />

Nous verrons très vite que le grand avantage de ces méthodes est de préremplir la valeur du champ si elle est renseigné. Dans notre cas, l'objet a été instancié sans argument, mais nous verrons bientôt qu'en cas d'échec de validation du formulaire, il sera rendu à nouveau en conservant les valeurs des champs renseignés.

Lorsqu'un champ de texte doit recevoir un email ou une URL, pensez bien à spécifier le type email ou url plutôt que text : ça permet aux navigateurs qui le prennent en charge d'effectuer d'aider l'utilisateurs. Par exemple, un smartphone comme l'iPhone gère le type email est affichera une version différente du clavier en présentant les caractères qu'on y retrouve le plus souvent (pas d'espace mais un @ par exemple). Certains navigateurs sont même capable d'indiquer à l'utilisateur si l'email qu'ils ont tapé n'est pas valide.

render_errors

Cette fois, il s'agit d'un helper personnalisé. Son rôle est de générer une liste des erreurs.

def render_errors(object, attribute)
  errors = object.errors[attribute]
  render 'shared/errors', :errors => errors if errors.any?
end

La méthode récupère donc les erreurs pour l'attribut de l'objet donné et les transmet à une vue partielle très simple que voici :

%ul.errors
  - errors.each do |error|
    %li= error

f.submit

Ce helper génère simplement un bouton pour soumettre le formulaire.

 
pourquoi_utiliser_un_framework/gestion_des_formulaires.txt · Dernière modification: 2011/05/31 14:34 par Sephi-Chan