Bonnes pratiques pour la déclaration des options lors de la création d’un widget et pour l’utilisation de la méthode setOptions

Edit me

Introduction

Pour une bonne documentation et une utilisation plus aisée des widgets, plusieurs améliorations ont été apportées à Voozanoo 4 et d’autres sont à venir.

Les évolutions en place aujourd’hui sont, entre autres :

  • Génération de documentation automatique des options et attributs des widgets
  • Gestion de l’initialisation des options via la méthode setOptions de widgetBase
  • Gestion de l’affichage des erreurs via le WidgetErrors (également géré par la méthode setOptions de WidgetBase)

L’ensemble de ces évolutions reposant sur la bonne déclaration des options, cette page à pour vocation de donner des bonnes pratiques en la matière.

La méthode setOptions de widgetbase

Le WidgetBase, dont hérite l’ensemble des widgets, propose une méthode setOptions qui récupère les valeurs des options passées par l’utilisateur et les attribuent aus ATTRS de type ‘option’ ou ‘option_array’ correspondants.

Si une fonction de validation (validator) est présente au sein de l’ATTR, setOptions l’appelle et affiche les éventuelles erreurs retournées à la place du widget (voir l’article consacré au WidgetErrors).

Documentation des widgets

Exemple d’un widget correctement documenté :

WidgetMeter.NAME = 'WidgetMeter';
WidgetMeter.DESCRIPTION = 'Permet d\'afficher un ...';

WidgetMeter.ATTRS = {};

Déclaration des options

Options simples

Exemple d’option correctement déclarée :

WidgetMeter.NAME = 'WidgetMeter';
WidgetMeter.DESCRIPTION = 'Permet d\'afficher un ...';

WidgetMeter.ATTRS = {
    sDenominatorColor: {
        type: 'option',
        value: 'black',
        mandatory: false,
        dynamic_value: true,

        description: 'Couleur de police du label du dénominateur.',
        possible_values: 'Nom de couleur (RGB, Hexa, etc + couleurs bootstrap) ou {dataset.field}',
        version: '>=2.26',
        validator: function (mValue) {
            /* Do things */
        }
    }
};

Détail des différents éléments :

  • ATTRS : Les options doivent être déclarées au sein des ATTRS du widget.
  • type : Le type ‘option’ est indispensable pour que le traitement de l’ATTR par setOptions et important pour la bonne documentation du widget.
  • value : Il s’agit de la valeur par défault de l’option. Pour une option qui serait obligatoire et donc sans valeur par défaut, on attribuera la valeur null à value. Cet élément est renseigné dans la documentation en tant que ‘valeur par défaut’ si la valeur est différentes de null.
  • mandatory : Indique si l’option est indispensable au bon fonctionnement du widget et donc obligatoire.
  • dynamic_value : Permet de rendre dynamique la valeur de l’option. Si true, la valeur passée dans l’option sera exécutée par JsQuery (système de calcul des options show_on)
  • description : Utilisé uniquement pour la documentation. Explique le fonctionnement et/ou l’utilité de l’option.
  • possible_values : Utilisé uniquement pour la documentation. Permet d’informer sur le type de valeur que peut prendre l’option ou sur la liste des valeurs possibles. Est affiché en tant que ‘valeurs possibles’ dans la documentation.
  • version : Utilisé uniquement pour la documentation. Indique jusqu’à quelle version de Voozanoo 4 la compatibilité est assurée.
  • validator : Fonction permettant de vérifier si la valeur passée par l’utilisateur est correcte ou non. Plus d’information sur son utilisation plus bas.

Options groupées

Une option groupée (ou option_array) permet d’appliquer la même logique (initialisation, affichage, validation, etc.) à un nombre indéfini de groupe d’options. Ces groupes rassemblent des options (sub_options) s’appliquant à un seul et même élément.

Exemple : WidgetMeter

Pour fournir les données du graphique, on déclare des groupes d’options comme suit :

   <!-- 1er groupe -->
   <option output="html" option_name="data_field_1" value="{examen.nb_exam_complet}"/>
   <option output="html" option_name="data_label_1" value="Examens complets"/>
   <option output="html" option_name="data_color_1" value="success"/>

   <!-- 2ème groupe -->
   <option output="html" option_name="data_field_2" value="{examen.nb_exam_to_validate}"/>
   <option output="html" option_name="data_label_2" value="Examens à valider"/>
   <option output="html" option_name="data_color_2" value="blue"/>

   <!-- 3ème groupe -->
   <option output="html" option_name="data_field_3" value="{examen.nb_exam_prevu"/>
   <option output="html" option_name="data_label_3" value="Examens prévus"/>
   <option output="html" option_name="data_color_3" value="primary"/>

Les options comportant le même numéro (n, dans data_field/color/label_n) concernent toutes un même élément (une même part) du graphique : elles définissent son label, sa couleur et sa valeur.

L’option groupée data, n’a pas d’existence à proprement parler (on ne peut pas ajouter une option <option output="html" option_name="data" value="[203, 500]"/> au WidgetMeter), mais elle permet de décrire chaque sous-option pour que setOptions puissent setter et lier les bonnes valeurs ensemble.

Exemple d’option groupée correctement déclarée :

WidgetMeter.ATTRS = {
    aData: {
        type: 'option_array',
        value: [],
        mandatory: true,
        validator: function (aValue) {
            /* Do things */
        },

        description: 'Données utilisées pour le graphique. Il est obligatoire de renseigner ' +
            'au moins une option data_field_n. Chaque option data_field/color/label doit être suivie d\'un ' +
            'chiffre n.',

        version: '>=2.26',
        sub_options: {
            'sField': {
                type: 'option',
                value: null,
                mandatory: true,
                dynamic_value: true,
                validator: function (mValue) {
                    /* Do things */
                },
                description: 'Field contenant la valeur de la donnée n à intégrer au graphique.',
                possible_values: 'string',
                version: '>=2.26'
            },
            'sColor': {
                type: 'option',
                value: null,
                mandatory: false,
                dynamic_value: true,
                validator: function (mValue) {
                   /* Do things */
                },
                description: 'Couleur appliquée à la donnée n au sein du graphique.',
                possible_values: 'nom de couleur (RGB, Hexa, etc + couleurs bootstrap) ou {dataset.field}',
                version: '>=2.26'
            },
            'sLabel': {
                type: 'option',
                value: null,
                mandatory: false,
                dynamic_value: false,
                validator: function (mValue) {
                   /* Do things */
                },
                description: 'Label de la donnée n à afficher dans la légende.',
                possible_values: 'label ou {dataset.field}',
                version: '>=2.26'
            }
        }
    }
};

Détail des différents éléments :

  • ATTRS : Les options groupées doivent également être déclarées au sein des ATTRS du widget.
  • type : Le type ‘option_array’ est indispensable pour que le traitement de l’ATTR par setOptions et important pour la bonne documentation du widget.
  • value : Il s’agit de la valeur par défault de l’option. Les option_array sont obligatoirement des arrays.
  • mandatory : Indique si l’option est indispensable au bon fonctionnement du widget et donc obligatoire.
  • dynamic_value : Permet de rendre dynamique la valeur de l’option. Si true, la valeur passée dans l’option sera exécutée par JsQuery (système de calcul des options show_on)
  • description : Utilisé uniquement pour la documentation. Explique le fonctionnement et/ou l’utilité de l’option.
  • version : Utilisé uniquement pour la documentation. Indique jusqu’à quelle version de Voozanoo 4 la compatibilité est assurée.
  • validator : Fonction permettant de vérifier si la valeur passée par l’utilisateur est correcte ou non. Plus d’information sur son utilisation plus bas.
  • sub_options : Contient les déclarations des sous-options de l’option groupée, celles-ci suivant le même modèle qu’une option normale.

Exemple de valeur récupérée pour un get d’option groupée (suit l’exemple ci-dessus) :

Y.WidgetMeter = Y.extend( WidgetMeter, Y.WidgetButton, {
    initializer: function() {
        WidgetMeter.superclass.initializer.apply( this, arguments );
        this.setOptions();

        console.log( this.get( 'aData' ) );

/*   [
        {field: {dataset.field}, label: 'Data 1', position: 1, color: 'success'},
        {field: '100', label: {dataset.field}, position: 2, color: '#222222'}, 
        ...
      ]
*/
    }
} );

A noter

Chaque ATTR, et donc chaque option ou option groupée, peut inclure des fonctions setter et getter qui seront appelées lors de l’utilisation de this.set(‘nomAttr’, mValue) et this.get(‘nomAttr’).

This.set() fait automatiquement appel au validator avant de setter et ne set pas si le validator retourne false.

Validation des options

Pour que la validation des options se fasse de façon automatique et que l’utilisateur puisse bénéficier de messages d’erreurs clairs et faciles à trouver, il est recommandé de combiner l’utilisation de la méthode setOptions pour setter les options et l’attribution d’une fonction validator à chaque option.

Pour ce faire, il suffit de spécifier un validateur dans la déclaration de l’option au sein des ATTRS et de lui faire retourner :

  • true si l’option est valide (selon les critères propres à l’option en question)
  • un tableau de messages d’erreurs si des erreurs ont été rencontrées

Fonctionnement au sein de setOptions

Lorsque setOptions est appelée, la fonction effectue, pour chaque option :

  1. l’attribution de la valeur déclarée par l’utilisateur
  2. la vérification de la validité de la valeur en appelant son son validateur (validator) s’il a été spécifié et en stockant les eventuels messages d’erreurs dans un tableau d’objet erreur. Ces objets incluent :
    • sType : Le type d’option (option ou option_array)
    • sKey : Le nom de l’option en snake_case (Ex : pour l’ATTRS sShowOn, show_on )
    • mValue : La valeur passée par l’utilisateur
    • sMessage : Le message d’erreur

Une fois l’ensemble des options attribuées et testées, setOptions vérifie si des messages d’erreurs on été renvoyés par les validators. Le cas échéant, un WidgetErrors est instancié et affiché à la place du widget.

Exemple de validator:

validator: function( mValue ) {
    return [ 'doughnut', 'bar' ].indexOf( mValue ) !== -1 || [ 'To date, the option only takes "bar" or "doughnut" as value.' ];
}

A noter

WidgetBase propose quelques validateur générique qui peuvent être utilisés pour les vérifications de base.

Ces validateurs prennent en paramètre la valeur de l’option et renvoient soit true, soit un tableau de messages d’erreurs :

  • basicStringValidator : Vérifie que :
    • La valeur reçue n’est pas un booléen (les valeurs reçues par les options sont soit de type string soit de type boolean). Sinon, retourne le message “The option does not accept boolean values.”
    • La valeur peut être interprétée (dans le cas d’une query {dataset.field} par exemple). Sinon, retourne le message “The query passed to the option can’t be resolved.”
    • La valeur interprétée renvoie bien une string. Sinon, retourne le message “The query passed to the option does not resolve to a string.”
  • basicNumberValidator : Vérifie que :
    • La valeur reçue n’est pas un booléen (les valeurs reçues par les options sont soit de type string soit de type boolean). Sinon, retourne le message “The option does not accept boolean values.”
    • La valeur peut être interprétée (dans le cas d’une query {dataset.field} par exemple). Sinon, retourne le message “The query passed to the option can’t be resolved.”
    • La valeur interprétée renvoie bien une string représentant un nombre ou un nombre (integer ou float). Sinon, retourne le message “The query passed to the option does not resolve to a number.”
  • basicBooleanValidator : Vérifie que :
    • La valeur peut être interprétée (dans le cas d’une query {dataset.field} par exemple). Sinon, retourne le message “The query passed to the option can’t be resolved.”
    • La valeur interprétée renvoie bien une string représentant un booléen, 0, ou 1 (les variables de type booléen renvoient 0 ou 1 ou null) ou un booléen. Sinon, retourne le message “The query passed to the option does not resolve to a boolean.”
  • basicFieldNameValidator : Prend en argument le nom du champ et le nom ou l’objet du dataset auquel il appartient. Vérifie que :
    • La valeur reçue n’est pas un booléen (les valeurs reçues par les options sont soit de type string soit de type boolean). Sinon, retourne le message “The option does not accept boolean values.”
    • La valeur peut être interprétée (dans le cas d’une query {dataset.field} par exemple). Sinon, retourne le message “The query passed to the option can’t be resolved.”
    • La valeur interprétée renvoie bien une string. Sinon, retourne le message “The query passed to the option does not resolve to a string.”
    • La valeur interprétée correspond bien à un champ du dataset passé en argument. Sinon, retourne le message “No matching field found in ‘datasetName’”.
  • basicDatasetValidator : Vérifie que :
    • La valeur reçue fait référence à un dataset de la frame courant si elle est de type string”

Exemple

Voici un exemple d’application de ces bonnes pratique avec le WidgetBoolean.

Le WidgetBoolean s’utilise de la manière suivante dans une resource de type formulaire :

<form_row>
    <label dataset="patient" field="consent"/>
    <value dataset="patient" field="consent" mode="rw">
        <option output="html" option_name="widget" value="WidgetBoolean"/>
        <option output="html" option_name="can_be_null" value="false"/>
    </value>
</form_row>

Dans le code JavaScript du WidgetBoolean nous avons :

WidgetBoolean.NAME = 'WidgetBoolean';   
WidgetBoolean.DESCRIPTION = 'Permet de représenter les champs de type booléen';
WidgetBoolean.ATTRS = {

    bCanBeNull: {
        type: 'option',
        value: true,
        mandatory: false,

        description: 'Ne plus permettre le choix "null", une fois la valeur changée',
        possible_values: '{boolean}',
        version: '>=2.26',
        validator: function( mValue ) {
            return this.basicBooleanValidator( mValue );
        }
    }

};

Y.WidgetBoolean = Y.extend(WidgetBoolean, Y.WidgetField,
    {
        initializer: function() {
            WidgetBoolean.superclass.initializer.apply( this, arguments );
            this.setOptions();
        },

        Render: function( sNodeId ) {
            if( this.get( 'bCanBeNull' ) ) {
               //...
            } else {
                //...
            }
        }
    });
}, '0.0.1', {
    requires: ['widgetfield']
});