Basic input validation (dynamic table)

This commit is contained in:
Denys Vuika 2016-10-25 11:49:43 +01:00 committed by Vito Albano
parent 3c0ad93477
commit fe7b05df4f
14 changed files with 293 additions and 69 deletions

View File

@ -29,6 +29,7 @@ export class DynamicTableModel extends FormWidgetModel {
rows: DynamicTableRow[] = [];
private _selectedRow: DynamicTableRow;
private _validators: CellValidator[] = [];
get selectedRow(): DynamicTableRow {
return this._selectedRow;
@ -65,6 +66,11 @@ export class DynamicTableModel extends FormWidgetModel {
this.rows = json.value.map(obj => <DynamicTableRow> { selected: false, value: obj });
}
}
this._validators = [
new RequiredCellValidator(),
new NumberCellValidator()
];
}
flushValue() {
@ -112,6 +118,25 @@ export class DynamicTableModel extends FormWidgetModel {
}
}
validateRow(row: DynamicTableRow): DynamicRowValidationSummary {
let summary = <DynamicRowValidationSummary> {
isValid: true,
text: null
};
if (row) {
for (let col of this.columns) {
for (let validator of this._validators) {
if (!validator.validate(row, col, summary)) {
return summary;
}
}
}
}
return summary;
}
getCellValue(row: DynamicTableRow, column: DynamicTableColumn): any {
let result = row.value[column.id];
@ -134,3 +159,90 @@ export class DynamicTableModel extends FormWidgetModel {
return result || '';
}
}
export interface DynamicRowValidationSummary {
isValid: boolean;
text: string;
}
export interface CellValidator {
isSupported(column: DynamicTableColumn): boolean;
validate(row: DynamicTableRow, column: DynamicTableColumn, summary?: DynamicRowValidationSummary): boolean;
}
export class RequiredCellValidator implements CellValidator {
private supportedTypes: string[] = [
'String',
'Number',
'Amount',
'Date',
'Dropdown'
];
isSupported(column: DynamicTableColumn): boolean {
return column && column.required && this.supportedTypes.indexOf(column.type) > -1;
}
validate(row: DynamicTableRow, column: DynamicTableColumn, summary?: DynamicRowValidationSummary): boolean {
if (this.isSupported(column)) {
let value = row.value[column.id];
if (column.required) {
if (value === null || value === undefined || value === '') {
if (summary) {
summary.isValid = false;
summary.text = `Field '${column.name}' is required.`;
}
return false;
}
}
}
return true;
}
}
export class NumberCellValidator implements CellValidator {
private supportedTypes: string[] = [
'Number',
'Amount'
];
isSupported(column: DynamicTableColumn): boolean {
return column && column.required && this.supportedTypes.indexOf(column.type) > -1;
}
isNumber(value: any): boolean {
if (value === null || value === undefined || value === '') {
return false;
}
return !isNaN(+value);
}
validate(row: DynamicTableRow, column: DynamicTableColumn, summary?: DynamicRowValidationSummary): boolean {
if (this.isSupported(column)) {
let value = row.value[column.id];
if (value === null ||
value === undefined ||
value === '' ||
this.isNumber(value)) {
return true;
}
if (summary) {
summary.isValid = false;
summary.text = `Field '${column.name}' must be a number.`;
}
return false;
}
return true;
}
}

View File

@ -11,10 +11,6 @@
width: 100%;
}
.dynamic-table-widget__table-editor {
width: 100%;
}
.dynamic-table-widget__invalid .mdl-textfield__input {
border-color: #d50000;
}

View File

@ -51,49 +51,12 @@
</div>
</div>
<div *ngIf="editMode" class="mdl-shadow--2dp">
<div class="mdl-grid" *ngFor="let column of content.columns">
<div class="mdl-cell mdl-cell--6-col" [ngSwitch]="column.type">
<div *ngSwitchCase="'Dropdown'">
<alf-dropdown-editor
[table]="content"
[row]="editRow"
[column]="column">
</alf-dropdown-editor>
</div>
<div *ngSwitchCase="'Date'">
<alf-date-editor
[table]="content"
[row]="editRow"
[column]="column">
</alf-date-editor>
</div>
<row-editor *ngIf="editMode"
[table]="content"
[row]="editRow"
[column]="column"
(save)="onSaveChanges()"
(cancel)="onCancelChanges()">
</row-editor>
<div *ngSwitchCase="'Boolean'">
<alf-boolean-editor
[table]="content"
[row]="editRow"
[column]="column">
</alf-boolean-editor>
</div>
<div *ngSwitchDefault>
<alf-text-editor
[table]="content"
[row]="editRow"
[column]="column">
</alf-text-editor>
</div>
</div>
</div>
<div>
<button
class="mdl-button mdl-js-button mdl-js-ripple-effect"
(click)="onCancelChanges()">Cancel</button>
<button
class="mdl-button mdl-js-button mdl-js-ripple-effect"
(click)="onSaveChanges()">Save</button>
</div>
</div>
<!--<span *ngIf="content.validationSummary" class="mdl-textfield__error">{{content.validationSummary}}</span>-->
</div>

View File

@ -32,12 +32,14 @@ export class DynamicTableWidget extends WidgetComponent implements OnInit {
editMode: boolean;
editRow: DynamicTableRow;
validationSummary: string;
constructor(private elementRef: ElementRef) {
super();
}
ngOnInit() {
this.validationSummary = 'hello world';
}
onRowClicked(row: DynamicTableRow) {

View File

@ -4,6 +4,8 @@
type="checkbox"
[attr.id]="column.id"
[checked]="table.getCellValue(row, column)"
[required]="column.required"
[disabled]="!column.editable"
(change)="onValueChanged(row, column, $event)">
<span class="mdl-checkbox__label">{{column.name}}</span>
</label>

View File

@ -7,6 +7,8 @@
[value]="table.getCellValue(row, column)"
[attr.id]="column.id"
[readonly]="true"
[required]="column.required"
[disabled]="!column.editable"
(onOk)="onDateSelected($event)">
<label class="mdl-textfield__label" [attr.for]="column.id">{{column.name}} (d-M-yyyy)</label>
</div>

View File

@ -1,9 +1,11 @@
<div>
<div class="dropdown-editor">
<label [attr.for]="column.id">{{column.name}}</label>
<div>
<select
[value]="value"
class="dropdown-editor__select"
[value]="value"
[required]="column.required"
[disabled]="!column.editable"
(change)="onValueChanged(row, column, $event)">
<option></option>
<option *ngFor="let opt of options" [value]="opt.name">{{opt.name}}</option>

View File

@ -36,24 +36,27 @@ export class DropdownEditorComponent extends CellEditorComponent implements OnIn
}
ngOnInit() {
this.value = this.table.getCellValue(this.row, this.column);
this.options = this.column.options || [];
let field = this.table.field;
if (field && field.restUrl) {
this.formService
.getRestFieldValuesColumn(
field.form.taskId,
field.id,
this.column.id
)
.subscribe(
(result: DynamicTableColumnOption[]) => {
this.column.options = result || [];
this.options = this.column.options;
},
err => this.handleError(err)
);
if (field) {
if (this.column.optionType === 'rest') {
this.formService
.getRestFieldValuesColumn(
field.form.taskId,
field.id,
this.column.id
)
.subscribe(
(result: DynamicTableColumnOption[]) => {
this.column.options = result || [];
this.options = this.column.options;
this.value = this.table.getCellValue(this.row, this.column);
},
err => this.handleError(err)
);
} else {
this.options = this.column.options || [];
this.value = this.table.getCellValue(this.row, this.column);
}
}
}

View File

@ -0,0 +1,16 @@
.row-editor {
padding: 8px;
}
.row-editor__validation-summary {
visibility: hidden;
}
.row-editor__invalid .row-editor__validation-summary {
padding-left: 16px;
padding-right: 16px;
padding-top: 8px;
padding-bottom: 8px;
color: #d50000;
visibility: visible;
}

View File

@ -0,0 +1,45 @@
<div class="row-editor mdl-shadow--2dp"
[class.row-editor__invalid]="!validationSummary.isValid">
<div class="mdl-grid" *ngFor="let column of table.columns">
<div class="mdl-cell mdl-cell--6-col" [ngSwitch]="column.type">
<div *ngSwitchCase="'Dropdown'">
<alf-dropdown-editor
[table]="table"
[row]="row"
[column]="column">
</alf-dropdown-editor>
</div>
<div *ngSwitchCase="'Date'">
<alf-date-editor
[table]="table"
[row]="row"
[column]="column">
</alf-date-editor>
</div>
<div *ngSwitchCase="'Boolean'">
<alf-boolean-editor
[table]="table"
[row]="row"
[column]="column">
</alf-boolean-editor>
</div>
<div *ngSwitchDefault>
<alf-text-editor
[table]="table"
[row]="row"
[column]="column">
</alf-text-editor>
</div>
</div>
</div>
<div class="row-editor__validation-summary" *ngIf="!validationSummary.isValid">{{validationSummary.text}}</div>
<div>
<button
class="mdl-button mdl-js-button mdl-js-ripple-effect"
(click)="onCancelChanges()">Cancel</button>
<button
class="mdl-button mdl-js-button mdl-js-ripple-effect"
(click)="onSaveChanges()">Save</button>
</div>
</div>

View File

@ -0,0 +1,73 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { DynamicTableModel, DynamicTableRow, DynamicTableColumn, DynamicRowValidationSummary } from './../../core/index';
@Component({
moduleId: module.id,
selector: 'row-editor',
templateUrl: './row.editor.html',
styleUrls: ['./row.editor.css']
})
export class RowEditorComponent {
@Input()
table: DynamicTableModel;
@Input()
row: DynamicTableRow;
@Input()
column: DynamicTableColumn;
@Output()
save: EventEmitter<any> = new EventEmitter<any>();
@Output()
cancel: EventEmitter<any> = new EventEmitter<any>();
validationSummary: DynamicRowValidationSummary = <DynamicRowValidationSummary> { isValid: true, text: null };
onCancelChanges() {
this.cancel.emit({
table: this.table,
row: this.row,
column: this.column
});
}
onSaveChanges() {
this.validate();
if (this.isValid()) {
this.save.emit({
table: this.table,
row: this.row,
column: this.column
});
}
}
private isValid(): boolean {
return this.validationSummary && this.validationSummary.isValid;
}
private validate() {
this.validationSummary = this.table.validateRow(this.row);
}
}

View File

@ -1,9 +1,11 @@
<div alfresco-mdl-textfield class="dynamic-table-widget__table-editor">
<div alfresco-mdl-textfield class="text-editor">
<input
class="mdl-textfield__input"
type="text"
[value]="table.getCellValue(row, column)"
(keyup)="onValueChanged(row, column, $event)"
[required]="column.required"
[disabled]="!column.editable"
[attr.id]="column.id">
<label class="mdl-textfield__label" [attr.for]="column.id">{{column.name}}</label>
</div>

View File

@ -39,6 +39,7 @@ import { DateEditorComponent } from './dynamic-table/editors/date/date.editor';
import { DropdownEditorComponent } from './dynamic-table/editors/dropdown/dropdown.editor';
import { BooleanEditorComponent } from './dynamic-table/editors/boolean/boolean.editor';
import { TextEditorComponent } from './dynamic-table/editors/text/text.editor';
import { RowEditorComponent } from './dynamic-table/editors/row.editor';
// core
export * from './widget.component';
@ -68,6 +69,7 @@ export * from './amount/amount.widget';
export * from './dynamic-table/dynamic-table.widget';
// editors (dynamic table)
export * from './dynamic-table/editors/row.editor';
export * from './dynamic-table/editors/date/date.editor';
export * from './dynamic-table/editors/dropdown/dropdown.editor';
export * from './dynamic-table/editors/boolean/boolean.editor';
@ -97,5 +99,6 @@ export const WIDGET_DIRECTIVES: any[] = [
DateEditorComponent,
DropdownEditorComponent,
BooleanEditorComponent,
TextEditorComponent
TextEditorComponent,
RowEditorComponent
];