Aurelia: validating form with reusable validatable custom element

荒凉一梦 提交于 2019-12-12 18:36:25

问题


Short question: How can I validate a parent form when the validation is part of child custom elements?

Long version:

I built a reusable custom element which includes validation which is working like I expect it to do:

validated-input.html:

<template>
<div class="form-group" validate.bind="validation">
    <label></label>
    <input type="text" value.bind="wert" class="form-control" />
</div>
</template>

validated-input.js:

import { bindable, inject } from 'aurelia-framework';
import { Validation } from 'aurelia-validation';

@inject(Validation)
export class ValidatedInputCustomElement {
    @bindable wert;

    constructor(validation) {
        this.validation = validation.on(this)
        .ensure('wert')
            .isNotEmpty()
            .isGreaterThan(0);
    }
} 

I will have some forms that will use this custom element more than once in the same view (can be up to 8 or 12 times or even more). A very simplified example could look like this:

<template>
  <require from="validated-input"></require>
  <form submit.delegate="submit()">
    <validated-input wert.two-way="val1"></validated-input>
    <validated-input wert.two-way="val2"></validated-input>
    <validated-input wert.two-way="val3"></validated-input>
    <button type="submit" class="btn btn-default">save</button>
  </form>
</template>

In the corresponding viewmodel file I would like to ensure that the data can only be submitted if everything is valid. I would like to do something like

this.validation.validate()
    .then(() => ...)
    .catch(() => ...);

but I don't understand yet how (or if) I can pull the overall validation into the parent view.

What I came up with up to now is referencing the viewmodel of my validated-input like this:

<validated-input wert.two-way="val1" view-model.ref="vi1"></validated-input>

and then to check it in the parent like this:

this.vi1.validation.validate()
    .then(() => ...)
    .catch(() => ...);

but this would make me need to call it 8/12/... times.

And I will probably have some additional validation included in the form.

Here is a plunkr with an example: https://plnkr.co/edit/v3h47GAJw62mlhz8DeLf?p=info


回答1:


You can define an array of validation objects (as Fabio Luz wrote) at the form level and then register the custom element validations in this array. The validation will be started on the form submit.

The form code looks like:

validationArray = [];

validate() {
    var validationResultsArray = [];

    // start the validation here
    this.validationArray.forEach(v => validationResultsArray.push(v.validate()));

    Promise.all(validationResultsArray)
        .then(() => this.resulttext = "validated")
        .catch(() => this.resulttext = "not validated");
}

validated-input.js gets a new function to register the validation

bind(context) {
    context.validationArray.push(this.validation);
}

The plunker example is here https://plnkr.co/edit/X5IpbwCBwDeNxxpn55GZ?p=preview




回答2:


but this would make me need to call it 8/12/... times.

And I will probably have some additional validation included in the form.

These lines are very important to me. In my opinion (considering that you do not want to call 8/12 times, and you also need an additional validation), you should validate the entire form, instead of each element. In that case, you could inject the validation in the root component (or the component that owns the form), like this:

import { Validation } from 'aurelia-validation';
import { bindable, inject } from 'aurelia-framework';

@inject(Validation)
export class App {
  val1 = 0;
  val2 = 1;
  val3 = 2;

  resulttext = "";

  constructor(validation) {
    this.validation = validation.on(this)
      .ensure('val1')
        .isNotEmpty()
        .isGreaterThan(0)
      .ensure('val2')
        .isNotEmpty()
        .isGreaterThan(0)
      .ensure('val3')
        .isNotEmpty()
        .isGreaterThan(0);
     //some additional validation here
  }

  validate() {
    this.validation.validate()
        .then(() => this.resulttext = "valid")
        .catch(() => this.resulttext = "not valid");
  }
}

View:

<template>
  <require from="validated-input"></require>
  <form submit.delegate="validate()" validation.bind="validation">
    <validated-input wert.two-way="val1"></validated-input>
    <validated-input wert.two-way="val2"></validated-input>
    <validated-input wert.two-way="val3"></validated-input>

    <button type="submit" class="btn btn-default">validate</button>
  </form>
  <div>${resulttext}</div>
</template>

Now, you can re-use the validated-input component in other places. And of course, you probably have to rename it, because the name validated-input does not make sense in this case.

Here is the plunker example https://plnkr.co/edit/gd9S2y?p=preview


Another approach would be pushing all the validation objects into an array and then calling a function to validate all validations objects, but that sounds strange to me.

Hope it helps!



来源:https://stackoverflow.com/questions/35624771/aurelia-validating-form-with-reusable-validatable-custom-element

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!