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:
parent
51130da791
commit
4e0e641002
@ -49,7 +49,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Save 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 -->
|
<!-- Menu button slot -->
|
||||||
<ng-content *ngIf="!editMode" select=".menu-slot"></ng-content>
|
<ng-content *ngIf="!editMode" select=".menu-slot"></ng-content>
|
||||||
|
@ -71,6 +71,12 @@ export class HeadBarComponent {
|
|||||||
@Input()
|
@Input()
|
||||||
public editMode = false;
|
public editMode = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The save button can manually be disabled.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
public isSaveButtonEnabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine multiSelect mode: changed interactions and head bar
|
* Determine multiSelect mode: changed interactions and head bar
|
||||||
*/
|
*/
|
||||||
|
19
client/src/app/shared/validators/custom-validators.ts
Normal file
19
client/src/app/shared/validators/custom-validators.ts
Normal 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 };
|
||||||
|
};
|
@ -13,6 +13,7 @@
|
|||||||
<!-- Duration -->
|
<!-- Duration -->
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input type="string" matInput placeholder="{{ 'Duration' | translate }}" formControlName="durationText" />
|
<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>
|
</mat-form-field>
|
||||||
|
|
||||||
<!-- Item number (prefix) -->
|
<!-- Item number (prefix) -->
|
||||||
@ -34,6 +35,6 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions>
|
<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>
|
<button mat-button (click)="onCancelButton()"><span translate>Cancel</span></button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,6 +5,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|||||||
import { ViewItem } from '../../models/view-item';
|
import { ViewItem } from '../../models/view-item';
|
||||||
import { ItemVisibilityChoices } from 'app/shared/models/agenda/item';
|
import { ItemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||||
|
import { durationValidator } from 'app/shared/validators/custom-validators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog component to change agenda item details
|
* Dialog component to change agenda item details
|
||||||
@ -42,7 +43,7 @@ export class ItemInfoDialogComponent {
|
|||||||
) {
|
) {
|
||||||
this.agendaInfoForm = this.formBuilder.group({
|
this.agendaInfoForm = this.formBuilder.group({
|
||||||
type: [''],
|
type: [''],
|
||||||
durationText: [''],
|
durationText: ['', durationValidator],
|
||||||
item_number: [''],
|
item_number: [''],
|
||||||
comment: ['']
|
comment: ['']
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
mainButtonIcon="edit"
|
mainButtonIcon="edit"
|
||||||
[nav]="false"
|
[nav]="false"
|
||||||
[editMode]="editAssignment"
|
[editMode]="editAssignment"
|
||||||
|
[isSaveButtonEnabled]="assignmentForm.valid"
|
||||||
(mainEvent)="setEditMode(!editAssignment)"
|
(mainEvent)="setEditMode(!editAssignment)"
|
||||||
(saveEvent)="saveAssignment()"
|
(saveEvent)="saveAssignment()"
|
||||||
>
|
>
|
||||||
@ -242,16 +243,16 @@
|
|||||||
formControlName="title"
|
formControlName="title"
|
||||||
[value]="assignmentCopy.title || ''"
|
[value]="assignmentCopy.title || ''"
|
||||||
/>
|
/>
|
||||||
|
<mat-error>{{ 'The title is required' | translate }}</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<h4>{{ 'Description' | translate }}:</h4>
|
|
||||||
|
|
||||||
<!-- description: HTML Editor -->
|
<!-- description: HTML Editor -->
|
||||||
|
<h4>{{ 'Description' | translate }}:</h4>
|
||||||
<editor
|
<editor
|
||||||
formControlName="description"
|
formControlName="description"
|
||||||
[init]="tinyMceSettings"
|
[init]="tinyMceSettings"
|
||||||
*ngIf="assignment && editAssignment"
|
*ngIf="assignment && editAssignment"
|
||||||
required
|
|
||||||
></editor>
|
></editor>
|
||||||
|
|
||||||
<!-- searchValueSelector: tags -->
|
<!-- searchValueSelector: tags -->
|
||||||
@ -292,8 +293,11 @@
|
|||||||
matInput
|
matInput
|
||||||
placeholder="{{ 'Number of persons to be elected' | translate }}"
|
placeholder="{{ 'Number of persons to be elected' | translate }}"
|
||||||
formControlName="open_posts"
|
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>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO searchValueSelector: Parent -->
|
<!-- TODO searchValueSelector: Parent -->
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material';
|
||||||
import { Router, ActivatedRoute } from '@angular/router';
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
import { Title, DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { Title, DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
|
|
||||||
@ -196,10 +196,10 @@ export class AssignmentDetailComponent extends BaseViewComponent implements OnIn
|
|||||||
phase: null,
|
phase: null,
|
||||||
tags_id: [],
|
tags_id: [],
|
||||||
attachments_id: [],
|
attachments_id: [],
|
||||||
title: '',
|
title: ['', Validators.required],
|
||||||
description: '',
|
description: [''],
|
||||||
poll_description_default: '',
|
poll_description_default: [''],
|
||||||
open_posts: 0,
|
open_posts: [1, [Validators.required, Validators.min(1)]],
|
||||||
agenda_create: [''],
|
agenda_create: [''],
|
||||||
agenda_parent_id: [],
|
agenda_parent_id: [],
|
||||||
agenda_type: ['']
|
agenda_type: ['']
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
<p>
|
<p>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
|
<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>
|
<span translate>Required</span>
|
||||||
</mat-hint>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@ -39,7 +39,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<button mat-button (click)="create()">
|
<button [disabled]="createForm.invalid" mat-button (click)="create()">
|
||||||
<span translate>Create</span>
|
<span translate>Create</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-button (click)="commentSectionToCreate = null">
|
<button mat-button (click)="commentSectionToCreate = null">
|
||||||
@ -62,18 +62,20 @@
|
|||||||
{{ section.name }}
|
{{ section.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="read">
|
<div class="read">
|
||||||
<mat-icon>visibility</mat-icon>
|
<os-icon-container icon="visibility">
|
||||||
{{ section.read_groups }}
|
{{ section.read_groups }}
|
||||||
<ng-container *ngIf="section.read_groups.length === 0">
|
<ng-container *ngIf="section.read_groups.length === 0">
|
||||||
–
|
–
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
</os-icon-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="write">
|
<div class="write">
|
||||||
<mat-icon>edit</mat-icon>
|
<os-icon-container icon="edit">
|
||||||
{{ section.write_groups }}
|
{{ section.write_groups }}
|
||||||
<ng-container *ngIf="section.write_groups.length === 0">
|
<ng-container *ngIf="section.write_groups.length === 0">
|
||||||
–
|
–
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
</os-icon-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
@ -83,9 +85,9 @@
|
|||||||
<p>
|
<p>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
|
<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>
|
<span translate>Required</span>
|
||||||
</mat-hint>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@ -128,7 +130,7 @@
|
|||||||
<button *ngIf="editId === section.id" mat-button (click)="editId = null" mat-icon-button>
|
<button *ngIf="editId === section.id" mat-button (click)="editId = null" mat-icon-button>
|
||||||
<mat-icon>cancel</mat-icon>
|
<mat-icon>cancel</mat-icon>
|
||||||
</button>
|
</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>
|
<mat-icon>save</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-button (click)="onDeleteButton(section)" mat-icon-button>
|
<button mat-button (click)="onDeleteButton(section)" mat-icon-button>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
[prevUrl]="getPrevUrl()"
|
[prevUrl]="getPrevUrl()"
|
||||||
[nav]="false"
|
[nav]="false"
|
||||||
[editMode]="editMotion"
|
[editMode]="editMotion"
|
||||||
|
[isSaveButtonEnabled]="contentForm.valid"
|
||||||
(mainEvent)="setEditMode(!editMotion)"
|
(mainEvent)="setEditMode(!editMotion)"
|
||||||
(saveEvent)="saveMotion()"
|
(saveEvent)="saveMotion()"
|
||||||
>
|
>
|
||||||
@ -631,6 +632,7 @@
|
|||||||
[value]="motionCopy.title"
|
[value]="motionCopy.title"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
<mat-error>{{ 'The title is required' | translate }}</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -759,7 +761,7 @@
|
|||||||
<div class="motion-text" *ngIf="!editMotion"><div [innerHtml]="sanitizedText(motion.reason)"></div></div>
|
<div class="motion-text" *ngIf="!editMotion"><div [innerHtml]="sanitizedText(motion.reason)"></div></div>
|
||||||
|
|
||||||
<!-- The HTML Editor -->
|
<!-- The HTML Editor -->
|
||||||
<editor formControlName="reason" [init]="tinyMceSettings" *ngIf="editMotion" required></editor>
|
<editor formControlName="reason" [init]="tinyMceSettings" *ngIf="editMotion" [required]="reasonRequired"></editor>
|
||||||
<div
|
<div
|
||||||
*ngIf="
|
*ngIf="
|
||||||
reasonRequired &&
|
reasonRequired &&
|
||||||
|
@ -143,6 +143,7 @@
|
|||||||
<div mat-dialog-actions>
|
<div mat-dialog-actions>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
[disabled]="dialogData.value === ''"
|
||||||
mat-button
|
mat-button
|
||||||
color="primary"
|
color="primary"
|
||||||
[mat-dialog-close]="{ action: 'update', value: dialogData.value }"
|
[mat-dialog-close]="{ action: 'update', value: dialogData.value }"
|
||||||
|
@ -36,11 +36,11 @@
|
|||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
<p translate>Please enter a name for the new workflow:</p>
|
<p translate>Please enter a name for the new workflow:</p>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input matInput osAutofocus [(ngModel)]="newWorkflowTitle" />
|
<input matInput osAutofocus [(ngModel)]="newWorkflowTitle" required/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions>
|
<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>
|
<span translate>Save</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" mat-button [mat-dialog-close]="null">
|
<button type="button" mat-button [mat-dialog-close]="null">
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
[mainButton]="true"
|
[mainButton]="true"
|
||||||
[nav]="true"
|
[nav]="true"
|
||||||
[editMode]="editTag"
|
[editMode]="editTag"
|
||||||
|
[isSaveButtonEnabled]="tagForm.valid"
|
||||||
(mainEvent)="setEditMode(!editTag)"
|
(mainEvent)="setEditMode(!editTag)"
|
||||||
(saveEvent)="saveTag()"
|
(saveEvent)="saveTag()"
|
||||||
[multiSelectMode]="isMultiSelect"
|
[multiSelectMode]="isMultiSelect"
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
[nav]="false"
|
[nav]="false"
|
||||||
[goBack]="true"
|
[goBack]="true"
|
||||||
[editMode]="editTopic"
|
[editMode]="editTopic"
|
||||||
|
[isSaveButtonEnabled]="topicForm.valid"
|
||||||
(mainEvent)="setEditMode(!editTopic)"
|
(mainEvent)="setEditMode(!editTopic)"
|
||||||
(saveEvent)="saveTopic()"
|
(saveEvent)="saveTopic()"
|
||||||
>
|
>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
[nav]="false"
|
[nav]="false"
|
||||||
[goBack]="!isAllowed('seeOtherUsers')"
|
[goBack]="!isAllowed('seeOtherUsers')"
|
||||||
[editMode]="editUser"
|
[editMode]="editUser"
|
||||||
|
[isSaveButtonEnabled]="personalInfoForm.valid"
|
||||||
(mainEvent)="setEditMode(!editUser)"
|
(mainEvent)="setEditMode(!editUser)"
|
||||||
(saveEvent)="saveUser()"
|
(saveEvent)="saveUser()"
|
||||||
>
|
>
|
||||||
|
Loading…
Reference in New Issue
Block a user