Form validations

The system validates the input of the users.
In most cases, if a form is invalid, the user has no chance to save his changes.
This commit is contained in:
GabrielMeyer 2019-06-17 16:40:23 +02:00
parent 51130da791
commit 4e0e641002
14 changed files with 70 additions and 31 deletions

View File

@ -49,7 +49,7 @@
</button>
<!-- Save button -->
<button mat-button *ngIf="editMode" (click)="save()"><strong translate class="upper">Save</strong></button>
<button mat-button *ngIf="editMode" [disabled]="!isSaveButtonEnabled" (click)="save()"><strong translate class="upper">Save</strong></button>
<!-- Menu button slot -->
<ng-content *ngIf="!editMode" select=".menu-slot"></ng-content>

View File

@ -71,6 +71,12 @@ export class HeadBarComponent {
@Input()
public editMode = false;
/**
* The save button can manually be disabled.
*/
@Input()
public isSaveButtonEnabled = true;
/**
* Determine multiSelect mode: changed interactions and head bar
*/

View File

@ -0,0 +1,19 @@
import { ValidatorFn, FormGroup, ValidationErrors } from '@angular/forms';
/**
* Constant to validate a `duration` field.
*
* `([0-9]+)` looks for a number with any length to the `:` (optional).\n
*
* `([0-5][0-9]?)?`: The user can optionally enter a number for minutes/seconds (0 - 59)
*
* Afterwards the duration can be specified as hours or minutes (via `[h|m]?` - optional). Defaults to `h`.
*
* @param control The form-control to validate
*
* @returns {ValidationErrors | null} Null, if the input is correct, `ValidationErrors` otherwise.
*/
export const durationValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
const regExp = /^\s*([0-9]+)(:)?([0-5][0-9]?)?\s*[h|m]?$/g;
return regExp.test(control.value) || control.value === '' ? null : { valid: false };
};

View File

@ -13,6 +13,7 @@
<!-- Duration -->
<mat-form-field>
<input type="string" matInput placeholder="{{ 'Duration' | translate }}" formControlName="durationText" />
<mat-error *ngIf="agendaInfoForm.invalid">Your input does not match the following structure 'hh:mm'...</mat-error>
</mat-form-field>
<!-- Item number (prefix) -->
@ -34,6 +35,6 @@
</form>
</div>
<div mat-dialog-actions>
<button mat-button (click)="saveItemInfo()"><span translate>Save</span></button>
<button mat-button (click)="saveItemInfo()" [disabled]="agendaInfoForm.invalid"><span translate>Save</span></button>
<button mat-button (click)="onCancelButton()"><span translate>Cancel</span></button>
</div>

View File

@ -5,6 +5,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ViewItem } from '../../models/view-item';
import { ItemVisibilityChoices } from 'app/shared/models/agenda/item';
import { DurationService } from 'app/core/ui-services/duration.service';
import { durationValidator } from 'app/shared/validators/custom-validators';
/**
* Dialog component to change agenda item details
@ -42,7 +43,7 @@ export class ItemInfoDialogComponent {
) {
this.agendaInfoForm = this.formBuilder.group({
type: [''],
durationText: [''],
durationText: ['', durationValidator],
item_number: [''],
comment: ['']
});

View File

@ -3,6 +3,7 @@
mainButtonIcon="edit"
[nav]="false"
[editMode]="editAssignment"
[isSaveButtonEnabled]="assignmentForm.valid"
(mainEvent)="setEditMode(!editAssignment)"
(saveEvent)="saveAssignment()"
>
@ -242,16 +243,16 @@
formControlName="title"
[value]="assignmentCopy.title || ''"
/>
<mat-error>{{ 'The title is required' | translate }}</mat-error>
</mat-form-field>
</div>
<h4>{{ 'Description' | translate }}:</h4>
<!-- description: HTML Editor -->
<h4>{{ 'Description' | translate }}:</h4>
<editor
formControlName="description"
[init]="tinyMceSettings"
*ngIf="assignment && editAssignment"
required
></editor>
<!-- searchValueSelector: tags -->
@ -292,8 +293,11 @@
matInput
placeholder="{{ 'Number of persons to be elected' | translate }}"
formControlName="open_posts"
[value]="assignmentCopy.assignment.open_posts || null"
type="number"
required
/>
<mat-error *ngIf="assignmentForm.get('open_posts').hasError('required')">{{ 'This field is required' | translate }}</mat-error>
<mat-error *ngIf="assignmentForm.get('open_posts').hasError('min')">{{ 'The number has to be greater than "0" ' | translate }}</mat-error>
</mat-form-field>
</div>
<!-- TODO searchValueSelector: Parent -->

View File

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatSnackBar } from '@angular/material';
import { Router, ActivatedRoute } from '@angular/router';
import { Title, DomSanitizer, SafeHtml } from '@angular/platform-browser';
@ -196,10 +196,10 @@ export class AssignmentDetailComponent extends BaseViewComponent implements OnIn
phase: null,
tags_id: [],
attachments_id: [],
title: '',
description: '',
poll_description_default: '',
open_posts: 0,
title: ['', Validators.required],
description: [''],
poll_description_default: [''],
open_posts: [1, [Validators.required, Validators.min(1)]],
agenda_create: [''],
agenda_parent_id: [],
agenda_type: ['']

View File

@ -13,9 +13,9 @@
<p>
<mat-form-field>
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
<mat-hint *ngIf="!createForm.controls.name.valid">
<mat-error *ngIf="!createForm.controls.name.valid">
<span translate>Required</span>
</mat-hint>
</mat-error>
</mat-form-field>
</p>
<p>
@ -39,7 +39,7 @@
</form>
</mat-card-content>
<mat-card-actions>
<button mat-button (click)="create()">
<button [disabled]="createForm.invalid" mat-button (click)="create()">
<span translate>Create</span>
</button>
<button mat-button (click)="commentSectionToCreate = null">
@ -62,18 +62,20 @@
{{ section.name }}
</div>
<div class="read">
<mat-icon>visibility</mat-icon>
{{ section.read_groups }}
<ng-container *ngIf="section.read_groups.length === 0">
&ndash;
</ng-container>
<os-icon-container icon="visibility">
{{ section.read_groups }}
<ng-container *ngIf="section.read_groups.length === 0">
&ndash;
</ng-container>
</os-icon-container>
</div>
<div class="write">
<mat-icon>edit</mat-icon>
{{ section.write_groups }}
<ng-container *ngIf="section.write_groups.length === 0">
&ndash;
</ng-container>
<os-icon-container icon="edit">
{{ section.write_groups }}
<ng-container *ngIf="section.write_groups.length === 0">
&ndash;
</ng-container>
</os-icon-container>
</div>
</div>
</mat-panel-title>
@ -83,9 +85,9 @@
<p>
<mat-form-field>
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
<mat-hint *ngIf="!updateForm.controls.name.valid">
<mat-error *ngIf="!updateForm.controls.name.valid">
<span translate>Required</span>
</mat-hint>
</mat-error>
</mat-form-field>
</p>
<p>
@ -128,7 +130,7 @@
<button *ngIf="editId === section.id" mat-button (click)="editId = null" mat-icon-button>
<mat-icon>cancel</mat-icon>
</button>
<button *ngIf="editId === section.id" mat-button (click)="onSaveButton(section)" mat-icon-button>
<button *ngIf="editId === section.id" [disabled]="updateForm.invalid" mat-button (click)="onSaveButton(section)" mat-icon-button>
<mat-icon>save</mat-icon>
</button>
<button mat-button (click)="onDeleteButton(section)" mat-icon-button>

View File

@ -4,6 +4,7 @@
[prevUrl]="getPrevUrl()"
[nav]="false"
[editMode]="editMotion"
[isSaveButtonEnabled]="contentForm.valid"
(mainEvent)="setEditMode(!editMotion)"
(saveEvent)="saveMotion()"
>
@ -631,6 +632,7 @@
[value]="motionCopy.title"
required
/>
<mat-error>{{ 'The title is required' | translate }}</mat-error>
</mat-form-field>
</div>
</div>
@ -759,7 +761,7 @@
<div class="motion-text" *ngIf="!editMotion"><div [innerHtml]="sanitizedText(motion.reason)"></div></div>
<!-- The HTML Editor -->
<editor formControlName="reason" [init]="tinyMceSettings" *ngIf="editMotion" required></editor>
<editor formControlName="reason" [init]="tinyMceSettings" *ngIf="editMotion" [required]="reasonRequired"></editor>
<div
*ngIf="
reasonRequired &&

View File

@ -143,6 +143,7 @@
<div mat-dialog-actions>
<button
type="submit"
[disabled]="dialogData.value === ''"
mat-button
color="primary"
[mat-dialog-close]="{ action: 'update', value: dialogData.value }"

View File

@ -36,11 +36,11 @@
<div mat-dialog-content>
<p translate>Please enter a name for the new workflow:</p>
<mat-form-field>
<input matInput osAutofocus [(ngModel)]="newWorkflowTitle" />
<input matInput osAutofocus [(ngModel)]="newWorkflowTitle" required/>
</mat-form-field>
</div>
<div mat-dialog-actions>
<button type="submit" mat-button color="primary" [mat-dialog-close]="newWorkflowTitle">
<button type="submit" mat-button color="primary" [disabled]="newWorkflowTitle === ''" [mat-dialog-close]="newWorkflowTitle">
<span translate>Save</span>
</button>
<button type="button" mat-button [mat-dialog-close]="null">

View File

@ -2,6 +2,7 @@
[mainButton]="true"
[nav]="true"
[editMode]="editTag"
[isSaveButtonEnabled]="tagForm.valid"
(mainEvent)="setEditMode(!editTag)"
(saveEvent)="saveTag()"
[multiSelectMode]="isMultiSelect"

View File

@ -4,6 +4,7 @@
[nav]="false"
[goBack]="true"
[editMode]="editTopic"
[isSaveButtonEnabled]="topicForm.valid"
(mainEvent)="setEditMode(!editTopic)"
(saveEvent)="saveTopic()"
>

View File

@ -4,6 +4,7 @@
[nav]="false"
[goBack]="!isAllowed('seeOtherUsers')"
[editMode]="editUser"
[isSaveButtonEnabled]="personalInfoForm.valid"
(mainEvent)="setEditMode(!editUser)"
(saveEvent)="saveUser()"
>