none
Personnalisation des validations jquery pour MVC Entity Framework CodeFirst RRS feed

  • Discussion générale

  • Bonjour tout le monde,

    Ces dernières semaines je me suis beaucoup bagarré avec la personnalisation de la validation de saisie de données sous jquery 1.7.1, sur un projet MVC 4 / Entity Framework 4.4 Code First avec Visual Web Developer 2010 Express.

    Puis je suis arrivé à la faire fonctionner, alors je vais vous expliquer comment, dans un double but :

    • ça permettra à ceux qui marchent dans mes pas d'avoir des billes pour arriver à leurs fins
    • ça permettra aussi à des gens plus expérimentés qui découvriraient des erreurs, des points qui fonctionnent "par chance", de m'alerter.

    En synthèse ça devrait permettre à tout le monde de mieux travailler avec MVC - Entity Framework et la validation sous jquery.

    Comme vous l'avez j'imagine compris, ce texte s'adresse à des programmeurs web .Net ayant déjà (au moins un peu) programmé sous MVC avec Entity Framework CodeFirst, et ayant lu la description du principe de la validation de données avec jquery.

    Pour commencer nous allons nous intéresser au modèle de documents, sur lequel sont basées toutes les pages créées. Ce fichier se trouve logiquement dans le répertoire Views\Shared, et avec les versions indiquées, par défaut il s'appelle _Layout.cshtml

    Il a cet aspect :

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>@ViewBag.Title</title>
        @Styles.Render("~/Content/themes/base/css", "~/Content/css")
        @Scripts.Render("~/bundles/jquery")
        @Scripts.Render("~/bundles/jqueryval")
    @*  @Scripts.Render("~/bundles/modernizr")  *@
        @Scripts.Render("~/bundles/globalize")
        
        </head>
    <body>
        @RenderBody()
    
        @RenderSection("scripts", required: false)
     </body>
    </html>

    Nous voyons là-dedans des balises @Scripts.Render, elles renvoient à des éléments définis dans le fichier BundleConfig.cs dans le répertoire App_Start. Ceci sera important pour mieux interpréter les résultats obtenus, surtout pour peu qu'ils viennent à s'écarter de ce qui est annoncé, en raison d'une configuration du projet légèrement différente de ce qui est prévu.


    Après ces préliminaires, nous allons entrer dans le vif du sujet.

    Nous avons des validations fournies toutes prêtes sous jquery, de sorte qu'il suffit d'un tag sur une donnée dans la description de la base de données, pour que la validation se mette en place automatiquement sans que le programmeur final ait une ligne de code à écrire.

    ça, ça marche bien avec des nombres. Si un statut doit être compris entre 4 et 7, il suffira de mettre [Range(4;7)] juste avant la déclaration du champ, et puis c'est tout, l'utilisateur bénéficiera d'une validation côté client du moment qu'il autorise l'exécution des scripts, et si ce n'est pas le cas il y a une validation qui s'exécute côté serveur, mais qui bien entendu nécessite un postback pour afficher un message d'erreur, à la différence de la validation côté client.

    Et donc, si nous sommes ici, c'est que ça ne marche pas à tous les coups : quelle surprise ! Eh oui, je veux une date de sortie ultérieure au premier janvier 1713, ça jquery n'a pas prévu de me le valider, il va me falloir modifier la fonction Range qui sert à valider les intervalles, de façon qu'elle valide le respect d'un intervalle aussi bien par une date que par un nombre.

    L'avant-dernière ligne ci-dessous permet alors d'indiquer que la date doit être ultérieure au premier janvier 1713. Les lignes précédentes ne sont pas tout-à-fait dans le sujet mais permettent d'améliorer l'affichage.

            [DisplayFormat(ApplyFormatInEditMode=true,DataFormatString="{0:MM/dd/yyyy}")]
            [Display(Name="Date of release",ShortName="RlsDate",Prompt="RelDate",Description="The date when the movie was released")]
            [Range(typeof(DateTime), "1713/1/1", "9999/1/1", ErrorMessage="min Jan 01 1713 ; max Jan 01 9999")]
            public Nullable<DateTime> ReleaseDate { get; set; }


    Dans le répertoire Scripts, il va nous falloir ajouter un nouveau fichier de scripts, qui contiendra les définitions qui vont nous intéresser. Pour cela, vous vous rappelez qu'il faut faire un clic droit sur le répertoire Scripts, puis Ajouter, Nouvel élément, fichier texte.

    Je propose d'appeler ce fichier Extend.js, puisque donner une nouvelle définition d'une fonction s'appelle l'étendre.

    Dans ce fichier nous allons mettre ceci :

    alert("This is Extend");
    
        jQuery.validator.addMethod("date", function (value, element) {
            return this.optional(element) || Globalize.parseDate(value) != null;
        },"date error");
        jQuery.validator.addMethod("number", function (value, element) {
            //alert(value);
            var prs = Globalize.parseFloat(value);
            //alert(prs);
            return this.optional(element) || !isNaN(prs);
        });
        // http://docs.jquery.com/Plugins/Validation/Methods/range
        jQuery.validator.addMethod("range", function (value, element, param) {
            if (element.attributes["type"].value == "datetime") {
                var DateValue = new Date(value);
                param[0] = new Date(element.attributes["data-val-range-min"].value);
                param[1] = new Date(element.attributes["data-val-range-max"].value);
                value = DateValue;
            }
            var ret1 = (value >= param[0]);
            var ret2 = (value <= param[1]);
            return this.optional(element) || (ret1 && ret2);
        });


    Vous voyez qu'en tête de fichier, j'ai mis une instruction alert, qui donc affichera le nom du module. Ceci vous permettra de vous assurer que vous l'avez bien chargé : qu'il est bien déclaré dans _Layout.cshtml, ça nous allons y venir, et qu'il n'y a pas une faute de syntaxe dans les fonctions, auquel cas celles-ci ne seraient pas reconnues, le fichier serait susceptible de ne même pas apparaître dans le débogueur de Firefox. Ceci m'est arrivé avec le premier test que j'ai effectué, avec le module MCNext car les définitions utilisaient une syntaxe semble-t-il pas compatible avec les versions que j'ai mentionnées ci-dessus.

    Au demeurant, j'ai mis addMethod et ça fonctionne, d'autres ont parlé de extend, en définitive je dois bien avouer ne pas vraiment maîtriser la différence. J'imagine que extend permet de conserver l'ancienne fonction pour y faire référence, mais j'ai aussi une syntaxe de addMethod qui le permet, alors que je n'ai pas réussi à mettre en œuvre l'instruction extend.


    Dans BundleConfig.cs nous trouvons la déclaration des modules de validation :

                bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                            "~/Scripts/jquery.unobtrusive*",
                            "~/Scripts/jquery.validate*"));

    Intuitivement, d'après les noms, je m'attendrais bien à ce que par défaut les fonctions de validation soient déclarées dans jquery.validate.js. Ci-dessus, il s'agit du dernier déclaré, ce qui est cohérent : c'est le dernier module déclaré à contenir la fonction recherchée, qui l'emporte.

    Je vous propose d'ouvrir ce fichier (jquery.validate.js) pour ajouter, en tête, une instruction alert pour afficher son nom. Sous les suppositions précédemment faites :

    alert('jquery.validate.js');

    Ceci va nous permettre de constater dans quel ordre se chargent les modules.

    Maintenant, nous sommes ici pour ajouter d'autres définitions. Le fichier Extend.js est déjà créé dans le répertoire Scripts, à présent il faut qu'il soit référencé dans _Layout.cshtml (ou le fichier qui tient le même rôle, ainsi qu'évoqué en introduction ; par simplicité de langage je vais continuer de parler de _Layout.cshtml).

    J'ouvre ce fichier, puis je clique (simple) sur Extend.js dans le répertoire Scripts, et je le fais glisser avec la souris sur la surface de _Layout.cshtml

    Ceci va y introduire, au moment où je vais lâcher le bouton de la souris, cette balise :

        <script src="/Scripts/Extend.js" type="text/javascript"></script>

    Un endroit possible pour cette balise est la fin de la section head.

    Nous lançons le site et affichons une page concernée par les validations de données. Les noms des modules s'affichent dans des boîtes de message (instruction alert). En fonction des différentes inclusions effectuées à différents endroits du projet il peut arriver que jquery.validate.js soit chargé plusieurs fois. Par défaut cela ne pose pas de problème (si ce n'est un peu de gaspillage). Mais, attention : Extend.js doit être chargé en dernier, puisque c'est là-dedans que nous introduisons les définitions que nous voulons voir s'exécuter pour les fonctions de validation que nous modifions. Si il n'est pas chargé en dernier, c'est bien simple, il sera ignoré, puisque le module par défaut contient déjà des fonctions avec les mêmes noms.

    Dans mon cas j'ai donc mis la balise <scripts src="/Scripts/Extend.js"/> (détaillée ci-dessus) à la fin de body, au lieu de la mettre à la fin de head. Ainsi, au chargement suivant, j'ai bien eu le message "This is Extend" affiché en dernier.

    Une fois cette vérification effectuée, n'oubliez pas de retirer ou au moins mettre en commentaires les balises alert qui servent à afficher les noms des modules.

    Il ne vous reste plus qu'à tester, et au besoin adapter les fonctions à votre configuration. Si une date est définie avec un intervalle, voici ce que vous obtenez :

    • valeur non date : message "saisissez une date" (ou équivalent)
    • valeur date hors intervalle : message "veuillez saisir une date entre X et Y (ou équivalent, et avec X et Y renseignés selon déclaration)
    • valeur date dans l'intervalle : pas de message d'erreur.

    En cas de doute sur l'exécution d'une fonction vous pouvez mettre une instruction alert pour afficher son nom. S'agissant d'une fonction de validation elle sera exécutée à chaque caractère saisi dans le champ. N'oubliez pas bien entendu de retirer cette instruction avant de mettre en production.

    J'ai la surprise de m'apercevoir que ça fonctionne aussi du côté serveur. Je me serais pourtant attendu à avoir à écrire la fonction de validation en csharp pour l'exécuter côté serveur ?


    A présent, ce qui reste à connaître, est le résultat que donnera l'utilisation de ce que je viens de dire, par quelqu'un qui découvre le sujet.







    • Modifié Gloops samedi 11 avril 2015 19:42
    samedi 11 avril 2015 19:05

Toutes les réponses