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>
|
||||
|
||||
<!-- 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>
|
||||
|
@ -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
|
||||
*/
|
||||
|
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 -->
|
||||
<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>
|
||||
|
@ -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: ['']
|
||||
});
|
||||
|
@ -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 -->
|
||||
|
@ -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: ['']
|
||||
|
@ -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">
|
||||
–
|
||||
</ng-container>
|
||||
<os-icon-container icon="visibility">
|
||||
{{ section.read_groups }}
|
||||
<ng-container *ngIf="section.read_groups.length === 0">
|
||||
–
|
||||
</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">
|
||||
–
|
||||
</ng-container>
|
||||
<os-icon-container icon="edit">
|
||||
{{ section.write_groups }}
|
||||
<ng-container *ngIf="section.write_groups.length === 0">
|
||||
–
|
||||
</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>
|
||||
|
@ -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 &&
|
||||
|
@ -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 }"
|
||||
|
@ -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">
|
||||
|
@ -2,6 +2,7 @@
|
||||
[mainButton]="true"
|
||||
[nav]="true"
|
||||
[editMode]="editTag"
|
||||
[isSaveButtonEnabled]="tagForm.valid"
|
||||
(mainEvent)="setEditMode(!editTag)"
|
||||
(saveEvent)="saveTag()"
|
||||
[multiSelectMode]="isMultiSelect"
|
||||
|
@ -4,6 +4,7 @@
|
||||
[nav]="false"
|
||||
[goBack]="true"
|
||||
[editMode]="editTopic"
|
||||
[isSaveButtonEnabled]="topicForm.valid"
|
||||
(mainEvent)="setEditMode(!editTopic)"
|
||||
(saveEvent)="saveTopic()"
|
||||
>
|
||||
|
@ -4,6 +4,7 @@
|
||||
[nav]="false"
|
||||
[goBack]="!isAllowed('seeOtherUsers')"
|
||||
[editMode]="editUser"
|
||||
[isSaveButtonEnabled]="personalInfoForm.valid"
|
||||
(mainEvent)="setEditMode(!editUser)"
|
||||
(saveEvent)="saveUser()"
|
||||
>
|
||||
|
Loading…
Reference in New Issue
Block a user