Accueil
NexoPOS

Composant d’onglet personnalisé

NexoPOS offre un moyen rapide de créer un formulaire à onglets avec des champs personnalisés, mais il est également possible d’afficher un composant Vue personnalisé au lieu du contenu des onglets. Dans ce scénario, vous contrôlez la manière dont les champs sont affichés, ce qui vous donne plus de flexibilité pour créer une mise en page unique pour votre composant CRUD.

Pour que votre composant fonctionne de manière transparente avec NexoPOS, vous devez fournir des informations de validation pour votre composant d’onglet personnalisé. N’oubliez pas que le composant personnalisé doit se comporter comme un onglet avec des champs. Cela garantira qu’au moment de l’envoi du formulaire, les données sont vérifiées pour assurer leur sécurité.

Enregistrer le composant de vue personnalisé

La première étape consiste à enregistrer votre composant dans les objets globaux de composants : nsExtraComponents. Reportez-vous à la documentation sur l’injection d’un composant Vue.

Exigences

Pour que les composants personnalisés fonctionnent, il faut fournir et prendre en compte quelques éléments :

  • Le composant ns-crud-form.vue fournit des props personnalisées à votre composant nommé « tab ». Voici la définition du formulaire telle qu’elle a été faite dans la classe CRUD PHP (consultez la méthode getForm).
  • Vos données doivent inclure un tableau d’erreurs avec une valeur vide, telle que définie dans la méthode data() de votre composant de vue.
  • Lors du montage du composant, vous devez définir s’il est valide ou non. Ensuite, vous émettrez un événement « invalid » avec le tableau des erreurs.
  • En utilisant les props « tab », vous pouvez accéder à l’erreur stockée dans votre composant tab et l’afficher (ce sont les erreurs émises, mais comme il n’est pas recommandé de modifier les props, nous procédons avec l’émission d’événements).

Exemple concret

Voici à quoi doit ressembler votre onglet de composant personnalisé.

export default {
    props: [ 'tab' ],
    data() {
        return {
            errors: []
        }
    },
    template: `
    <div v-if="tab.instance.errors.length > 0" class="mb-2">
        <ns-notice v-for="error of tab.instance.errors">
            <template v-slot:title>{{ __m( 'Warning', 'YourModule' ) }}</template>
            <template v-slot:description>{{ error.message }}</template>
        </ns-notice>
    </div>
    `,
    mounted() {
        this.checkValidity();
    },
    methods: {
        checkValidity() {
            this.errors.push({
                identifier: 'invalid_email', // identifier of the error, this must be unique
                message: 'You must provide a valid email',
                name: 'email', // name of the field               
            });
          
            this.$emit( 'invalid', this.errors );
        }
    }
}

Émettre des données de formulaire au composant parent

Maintenant que l’utilisateur a interagi avec votre composant d’onglet personnalisé, vous souhaiterez peut-être envoyer ses données au composant ns-crud-form afin qu’il puisse les transmettre au serveur. La meilleure approche consiste ici à émettre un événement « changed » avec les champs (un tableau de champs avec une définition spécifique, que nous partagerons plus tard). Nous le ferons ainsi car le composant ns-crud-form ne peut pas exiger les données du formulaire à la demande. Votre composant est alors responsable de les fournir.

export default {
    props: [ 'tab' ],
    data() {
        return {
            errors: [],
            fields: [],
            validation: new FormValidation
        }
    },
    template: `
    <div>
      <div v-if="tab.instance.errors.length > 0" class="mb-2">
          <ns-notice v-for="error of tab.instance.errors">
              <template v-slot:title>{{ __m( 'Warning', 'YourModule' ) }}</template>
              <template v-slot:description>{{ error.message }}</template>
          </ns-notice>
      </div>
      <ns-field field="field" v-for="field of fields"/>
    </div>
    `,
    mounted() {
        // ...
    },
    watch: {
        // we carefully check fields change and emit event to the parent component
        fields: {
            deep: true,
            handle() {
                if ( this.validation.validateFields( this.fields ) ) {
                    this.$emit( 'changed', this.fields );
                }
            }
        }
    },
    methods: {
        // we're loading fields from our endpoint
        loadCustomFields() {
            nsHttpClient.get( '/api/mymodule/fields' ).subscribe({
                next: fields => {
                    this.fields   =   this.validation.createFields( fields );
                })
        },
    }
}

Expliquons ce qui se passe ici :

  1. Nous chargerons les champs depuis notre point de terminaison (mais vous pouvez aussi les coder en dur, comme vous le souhaitez) ;
  2. Nous allons créer les champs à l’aide de la classe FormValidation, disponible globalement sur le tableau de bord.
  3. Nous allons surveiller les changements apportés à nos champs et émettre des événements de modification au composant parent afin qu’il puisse fusionner vos données de champ avec l’ensemble du formulaire. Mais faites-le uniquement si c’est valide.

Structure du champ

Si vous ne souhaitez pas utiliser le composant ns-field mais plutôt un élément personnalisé que vous avez créé, vous devez tout de même respecter la structure du champ si vous voulez que les données de votre formulaire soient correctement envoyées. Les données de votre composant doivent être fournies sous la forme d’un objet de champs. Voici la structure d’un champ :

{
    type: 'text',
    name: 'email',
    description: 'field description',
    validation: 'required', 
    value: 'field value.'
}

Les attributs les plus importants ici sont « nom » et « valeur ».

Restauration de l’état lors du changement d’onglet

Un formulaire CRUD se compose généralement de plusieurs onglets. À l’état actuel, si vous passez d’un onglet à un autre, votre composant personnalisé perd son état. Cela s’explique principalement par le fait que le composant est monté et démonté lors du changement d’onglet, puis perd son état.

Lorsque vous émettez un « changed » avec les champs de votre composant, ceux-ci sont mis à disposition sur une propriété (onglet) injectée dans votre composant.

export default {
    props: [ 'tab' ],
    data() {
        return {
            errors: [],
            fields: [],
            state: {},
            validation: new FormValidation
        }
    },
    template: `
    <div>
      <div v-if="tab.instance.errors.length > 0" class="mb-2">
          <ns-notice v-for="error of tab.instance.errors">
              <template v-slot:title>{{ __m( 'Warning', 'YourModule' ) }}</template>
              <template v-slot:description>{{ error.message }}</template>
          </ns-notice>
      </div>
      <ns-field field="field" v-for="field of fields"/>
    </div>
    `,
    mounted() {
        // if the tab has received our state
        if ( this.tab.fields.length > 0 ) {
            // in case you emit a "changed" event with only one field 
            // you can get the value of that field
            this.state =  this.tab.fields[0].value;
        }
    },
    watch: {
        // we carefully check fields change and emit event to the parent component
        fields: {
            deep: true,
            handle() {
                if ( this.validation.validateFields( this.fields ) ) {
                    this.$emit( 'changed', this.fields );
                }
            }
        }
    },
    methods: {
        // we're loading fields from our endpoint
        loadCustomFields() {
            nsHttpClient.get( '/api/mymodule/fields' ).subscribe({
                next: fields => {
                    this.fields   =   this.validation.createFields( fields );
                })
        },
    }
}