Home - About me - Browse by categories

[ASP.NET MVC] Validation de formulaire avec jQuery

ASP.NET MVC 3 supporte de nouvelle fonctionnalités en terme de validation de formulaire côté client, à l’aide de la bibliothèque de jQuery et de son plugin dédié à la validation.

Dans ce post, nous verrons comment valider un formulaire côté client. Il faut garder à l’esprit que la validation côté client n’est là que pour rendre le formulaire plus ergonomique (il s’agit plutôt d’aide à la saisie) : il est impératif de toujours valider un modèle côté serveur !

Dans le fichier de configuration de votre application ASP.NET MVC 3, vous devriez voir les deux clés suivantes dans la section :

<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />

La première, ClientValidationEnabled, permet de préciser que vous souhaitez que la validation des formulaires côté client (en Javascript donc) soit activée.

La seconde, UnobtrusiveJavaScriptEnabled, permet de préciser que vous souhaitez que du javascript non intrusif soit utilisé pour la validation. Le monteur ASP.NET MVC 3 rajoutera alors des attributs supplémentaires lors de la génération des champs d’un formulaire par l’intermédiaire d’un HtmlHelper (TextBox, TextBoxFor, CheckBox, CheckBoxFor…).

Ces attributs pourront ensuite être interprétés par un framework de validation JQuery : jQuery Validate (et jQuery Validate Unobtrusive).

Lorsque vous ajoutez une nouvelle vue à votre projet via l’interface Visual Studio prévue à cet effet, il est possible de cocher une case Reference script libraries afin que les scripts nécessaires à la mise en place de la validation soit ajoutés à la page :

image

Cela aura pour effet d’ajouter les lignes suivantes à la vue :

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

Il s’agit des scripts nécessaires pour faire fonctionner l’API jQuery Validate. L’ajout de ces deux références de scripts ainsi que les deux clés du fichier Web.config suffise à permettre la validation côté client :

image

NB : pour que cela fonctionne, le modèle doit porter des attributs de validation ! (cf. exemple en bas de page)

Le markup HTML généré lors de l’appel du formulaire est le suivant :

<div class="editor-label">
<label for="Title">Title</label>

<div class="editor-field">
<input class="text-box single-line" data-val="true" data-val-required="Le champ titre est obligatoire" id="Title" name="Title" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>

<div class="editor-label">
<label for="Description">Description</label>

<div class="editor-field">
<input class="text-box single-line" data-val="true" data-val-required="Le champ description est obligatoire" id="Description" name="Description" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Description" data-valmsg-replace="true"></span>

Comme il est possible de le constater, un certain nombre d’attribut / class ont été rajoutés sur les input afin de permettre la valdidation non intrusive :

  • data-val : indique que la validation doit avoir lieu sur cet élément

  • data-val-required : indique que le champ est requis et fournit le message d’erreur à afficher

  • data-val-message-for : indique que la balise span affiche le message d’erreur du champ description

Du coup, si JavaScript est activé sur la machine de l’utilisateur, les champs doivent être saisis convenablement avant le post du formulaire.

Il faut cependant valider le modèle côté serveur, dans le contrôleur :

if (ModelState.IsValid)
{
using (var repo = new BlogRepository())
{
repo.AddBlog(blog);
return RedirectToAction("Index", "Home");
}
}

Si vous désactivez la validation non intrusive (via la clé de configuration UnobtrusiveJavaScriptEnabled vue précédemment) vous obtiendrez alors le markup suivant à la suite du formulaire :

<script type="text/javascript">
//<![CDATA[
if (!window.mvcClientValidationMetadata) { window.mvcClientValidationMetadata = []; }
window.mvcClientValidationMetadata.push({"Fields":[{"FieldName":"Title","ReplaceValidationMessageContents":true,"ValidationMessageId":"Title_validationMessage","ValidationRules":[{"ErrorMessage":"Le champ titre est obligatoire","ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"Description","ReplaceValidationMessageContents":true,"ValidationMessageId":"Description_validationMessage","ValidationRules":[{"ErrorMessage":"Le champ description est obligatoire","ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"IsPublished","ReplaceValidationMessageContents":true,"ValidationMessageId":"IsPublished_validationMessage","ValidationRules":[{"ErrorMessage":"The IsPublished field is required.","ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"PublishDate","ReplaceValidationMessageContents":true,"ValidationMessageId":"PublishDate_validationMessage","ValidationRules":[{"ErrorMessage":"The PublishDate field is required.","ValidationParameters":{},"ValidationType":"required"}]}],"FormId":"form0","ReplaceValidationSummary":false,"ValidationSummaryId":"validationSummary"});
//]]>
</script>

Projet web d’exemple : BlogSample.Mvc.zip

A bientôt image

read more

[ASP.NET MVC] Repository Entity Framework dans un contexte stateless

Les mappeurs objets relationnels comme Entity Framework apportent de nombreux avantages lors du développement d’application utilisant des bases de données comme système de stockage. Au delà d’effectuer un mapping entre le monde objet et le monde relationnel, permettre la génération de requête SQL à partir du langage .NET LINQ, Entity Framework fournit également un système de détection de changement sur les entités afin de générer uniquement le nécessaire lorsque l’on demande un commit sur la base de données.

L’idée est assez simple au final : toute entité chargée depuis un contexte Entity Framework possède un état :

  • Unchanged : l’entité n’est pas modifiée, aucune requête SQL ne sera générée pour celle-ci lors du commit
  • Added : l’entité a été ajoutée au contexte, un ordre INSERT sera généré lors du commit
  • Modified : l’entité a été modifiée depuis qu’elle a été chargée via le contexte, un ordre UPDATE sera généré lors du commit
  • Deleted : l’entité a été supprimée du contexte, un ordre DELETE sera généré lors du commit

Il existe un dernier état que peut prendre une entité : Detached, c’est à dire qu’elle n’a pas été récupérée via le contexte Entity Framework.

L’état d’une entité est maintenue au sein du contexte par l’ObjectStateManager. Dès lors que le contexte Entity Framework est disposé, toutes les informations relatives au suivi des changements sont perdues.

ASP.NET MVC de part sa nature stateless force une très courte durée de vie pour un contexte Entity Framework : j’envoie une requête HTTP au serveur, le contexte Entity Framework est créé, la récupération de données est effectuée, le contexte est libéré et la connection à la base de données fermée, impliquant que le suivi des changements soit impossible dans ce cas : comment replacer mon objet dans le contexte lors que je post un formulaire HTML ?

Pour l’exemple, nous allons travailler avec l’entity data model suivant :

image

Créeons à présent la classe repository qui va permettre d’effectuer les opérations de CRUD sur un item de type Blog :

public class BlogRepository : IDisposable
{
private readonly BlogsContainer _blogsContainer = new BlogsContainer();

public void AddBlog(Blog blog)
{
_blogsContainer.Blogs.AddObject(blog);
_blogsContainer.SaveChanges();
}

public Blog GetBlog(int blogId)
{
return _blogsContainer.Blogs.SingleOrDefault(b => b.Id == blogId);
}

public IEnumerable<Blog> GetBlogs()
{
return _blogsContainer.Blogs.ToList();
}

public void DeleteBlog(Blog blog)
{

}

public void UpdateBlog(Blog blog)
{

}

public void Dispose()
{
_blogsContainer.Dispose();
}
}

Comme il est possible de le constater dans l’extrait de code ci-dessus, les opérations Add/Get sont réalisées de manière tout à fait classique sur l’object context Entity Framework. Cela va en revanche différer pour les méthodes Delete et Update, volontairement laissées vides pour le moment.

En effet, dans un contexte stateless, il n’est pas possible de bénéficier directement du mécanisme de change tracking proposé par Entity Framework : il faut auparavant attacher l’objet au contexte s’il ne l’est pas déjà.

Pour cela, l’object context expose une propriété ObjectStateManager qui va permettre de récupérer une ObjectStateEntry associée a une entité : si celle-ci existe, l’élément est connu du contexte, sinon il est inconnu :

ObjectStateEntry stateEntry;
if (_blogsContainer.ObjectStateManager.TryGetObjectStateEntry(blog, out stateEntry))
{
//l'objet Blog est connu du change tracker
}
else
{
//l'objet blog est inconnu du change tracker
}

Du coup, dans le cas d’un delete, on vérifie si l’objet est connu du change tracker : si oui, pas de souci, sinon on attache l’objet au contexte avant de demander sa suppression :

public void DeleteBlog(Blog blog)
{
ObjectStateEntry stateEntry;
if (!_blogsContainer.ObjectStateManager.TryGetObjectStateEntry(blog, out stateEntry))
{
_blogsContainer.Blogs.Attach(blog);
}

_blogsContainer.Blogs.DeleteObject(blog);
_blogsContainer.SaveChanges();
}

De la même manière, pour la mise à jour il faut vérifier si l’objet est connu du change tracker. Si oui, on va pouvoir utiliser la méthode ApplyCurrentValues de l’object set Blogs pour appliquer les changements et forcer le passage de l’entité dans l’état Modified. Sinon, il faut forcer le changement d’état via l’object state manager, après avoir attacher l’entité blog :

public void UpdateBlog(Blog blog)
{
ObjectStateEntry stateEntry;
if (!_blogsContainer.ObjectStateManager.TryGetObjectStateEntry(blog, out stateEntry))
{
_blogsContainer.Blogs.Attach(blog);
_blogsContainer.ObjectStateManager.ChangeObjectState(blog, System.Data.EntityState.Modified);
}
else
{
_blogsContainer.Blogs.ApplyCurrentValues(blog);
}

_blogsContainer.SaveChanges();
}

Et voilà un repository ultra simple, fonctionnel et adapté au développement d’applications stateless avec ASP.NET MVC.

A bientôt image

read more

Nouveau blog : aspnet mvc 3, windows azure, entity framework…

J’ai décidé d’externaliser mon blog et de quitter la plateforme Dotnet-France, sur laquelle mon ancien blog est toujours hébergé.

Pourquoi ce choix ? Tout simplement parce que je voulais écrire moi-même mon blog afin qu’il corresponde exactement à mes besoins et au passage cela m’a permis de prendre en main un certain nombre de technologies auxquelles je voulais me consacrer depuis un petit moment : ASP.NET MVC 3 et Windows Azure.

Vous l’aurez compris, ce blog a donc été développé avec ASP.NET MVC 3, et utilise le View Engine Razor (qui envoie franchement du paté!) et est hébergé sur Windows Azure pour la partie compute et Windows Azure Storage (Table) pour la partie Data.

Afin de mettre en place certains conceptes (injection de dépendances, scénario d’hébergement in cloud / on premises…), j’ai également développé un provider Entity Framework code first me permettant d’attaquer une base de données Sql Azure à la place du table storage.

J’ai identifié certains point assez intéressants lors du développement de ce blog, aussi je vais tâcher de publier des posts sur le développement d’applications web ASP.NET MVC 3 hébergées dans Windows Azure ou utilisant Entity Framework, l’injection de dépendance…

Mon ancien blog restera en ligne, mais ne sera plus alimenté, donc pensez à mettre à jour vos flux RSS si vous voulez continuer à suivre mes posts image

Comme avant, je n’ai pas véritablement de sujet de prédilection (bon ok, Silverlight / Windows Phone, si je dois choisir) mais j’aime toucher à tout, donc je continuerai à parler des technologies .NET en général sur ce blog, au fur et à mesure de mes découvertes / projets image

N’hésitez pas à me faire part de vos commentaires / remarques / suggestions quant à ce nouveau blog !

A bientôt pour de nouvelles aventures…

Julien

read more