#angular #primeng #angular-reactive-forms
#angular #primeng #angular-reactive-forms
Вопрос:
Я работаю над проектом Angular с использованием реактивной формы, и у меня есть следующие сомнения относительно стратегии проверки формы. Я попытаюсь подробно объяснить, что я сделал и в чем моя проблема.
В свой компонентный HTML-код я поместил эту форму (которая использует компоненты PrimeNG):
<form [formGroup]="projectForm">
<p-accordion [multiple]="true">
<p-accordionTab header="Informazioni generali ordine">
<div id="informazioni_generale_ordine">
<div class="row">
<div class="col-2">
<p>ID Ordine</p>
</div>
<div class="col-10">
<p-inputNumber id="idOrdine" formControlName="idOrdine"></p-inputNumber>
</div>
</div>
<div class="row">
<div class="col-2">
<p>Data inserimento ordine</p>
</div>
<div class="col-10">
<p-calendar id="dataInserimentoOrdine" formControlName="dataInserimentoOrdine"></p-calendar>
</div>
</div>
</div>
<div class="row">
<div class="col-2">
<p>Stato ordine</p>
</div>
<div class="col-10">
<input id="statoOrdine" formControlName="statoOrdine" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Commessa</p>
</div>
<div class="col-10">
<input id="commessa" formControlName="commessa" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>CIG</p>
</div>
<div class="col-10">
<input id="CIG" formControlName="CIG" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Data inizio attività</p>
</div>
<div class="col-10">
<p-calendar id="dataInizioAttivita" formControlName="dataInizioAttivita"></p-calendar>
</div>
</div>
<div class="row">
<div class="col-2">
<p>Data fine attività</p>
</div>
<div class="col-10">
<p-calendar id="dataFineAttivita" formControlName="dataFineAttivita"></p-calendar>
</div>
</div>
<div class="row">
<div class="col-2">
<p>Referente</p>
</div>
<div class="col-10">
<input id="referente" formControlName="referente" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Ruolo referente</p>
</div>
<div class="col-10">
<input id="ruoloReferente" formControlName="ruoloReferente" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Tipologia di partecipazione</p>
</div>
<div class="col-10">
<input id="tipologiaDiPartecipazione" formControlName="tipologiaDiPartecipazione" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Quota percentuale di RTI</p>
</div>
<div class="col-10">
<p-inputNumber id="quotaPercentualeDiRTI" formControlName="quotaPercentualeDiRTI" suffix="%"></p-inputNumber>
</div>
</div>
</p-accordionTab>
<p-accordionTab header="Informazioni cliente">
<div id="informazioni_cliente">
<div class="row">
<div class="col-2">
<p>Cliente</p>
</div>
<div class="col-10">
<input id="cliente" formControlName="cliente" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Partita IVA cliente</p>
</div>
<div class="col-10">
<input id="vatCliente" formControlName="vatCliente" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Cliente finale</p>
</div>
<div class="col-10">
<input id="clienteFinale" formControlName="clienteFinale" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Partita IVA cliente finale</p>
</div>
<div class="col-10">
<input id="vatClienteFinale" formControlName="vatClienteFinale" type="text" pInputText />
</div>
</div>
</div>
</p-accordionTab>
<p-accordionTab header="Informazioni contratto">
<div id="informazioni_contratto">
<div class="row">
<div class="col-2">
<p>Tipologia contratto</p>
</div>
<div class="col-10">
<input id="tipologiaContratto" formControlName="tipologiaContratto" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Importo contratto</p>
</div>
<div class="col-10">
<p-inputNumber id="importoContratto" formControlName="importoContratto" mode="currency" currency="EUR" locale="de-DE"></p-inputNumber>
</div>
</div>
<div class="row">
<div class="col-2">
<p>Link Contratto</p>
</div>
<div class="col-10">
<input id="linkContratto" formControlName="linkContratto" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Data sottoscrizione contratto</p>
</div>
<div class="col-10">
<p-calendar id="dataSottoscrizioneContratto" formControlName="dataSottoscrizioneContratto"></p-calendar>
</div>
</div>
</div>
</p-accordionTab>
<p-accordionTab header="Informazioni società">
<div id="informazioni_societa">
<div class="row">
<div class="col-2">
<p>Nome Società</p>
</div>
<div class="col-10">
<input id="nomeSocieta" formControlName="nomeSocieta" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Partita IVA Società</p>
</div>
<div class="col-10">
<input id="vatSocieta" formControlName="vatSocieta" type="text" pInputText/>
</div>
</div>
<div class="row">
<div class="col-2">
<p>BU</p>
</div>
<div class="col-10">
<input id="bu" formControlName="bu" type="text" pInputText />
</div>
</div>
</div>
</p-accordionTab>
<p-accordionTab header="Informazioni accordo quadro">
<div id="informazioni_accordo_quadro">
<div class="row">
<div class="col-2">
<p>Presenza accordo quadro</p>
</div>
<div class="col-10">
<p-selectButton [options]="presenzaAQOption" id="presenzaAQ" formControlName="presenzaAQ" ></p-selectButton>
</div>
</div>
<div class="row">
<div class="col-2">
<p>Link identificatovo accordo quadro</p>
</div>
<div class="col-10">
<input id="linkIdentificativoAQ" formControlName="linkIdentificativoAQ" type="text" pInputText />
</div>
</div>
<div class="row">
<div class="col-2">
<p>Accordo quadro</p>
</div>
<div class="col-10">
<p-inputNumber id="accordoQaudro" formControlName="accordoQaudro" mode="currency" currency="EUR" locale="de-DE" ></p-inputNumber>
</div>
</div>
<div class="row">
<div class="col-2">
<p>Residuo accordo quadro</p>
</div>
<div class="col-10">
<p-inputNumber id="residuoAccordoQaudro" formControlName="residuoAccordoQaudro" mode="currency" currency="EUR" locale="de-DE"></p-inputNumber>
</div>
</div>
<div class="row">
<div class="col-2">
<p>Compagine di accordo quadro</p>
</div>
<div class="col-10">
<input id="compagineDiAQ" formControlName="compagineDiAQ" type="text" pInputText />
</div>
</div>
</div>
</p-accordionTab>
</p-accordion>
</form>
Визуализация чего-то вроде этого:
Как вы можете видеть на предыдущем изображении и фрагменте кода, поскольку у меня много полей, я разделил эти поля на элементы аккордеона (но это не должно быть проблемой).
Затем в моем компонентном классе TypeScript я объявил это поле FormGroup (которое я ввел в свой конструктор):
projectForm: FormGroup;
Затем в компонентном методе ngOnInit() я определил все поля, определенные в HTML-форме, для извлечения значений, вставленных пользователем в поля ввода формы:
ngOnInit() {
this.projectForm = this.fb.group({
idOrdine: [null, [Validators.required, Validators.minLength(5)]],
dataInserimentoOrdine: [null, [Validators.required, Validators.minLength(5)]],
statoOrdine: [null, [Validators.required, Validators.minLength(5)]],
commessa: [null, [Validators.required, Validators.minLength(5)]],
CIG: [null, [Validators.required, Validators.minLength(5)]],
dataInizioAttivita: [null, [Validators.required, Validators.minLength(5)]],
dataFineAttivita: [null, [Validators.required, Validators.minLength(5)]],
referente: [null, [Validators.required, Validators.minLength(5)]],
ruoloReferente: [null, [Validators.required, Validators.minLength(5)]],
tipologiaDiPartecipazione: [null, [Validators.required, Validators.minLength(5)]],
quotaPercentualeDiRTI: [null, [Validators.required, Validators.minLength(5)]],
cliente: [null, [Validators.required, Validators.minLength(5)]],
vatCliente: [null, [Validators.required, Validators.minLength(5)]],
clienteFinale: [null, [Validators.required, Validators.minLength(5)]],
vatClienteFinale: [null, [Validators.required, Validators.minLength(5)]],
tipologiaContratto: [null, [Validators.required, Validators.minLength(5)]],
importoContratto: [null, [Validators.required, Validators.minLength(5)]],
linkContratto: [null, [Validators.required, Validators.minLength(5)]],
dataSottoscrizioneContratto: [null, [Validators.required, Validators.minLength(5)]],
nomeSocieta: [null, [Validators.required, Validators.minLength(5)]],
vatSocieta: [null, [Validators.required, Validators.minLength(5)]],
bu: [null, [Validators.required, Validators.minLength(5)]],
presenzaAQ: [null, [Validators.required, Validators.minLength(5)]],
linkIdentificativoAQ: [null, [Validators.required, Validators.minLength(5)]],
accordoQaudro: [null, [Validators.required, Validators.minLength(5)]],
residuoAccordoQaudro: [null, [Validators.required, Validators.minLength(5)]],
compagineDiAQ: [null, [Validators.required, Validators.minLength(5)]]
});
Наконец, в моем HTML я определил кнопку с событием для извлечения значений, вставленных пользователем в мою форму, что-то вроде этого:
<button pButton type="button" label="Save" icon="pi pi-check" (click)="saveOrder($event)"></button>
на данный момент метод SaveOrder() выводит только значения скомпилированной формы:
public saveOrder(event) {
console.log("saveOrder() START");
console.log(this.projectForm.value);
}
Кажется, это работает: при нажатии кнопки Сохранить вызывается метод SaveOrder(), и я вижу, что поле this.projectForm.value содержит значения, вставленные пользователем в форму …. до сих пор кажется, что все в порядке…
Проблема связана с ролями проверки. Как вы можете видеть на данный момент, я вставил те же поддельные правила, чтобы протестировать его (затем я создам специальную проверку для всех моих полей):
[Validators.required, Validators.minLength(5)]
в основном все поля запрашиваются и должны иметь длину не менее 5 символов. Итак, я ожидал, что если я не скомпилирую определенное поле или если я вставлю значение, состоящее из символа <5, это приведет к ошибке…но это не так.
Фактически, например, если пользователь не вставляет никаких значений в мою форму и не отправляет эту форму, я не получаю ошибки. Просто метод SaveOrder() выведет в консоль объект, где все поля пусты.
Почему? Что не так? Чего мне не хватает? Как я могу исправить эту проблему?
Ответ №1:
Angular выполняет несколько действий в фоновом режиме при использовании ReactiveForms.
Пока все поля не станут действительными, this.form.valid будет false . Это всегда первое, что нужно проверить при отправке формы. Если это значение равно false, не продолжайте ничего сохранять. Если вы добавите [disabled]=»!form.valid» к кнопке, она останется отключенной, пока не пройдут все проверки.
Ничего не заполняя в форме, просто откройте инспектор элементов и выберите элемент управления, в который вы добавили проверки. Вы должны увидеть, что он добавил ng-invalid в атрибут class. Вы можете использовать это, чтобы выделить поля ошибок с помощью CSS.
Например:
.ng-invalid { border: 1px solid red }
Основой этого метода является то, что он будет показывать ошибку, даже когда пользователь просто попадает на страницу.
Поэтому обычно вместе с ним используется другой класс, с ng-касанием.
.ng-touched.ng-invalid { ... }
Проблема в том, что ng-touched будет привязан только при посещении поля хотя бы один раз. Чтобы иметь возможность использовать это, вы можете использовать цикл, чтобы пройти через все элементы управления и коснуться их программно.
Также можно отобразить сообщение об ошибке, используя ngIf, которое будет отображаться только при наличии ошибки.
Например:
<ng-container *ngIf="form.controls.get('idOrdine').errors">Error message</ng-container>
Предпочтительно это следует использовать с переменной, которая устанавливается только при отправке формы, чтобы ошибки не появлялись при загрузке страницы в первый раз.
Вы можете взглянуть на элементы управления, а также заглянуть в экземпляр формы при отправке, чтобы получить более четкое представление о том, что происходит внутри формы. Вы можете найти множество способов сделать то же самое, но решения могут определяться только тем, какое поведение точно требуется.
Ответ №2:
Я столкнулся с той же проблемой. Я исправил это с помощью
ngNativeValidate
Используйте его, как показано ниже:-
<form ngNativeValidate [formGroup]="form" (ngSubmit)="onSubmit()">
Ответ №3:
Это ожидаемое поведение форма по-прежнему недействительна, пока не будет соответствовать определенным критериям проверки, все, что вам нужно, это добавить свою пользовательскую логику и отобразить правильное сообщение об ошибке в шаблоне и запретить вызов API, пока форма не станет действительной.
public saveOrder() {
// if form is invalid do not do any action.
if (this.projectForm.invalid) {
return
}
// call api or whatever your logic once form is submitted with valid data.
}
Если вы напечатаете, console.log(this.projectForm.controls)
вы увидите все элементы управления с их свойствами, вы сможете заметить статус каждого элемента управления формы, т.е. valid
, touched
pristine
подробнее dirty
читайте.
И на основе каждого статуса управления формой вы можете показать соответствующую ошибку в шаблоне.