Угловая шаблонно-управляемая проверка формы с несколькими полями - PullRequest
0 голосов
/ 16 мая 2018

Предположим, у меня есть эта простая форма с некоторыми полями ( Пример Stackblitz ):

@Component({
  selector: 'my-app',
  template: 
`
<h1>AppComponent</h1>

<form>
  <h2>UserData</h2>
  <userdata [user]="model.userData"></userdata>

  <h2>Actions</h2>
  <actionbar ></actionbar>
</form>
`,
})
export class AppComponent  { ... }

@Component({
  selector: 'userdata',
  template: 
`
<span class="status {{name.status}}">{{name.status}}</span>
full name:
<input name="name" #name="ngModel" pattern="^.* .*$" required [(ngModel)]="user.name">
<br>

<h3>--- Contacts ---</h3>

<span class="status {{email.status}}">{{email.status}}</span>
email:
<input name="email" #email="ngModel" type="email" [email]="true" [(ngModel)]="user.contacts.email">
<br>


<span class="status {{phone.status}}">{{phone.status}}</span>
phone:
<input name="phone" #phone="ngModel" pattern="^[0-9]*$" [(ngModel)]="user.contacts.phone">
<br>


<h4>---- Address ----</h4>

<span class="status {{street.status}}">{{street.status}}</span>
street:
<input name="street" #street="ngModel" [(ngModel)]="user.contacts.address.street">
<br>

<span class="status {{city.status}}">{{city.status}}</span>
city:
<input name="city" #city="ngModel" [(ngModel)]="user.contacts.address.city">
<br>

<span class="status {{zipcode.status}}">{{zipcode.status}}</span>
zipcode:
<input name="zipcode" #zipcode="ngModel" pattern="^[0-9]{5}$" [(ngModel)]="user.contacts.address.zipcode">
<br>

`,
  viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
})
export class UserDataComponent  {
  @Input() user: any;
}

@Component({
  selector: 'actionbar',
  template: 
`
<span class="status {{form.status}}">{{form.status}}</span>
<input type="button" value="Submit" [disabled]="form.invalid">
`,
  viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
})
export class ActionBarComponent { ... }

(в основном это форма с полями для полного имени и контактной информации, такой как электронная почта, телефон, адрес)

Обратите внимание, что форму можно отправить только в том случае, если она действительна. Форма действительна только в том случае, если все вложенное внутри нее является действительным.

Для проверок по одному полю это сделать довольно легко (в этих полях уже есть некоторые, например, шаблоны регулярных выражений и обязательность).

Теперь я хочу добавить два других бизнес-требования:

  1. требуется как минимум одна контактная информация (электронная почта, телефон или адрес);

  2. если задано какое-либо поле в адресе, то все они (улица, город, почтовый индекс) обязательны.

Возможно ли даже сделать это в шаблонно-управляемых формах?

Ответы [ 2 ]

0 голосов
/ 17 мая 2018

Вот как мне удалось реализовать валидацию по группам ( Пример Stackblitz ).

(для фрагментов, которые я вставил ниже, многоточие в скобкахуказать, что части были опущены для ясности; обратитесь к приведенному выше примеру stackblitz для полного содержания)

Для каждой группы я добавил элемент обертки (я думаю, что это также может быть <ng-container>, хотя и не проверял это) просто для того, чтобы иметь директиву ngModelGroup:

<div ngModelGroup="contacts" (...) >

<h3>--- Contacts ---</h3>

email: (...input...)
phone: (...input...)

<div ngModelGroup="address" (...) >

<h4>---- Address ----</h4>

street: (...input...)
city: (...input...)
zipcode: (...input...)

</div>

</div>

Теперь к каждому из этих новых ngModelGroup могут быть прикреплены валидаторы.Поскольку эти проверки являются специальными, я чувствовал, что они не стоят действительно многократно используемой реализации, все, что мне нужно, - это функции проверки (здесь вставлена ​​только одна из них; другая довольно проста, и вы всегда можете обратиться к stackblitz):

  ifOneThenFullAddress(c: AbstractControl): ValidationErrors | null {
    let value = c.value;

    let street = value && value.street;
    let city = value && value.city;
    let zipcode = value && value.zipcode;

    if ((street && city && zipcode) || (!street && !city && !zipcode))
      return null;

    return { ifOneThenAll: '' };
  }

(этот код был реализован внутри UserDataComponent)

Теперь, чтобы заставить двигатель угловой формы вызывать мои функции, мне пришлось реализовать Validator, но общий (который передает валидацию функции):

@Directive({
  selector: '[fn-validate]',
  providers: [{provide: NG_VALIDATORS, useExisting: FnValidateDirective, multi: true}]
})
export class FnValidateDirective implements Validator {
  @Input('fn-validate') fn: (c: AbstractControl) =>  ValidationErrors | null;

  validate(c: AbstractControl): ValidationErrors | null {
    return this.fn(c);
  }
}

Чтобы использовать его (и связать мои валидирующие функции), мне пришлось изменить элементы группы на:

<div ngModelGroup="contacts" [fn-validate]="atLeastOneContact">
<div ngModelGroup="address" [fn-validate]="ifOneThenFullAddress">

И вуаля, группа в целом была подтверждена моими специальными функциями.

0 голосов
/ 16 мая 2018

Возможно, но это непрактично, поскольку код становится запутанным.

Состояние 1:

<input [required]=!(somefielf1 || somefield2 || somefield3 ... etc )>

и для каждого входа должны соблюдаться разные условия. Ввод потребуется, если ни один из входов не заполнен.

Состояние 2:

то же самое здесь только наоборот - без отрицания

<input name="city" [required]="zipCode || street || whatever else|| ..."/>

Ввод потребуется, если одно из предоставленных полей не пустое.

Вам было бы намного легче делать это с реактивными формами. Условия были бы почти такими же, но их будет легче читать, поддерживать и изменять.

...