mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
#726 basic form validation support
- initial validation workflow - validation for Required field (text, multiline text widgets) - disable form outcomes on failed validation
This commit is contained in:
@@ -4,8 +4,9 @@
|
||||
</div>
|
||||
<div *ngIf="hasForm()">
|
||||
<div class="mdl-card mdl-shadow--2dp activiti-form-container">
|
||||
<div *ngIf="isTitleEnabled()" class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">{{form.taskName}}</h2>
|
||||
<div class="mdl-card__title">
|
||||
<i class="material-icons">{{ form.isValid ? 'event_available' : 'event_busy' }}</i>
|
||||
<h2 *ngIf="isTitleEnabled()" class="mdl-card__title-text">{{form.taskName}}</h2>
|
||||
</div>
|
||||
<div class="mdl-card__media">
|
||||
<div *ngIf="form.hasTabs()">
|
||||
@@ -19,9 +20,9 @@
|
||||
<div *ngIf="form.hasOutcomes()" class="mdl-card__actions mdl-card--border">
|
||||
<button *ngFor="let outcome of form.outcomes"
|
||||
alfresco-mdl-button
|
||||
[disabled]="readOnly"
|
||||
[disabled]="!isOutcomeButtonEnabled(outcome)"
|
||||
[class.mdl-button--colored]="!outcome.isSystem"
|
||||
[class.activiti-form-hide-button]="!isOutcomeButtonEnabled(outcome)"
|
||||
[class.activiti-form-hide-button]="!isOutcomeButtonVisible(outcome)"
|
||||
(click)="onOutcomeClicked(outcome, $event)">
|
||||
{{outcome.name}}
|
||||
</button>
|
||||
|
@@ -98,13 +98,13 @@ describe('ActivitiForm', () => {
|
||||
});
|
||||
|
||||
it('should not enable outcome button when model missing', () => {
|
||||
expect(formComponent.isOutcomeButtonEnabled(null)).toBeFalsy();
|
||||
expect(formComponent.isOutcomeButtonVisible(null)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should enable custom outcome buttons', () => {
|
||||
let formModel = new FormModel();
|
||||
let outcome = new FormOutcomeModel(formModel, { id: 'action1', name: 'Action 1' });
|
||||
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeTruthy();
|
||||
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
@@ -113,10 +113,10 @@ describe('ActivitiForm', () => {
|
||||
let outcome = new FormOutcomeModel(formModel, { id: '$save', name: FormOutcomeModel.SAVE_ACTION });
|
||||
|
||||
formComponent.showSaveButton = true;
|
||||
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeTruthy();
|
||||
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeTruthy();
|
||||
|
||||
formComponent.showSaveButton = false;
|
||||
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeFalsy();
|
||||
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should allow controlling [save] button visibility', () => {
|
||||
@@ -124,10 +124,10 @@ describe('ActivitiForm', () => {
|
||||
let outcome = new FormOutcomeModel(formModel, { id: '$save', name: FormOutcomeModel.COMPLETE_ACTION });
|
||||
|
||||
formComponent.showCompleteButton = true;
|
||||
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeTruthy();
|
||||
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeTruthy();
|
||||
|
||||
formComponent.showCompleteButton = false;
|
||||
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeFalsy();
|
||||
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should load form on refresh', () => {
|
||||
|
@@ -128,7 +128,7 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
|
||||
showSaveButton: boolean = true;
|
||||
|
||||
@Input()
|
||||
showDebugButton: boolean = false;
|
||||
showDebugButton: boolean = true;
|
||||
|
||||
@Input()
|
||||
readOnly: boolean = false;
|
||||
@@ -175,6 +175,21 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
|
||||
}
|
||||
|
||||
isOutcomeButtonEnabled(outcome: FormOutcomeModel): boolean {
|
||||
if (this.form.readOnly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outcome) {
|
||||
// Make 'Save' button always available
|
||||
if (outcome.name === FormOutcomeModel.SAVE_ACTION) {
|
||||
return true;
|
||||
}
|
||||
return this.form.isValid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isOutcomeButtonVisible(outcome: FormOutcomeModel): boolean {
|
||||
if (outcome && outcome.name) {
|
||||
if (outcome.name === FormOutcomeModel.COMPLETE_ACTION) {
|
||||
return this.showCompleteButton;
|
||||
|
@@ -65,6 +65,17 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
return this._readOnly;
|
||||
}
|
||||
|
||||
isValid(): boolean {
|
||||
|
||||
if (this.required) {
|
||||
if (this.type === FormFieldTypes.TEXT || this.type === FormFieldTypes.MULTILINE_TEXT) {
|
||||
return this._value ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constructor(form: FormModel, json?: any) {
|
||||
super(form, json);
|
||||
|
||||
@@ -129,7 +140,6 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
}
|
||||
|
||||
updateForm() {
|
||||
|
||||
switch (this.type) {
|
||||
case FormFieldTypes.DROPDOWN:
|
||||
/*
|
||||
@@ -177,5 +187,7 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
this.form.values[this.id] = this.value;
|
||||
}
|
||||
}
|
||||
|
||||
this.form.onFormFieldChanged(this);
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import { FormValues } from './form-values';
|
||||
import { ContainerModel } from './container.model';
|
||||
import { TabModel } from './tab.model';
|
||||
import { FormOutcomeModel } from './form-outcome.model';
|
||||
import { FormFieldModel } from './form-field.model';
|
||||
|
||||
export class FormModel {
|
||||
|
||||
@@ -31,6 +32,7 @@ export class FormModel {
|
||||
private _name: string;
|
||||
private _taskId: string;
|
||||
private _taskName: string = FormModel.UNSET_TASK_NAME;
|
||||
private _isValid: boolean = true;
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
@@ -48,6 +50,10 @@ export class FormModel {
|
||||
return this._taskName;
|
||||
}
|
||||
|
||||
get isValid(): boolean {
|
||||
return this._isValid;
|
||||
}
|
||||
|
||||
readOnly: boolean = false;
|
||||
tabs: TabModel[] = [];
|
||||
fields: ContainerModel[] = [];
|
||||
@@ -102,7 +108,8 @@ export class FormModel {
|
||||
if (field.tab) {
|
||||
let tab = tabCache[field.tab];
|
||||
if (tab) {
|
||||
tab.fields.push(new ContainerModel(this, field.json));
|
||||
// tab.fields.push(new ContainerModel(this, field.json));
|
||||
tab.fields.push(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,6 +126,48 @@ export class FormModel {
|
||||
}
|
||||
}
|
||||
|
||||
onFormFieldChanged(field: FormFieldModel) {
|
||||
this.validateField(field);
|
||||
}
|
||||
|
||||
// TODO: evaluate and cache once the form is loaded
|
||||
private getFormFields(): FormFieldModel[] {
|
||||
let result: FormFieldModel[] = [];
|
||||
|
||||
for (let i = 0; i < this.fields.length; i++) {
|
||||
let container = this.fields[i];
|
||||
for (let j = 0; j < container.columns.length; j++) {
|
||||
let column = container.columns[j];
|
||||
for (let k = 0; k < column.fields.length; k++) {
|
||||
let field = column.fields[k];
|
||||
result.push(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private validateForm() {
|
||||
this._isValid = true;
|
||||
let fields = this.getFormFields();
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
if (!fields[i].isValid()) {
|
||||
this._isValid = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private validateField(field: FormFieldModel) {
|
||||
if (!field) return;
|
||||
if (!field.isValid()) {
|
||||
this._isValid = false;
|
||||
return;
|
||||
}
|
||||
this.validateForm();
|
||||
}
|
||||
|
||||
private parseContainerFields(json: any): ContainerModel[] {
|
||||
let fields = [];
|
||||
|
||||
|
@@ -3,6 +3,7 @@
|
||||
type="text"
|
||||
rows= "3"
|
||||
[attr.id]="field.id"
|
||||
[attr.required]="isRequired()"
|
||||
[(ngModel)]="field.value"
|
||||
(ngModelChange)="checkVisibility(field)"
|
||||
[disabled]="field.readOnly">
|
||||
|
@@ -2,6 +2,7 @@
|
||||
<input class="mdl-textfield__input"
|
||||
type="text"
|
||||
[attr.id]="field.id"
|
||||
[attr.required]="isRequired()"
|
||||
[(ngModel)]="field.value"
|
||||
(ngModelChange)="checkVisibility(field)"
|
||||
[disabled]="field.readOnly">
|
||||
|
@@ -36,6 +36,13 @@ export class WidgetComponent implements AfterViewInit {
|
||||
return this.field ? true : false;
|
||||
}
|
||||
|
||||
isRequired(): any {
|
||||
if (this.field && this.field.required) {
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.setupMaterialComponents();
|
||||
this.fieldChanged.emit(this.field);
|
||||
|
Reference in New Issue
Block a user