Startseite
NexoPOS

Benutzerdefinierte Tab-Komponente

NexoPOS bietet eine schnelle Möglichkeit, ein tabellarisches Formular mit benutzerdefinierten Feldern zu erstellen. Es ist jedoch auch möglich, anstelle des Tab-Inhalts eine benutzerdefinierte Vue-Komponente zu rendern. In diesem Szenario steuern Sie, wie die Felder angezeigt werden, was Ihnen mehr Flexibilität gibt, um ein einzigartiges Layout für Ihre CRUD-Komponente zu erstellen.

Damit Ihre Komponente nahtlos mit NexoPOS funktioniert, müssen Sie Validierungsdetails für Ihre benutzerdefinierte Tab-Komponente bereitstellen. Denken Sie daran, dass sich die benutzerdefinierte Komponente wie ein Tab mit Feldern verhalten sollte. So wird sichergestellt, dass beim Absenden des Formulars auf Datensicherheit geprüft wird.

Benutzerdefinierte View-Komponente registrieren

Der erste Schritt besteht darin, deine Komponente in den globalen Komponentenobjekten zu registrieren: nsExtraComponents. Siehe die Dokumentation zum Einfügen einer Vue-Komponente.

Anforderungen

Damit die benutzerdefinierten Komponenten funktionieren, müssen ein paar Dinge bereitgestellt und berücksichtigt werden:

  • Die Komponente „ns-crud-form.vue“ stellt benutzerdefinierte Props für Ihre Komponente mit dem Namen „tab“ bereit. Dies ist die Definition des Formulars, wie sie in der CRUD-PHP-Klasse erstellt wurde (siehe die Methode „getForm“).
  • Ihre Daten müssen ein Array von Fehlern mit einem leeren Wert enthalten, wie in der data()-Methode der View-Komponente definiert.
  • Wenn die Komponente gemountet ist, solltest du festlegen, ob sie gültig ist oder nicht. Anschließend gibst du ein Ereignis „invalid“ mit dem Array der Fehler aus.
  • Mit den „tab“-Props kannst du den auf deinem Tab-Component gespeicherten Fehler abrufen und anzeigen (Das sind die ausgegebenen Fehler, aber da es nicht empfohlen ist, Props zu mutieren, gehen wir mit der Event-Emission fort).

Konkretes Beispiel

So sollte die Registerkarte für deine benutzerdefinierte Komponente aussehen.

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 );
        }
    }
}

Formulardaten an die übergeordnete Komponente übermitteln

Nachdem der Nutzer mit deiner benutzerdefinierten Tab-Komponente interagiert hat, solltest du möglicherweise seine Daten an die ns-crud-form-Komponente senden, damit diese sie an den Server übermitteln kann. Der bessere Ansatz besteht hier darin, ein „changed“-Event mit den Feldern auszugeben (ein Array von Feldern mit einer bestimmten Definition, die wir später teilen werden). Wir machen das so, weil die ns-crud-form-Komponente nicht in der Lage ist, Formular-Daten bei Bedarf anzufordern. Deine Komponente ist dann dafür verantwortlich, diese bereitzustellen.

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 );
                })
        },
    }
}

Lass uns erklären, was hier passiert:

  1. Wir laden Felder von unserem Endpunkt (aber es kann auch fest codiert werden, wie Sie möchten);
  2. Wir werden die Felder mithilfe der FormValidation-Klasse erstellen, die im Dashboard global verfügbar ist.
  3. Wir überwachen Änderungen an unseren Feldern und geben geänderte Events an die übergeordnete Komponente aus, damit sie Ihre Felddaten mit dem gesamten Formular zusammenführen kann. Aber nur, wenn es gültig ist.

Feldstruktur

Wenn Sie stattdessen keine ns-field-Komponente verwenden, sondern ein von Ihnen erstelltes benutzerdefiniertes Element, müssen Sie dennoch die Feldstruktur einhalten, damit Ihre Formulardaten korrekt übermittelt werden. Die Formulardaten Ihrer Komponente sollten als Objekt von Feldern bereitgestellt werden. Hier ist die Struktur eines Feldes:

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

Die wichtigsten Attribute hier sind „name“ und „value“.

Wiederherstellen des Zustands beim Wechsel des Tabs

Ein Crud-Formular besteht normalerweise aus mehreren Tabs. In der aktuellen Version geht der Status deiner benutzerdefinierten Komponente verloren, wenn du von einem Tab zu einem anderen wechselst. Das liegt hauptsächlich daran, dass die Komponente beim Tabwechsel gemountet und wieder entmountet wird und dadurch ihren Status verliert.

Wenn Sie ein „changed“ mit Ihren Komponentenfeldern ausgeben, werden diese auf einer Eigenschaft (Registerkarte) verfügbar gemacht, die in Ihre Komponente eingespeist wird.

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 );
                })
        },
    }
}