[ADF-1090] support custom dynamic table row validation (#2308)

* support custom dynamic table row validation

* test fixes

* export missing models
This commit is contained in:
Denys Vuika 2017-09-08 07:46:29 +01:00 committed by Eugenio Romano
parent 1f766f3ade
commit a8dfbeb572
13 changed files with 84 additions and 24 deletions

View File

@ -1,8 +1,3 @@
.material-icons {
position: relative;
top: 6px;
}
.user-profile { .user-profile {
margin-right: 10px; margin-right: 10px;
} }

View File

@ -20,7 +20,10 @@ import { AfterViewInit, Component, ElementRef, Input, OnDestroy, OnInit, ViewChi
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Pagination } from 'alfresco-js-api'; import { Pagination } from 'alfresco-js-api';
import { AnalyticsReportListComponent } from 'ng2-activiti-analytics'; import { AnalyticsReportListComponent } from 'ng2-activiti-analytics';
import { FORM_FIELD_VALIDATORS, FormEvent, FormFieldEvent, FormRenderingService, FormService } from 'ng2-activiti-form'; import {
DynamicTableRow, FORM_FIELD_VALIDATORS, FormEvent, FormFieldEvent, FormRenderingService,
FormService, ValidateDynamicTableRowEvent
} from 'ng2-activiti-form';
import { import {
FilterProcessRepresentationModel, FilterProcessRepresentationModel,
ProcessFiltersComponent, ProcessFiltersComponent,
@ -54,7 +57,7 @@ const currentProcessIdNew = '__NEW__';
const currentTaskIdNew = '__NEW__'; const currentTaskIdNew = '__NEW__';
@Component({ @Component({
selector: 'activiti-demo', selector: 'adf-activiti-demo',
templateUrl: './activiti-demo.component.html', templateUrl: './activiti-demo.component.html',
styleUrls: ['./activiti-demo.component.scss'], styleUrls: ['./activiti-demo.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
@ -160,6 +163,17 @@ export class ActivitiDemoComponent implements AfterViewInit, OnDestroy, OnInit {
console.log(`Field value changed. Form: ${e.form.id}, Field: ${e.field.id}, Value: ${e.field.value}`); console.log(`Field value changed. Form: ${e.form.id}, Field: ${e.field.id}, Value: ${e.field.value}`);
}); });
formService.validateDynamicTableRow.subscribe(
(e: ValidateDynamicTableRowEvent) => {
const row: DynamicTableRow = e.row;
if (row && row.value && row.value.name === 'admin') {
e.summary.isValid = false;
e.summary.text = 'Sorry, wrong value. You cannot use "admin".';
e.preventDefault();
}
}
);
// Uncomment this block to see form event handling in action // Uncomment this block to see form event handling in action
/* /*
formService.formEvents.subscribe((event: Event) => { formService.formEvents.subscribe((event: Event) => {

View File

@ -28,28 +28,28 @@
<button md-button <button md-button
[disabled]="!hasSelection()" [disabled]="!hasSelection()"
(click)="moveSelectionUp()"> (click)="moveSelectionUp()">
<i class="material-icons">arrow_upward</i> <md-icon>arrow_upward</md-icon>
</button> </button>
<button md-button <button md-button
[disabled]="!hasSelection()" [disabled]="!hasSelection()"
(click)="moveSelectionDown()"> (click)="moveSelectionDown()">
<i class="material-icons">arrow_downward</i> <md-icon>arrow_downward</md-icon>
</button> </button>
<button md-button <button md-button
[disabled]="field.readOnly" [disabled]="field.readOnly"
id="{{content.id}}-add-row" id="{{content.id}}-add-row"
(click)="addNewRow()"> (click)="addNewRow()">
<i class="material-icons">add_circle_outline</i> <md-icon>add_circle_outline</md-icon>
</button> </button>
<button md-button <button md-button
[disabled]="!hasSelection()" [disabled]="!hasSelection()"
(click)="deleteSelection()"> (click)="deleteSelection()">
<i class="material-icons">remove_circle_outline</i> <md-icon>remove_circle_outline</md-icon>
</button> </button>
<button md-button <button md-button
[disabled]="!hasSelection()" [disabled]="!hasSelection()"
(click)="editSelection()"> (click)="editSelection()">
<i class="material-icons">edit</i> <md-icon>edit</md-icon>
</button> </button>
</div> </div>
</div> </div>

View File

@ -18,6 +18,8 @@
/* tslint:disable:component-selector */ /* tslint:disable:component-selector */
import * as moment from 'moment'; import * as moment from 'moment';
import { ValidateDynamicTableRowEvent } from '../../../events/validate-dynamic-table-row.event';
import { FormService } from './../../../services/form.service';
import { FormFieldModel } from './../core/form-field.model'; import { FormFieldModel } from './../core/form-field.model';
import { FormWidgetModel } from './../core/form-widget.model'; import { FormWidgetModel } from './../core/form-widget.model';
@ -51,7 +53,7 @@ export class DynamicTableModel extends FormWidgetModel {
} }
} }
constructor(field: FormFieldModel) { constructor(field: FormFieldModel, private formService: FormService) {
super(field.form, field.json); super(field.form, field.json);
this.field = field; this.field = field;
@ -136,11 +138,18 @@ export class DynamicTableModel extends FormWidgetModel {
} }
validateRow(row: DynamicTableRow): DynamicRowValidationSummary { validateRow(row: DynamicTableRow): DynamicRowValidationSummary {
let summary = <DynamicRowValidationSummary> { const summary = <DynamicRowValidationSummary> {
isValid: true, isValid: true,
text: null text: null
}; };
const event = new ValidateDynamicTableRowEvent(this.form, this.field, row, summary);
this.formService.validateDynamicTableRow.next(event);
if (event.defaultPrevented || !summary.isValid) {
return summary;
}
if (row) { if (row) {
for (let col of this.columns) { for (let col of this.columns) {
for (let validator of this._validators) { for (let validator of this._validators) {

View File

@ -81,6 +81,7 @@ describe('DynamicTableWidgetComponent', () => {
let element: HTMLElement; let element: HTMLElement;
let table: DynamicTableModel; let table: DynamicTableModel;
let logService: LogService; let logService: LogService;
let formService: FormService;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
@ -104,7 +105,8 @@ describe('DynamicTableWidgetComponent', () => {
beforeEach(() => { beforeEach(() => {
const field = new FormFieldModel(new FormModel()); const field = new FormFieldModel(new FormModel());
logService = TestBed.get(LogService); logService = TestBed.get(LogService);
table = new DynamicTableModel(field); formService = TestBed.get(FormService);
table = new DynamicTableModel(field, formService);
let changeDetectorSpy = jasmine.createSpyObj('cd', ['detectChanges']); let changeDetectorSpy = jasmine.createSpyObj('cd', ['detectChanges']);
let nativeElementSpy = jasmine.createSpyObj('nativeElement', ['querySelector']); let nativeElementSpy = jasmine.createSpyObj('nativeElement', ['querySelector']);
changeDetectorSpy.nativeElement = nativeElementSpy; changeDetectorSpy.nativeElement = nativeElementSpy;
@ -310,7 +312,7 @@ describe('DynamicTableWidgetComponent', () => {
required: true, required: true,
value: null value: null
}); });
widget.content = new DynamicTableModel(field); widget.content = new DynamicTableModel(field, formService);
expect(widget.content.field.validate()).toBeFalsy(); expect(widget.content.field.validate()).toBeFalsy();
expect(widget.isValid()).toBe(widget.content.field.isValid); expect(widget.isValid()).toBe(widget.content.field.isValid);

View File

@ -52,7 +52,7 @@ export class DynamicTableWidgetComponent extends WidgetComponent implements OnIn
ngOnInit() { ngOnInit() {
if (this.field) { if (this.field) {
this.content = new DynamicTableModel(this.field); this.content = new DynamicTableModel(this.field, this.formService);
this.visibilityService.refreshVisibility(this.field.form); this.visibilityService.refreshVisibility(this.field.form);
} }
} }

View File

@ -37,7 +37,7 @@ describe('DateEditorComponent', () => {
row = <DynamicTableRow> { value: { date: '1879-03-14T00:00:00.000Z' } }; row = <DynamicTableRow> { value: { date: '1879-03-14T00:00:00.000Z' } };
column = <DynamicTableColumn> { id: 'date', type: 'Date' }; column = <DynamicTableColumn> { id: 'date', type: 'Date' };
const field = new FormFieldModel(new FormModel()); const field = new FormFieldModel(new FormModel());
table = new DynamicTableModel(field); table = new DynamicTableModel(field, null);
table.rows.push(row); table.rows.push(row);
table.columns.push(column); table.columns.push(column);
component = new DateEditorComponent(new MomentDateAdapter()); component = new DateEditorComponent(new MomentDateAdapter());

View File

@ -53,7 +53,7 @@ describe('DropdownEditorComponent', () => {
}; };
form = new FormModel({taskId: '<task-id>'}); form = new FormModel({taskId: '<task-id>'});
table = new DynamicTableModel(new FormFieldModel(form, {id: '<field-id>'})); table = new DynamicTableModel(new FormFieldModel(form, {id: '<field-id>'}), formService);
table.rows.push(row); table.rows.push(row);
table.columns.push(column); table.columns.push(column);
@ -151,7 +151,7 @@ describe('DropdownEditorComponent', () => {
it('should handle REST error getting option with processDefinitionId', () => { it('should handle REST error getting option with processDefinitionId', () => {
column.optionType = 'rest'; column.optionType = 'rest';
let procForm = new FormModel({processDefinitionId: '<process-definition-id>'}); let procForm = new FormModel({processDefinitionId: '<process-definition-id>'});
let procTable = new DynamicTableModel(new FormFieldModel(procForm, {id: '<field-id>'})); let procTable = new DynamicTableModel(new FormFieldModel(procForm, {id: '<field-id>'}), formService);
component.table = procTable; component.table = procTable;
const error = 'error'; const error = 'error';
@ -223,7 +223,7 @@ describe('DropdownEditorComponent', () => {
] ]
}; };
form = new FormModel({taskId: '<task-id>'}); form = new FormModel({taskId: '<task-id>'});
dynamicTable = new DynamicTableModel(new FormFieldModel(form, {id: '<field-id>'})); dynamicTable = new DynamicTableModel(new FormFieldModel(form, {id: '<field-id>'}), formService);
dynamicTable.rows.push(row); dynamicTable.rows.push(row);
dynamicTable.columns.push(column); dynamicTable.columns.push(column);
dropDownEditorComponent.table = dynamicTable; dropDownEditorComponent.table = dynamicTable;
@ -271,7 +271,7 @@ describe('DropdownEditorComponent', () => {
] ]
}; };
form = new FormModel({processDefinitionId: '<proc-id>'}); form = new FormModel({processDefinitionId: '<proc-id>'});
dynamicTable = new DynamicTableModel(new FormFieldModel(form, {id: '<field-id>'})); dynamicTable = new DynamicTableModel(new FormFieldModel(form, {id: '<field-id>'}), formService);
dynamicTable.rows.push(row); dynamicTable.rows.push(row);
dynamicTable.columns.push(column); dynamicTable.columns.push(column);
dropDownEditorComponent.table = dynamicTable; dropDownEditorComponent.table = dynamicTable;

View File

@ -16,6 +16,7 @@
*/ */
import { FormFieldModel, FormModel } from '../../index'; import { FormFieldModel, FormModel } from '../../index';
import { FormService } from './../../../../services/form.service';
import { DynamicRowValidationSummary, DynamicTableColumn, DynamicTableModel, DynamicTableRow } from './../dynamic-table.widget.model'; import { DynamicRowValidationSummary, DynamicTableColumn, DynamicTableModel, DynamicTableRow } from './../dynamic-table.widget.model';
import { RowEditorComponent } from './row.editor'; import { RowEditorComponent } from './row.editor';
@ -26,7 +27,7 @@ describe('RowEditorComponent', () => {
beforeEach(() => { beforeEach(() => {
component = new RowEditorComponent(); component = new RowEditorComponent();
const field = new FormFieldModel(new FormModel()); const field = new FormFieldModel(new FormModel());
component.table = new DynamicTableModel(field); component.table = new DynamicTableModel(field, new FormService(null, null, null));
component.row = <DynamicTableRow> {}; component.row = <DynamicTableRow> {};
component.column = <DynamicTableColumn> {}; component.column = <DynamicTableColumn> {};
}); });

View File

@ -74,6 +74,7 @@ export * from './error/error.component';
export { DocumentWidgetComponent } from './document/document.widget'; export { DocumentWidgetComponent } from './document/document.widget';
// editors (dynamic table) // editors (dynamic table)
export * from './dynamic-table/dynamic-table.widget.model';
export * from './dynamic-table/editors/row.editor'; export * from './dynamic-table/editors/row.editor';
export * from './dynamic-table/editors/date/date.editor'; export * from './dynamic-table/editors/date/date.editor';
export * from './dynamic-table/editors/dropdown/dropdown.editor'; export * from './dynamic-table/editors/dropdown/dropdown.editor';

View File

@ -20,3 +20,4 @@ export { FormErrorEvent } from './form-error.event';
export { FormFieldEvent } from './form-field.event'; export { FormFieldEvent } from './form-field.event';
export { ValidateFormFieldEvent } from './validate-form-field.event'; export { ValidateFormFieldEvent } from './validate-form-field.event';
export { ValidateFormEvent } from './validate-form.event'; export { ValidateFormEvent } from './validate-form.event';
export { ValidateDynamicTableRowEvent } from './validate-dynamic-table-row.event';

View File

@ -0,0 +1,33 @@
/*!
* @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 { DynamicRowValidationSummary, DynamicTableRow } from '../components/widgets/dynamic-table/dynamic-table.widget.model';
import { FormFieldModel, FormModel } from './../components/widgets/core/index';
import { FormFieldEvent } from './form-field.event';
export class ValidateDynamicTableRowEvent extends FormFieldEvent {
isValid = true;
constructor(form: FormModel,
field: FormFieldModel,
public row: DynamicTableRow,
public summary: DynamicRowValidationSummary) {
super(form, field);
}
}

View File

@ -23,7 +23,10 @@ import { ContentLinkModel } from './../components/widgets/core/content-link.mode
import { GroupUserModel } from './../components/widgets/core/group-user.model'; import { GroupUserModel } from './../components/widgets/core/group-user.model';
import { GroupModel } from './../components/widgets/core/group.model'; import { GroupModel } from './../components/widgets/core/group.model';
import { FormModel, FormOutcomeEvent, FormOutcomeModel, FormValues } from './../components/widgets/core/index'; import { FormModel, FormOutcomeEvent, FormOutcomeModel, FormValues } from './../components/widgets/core/index';
import { FormErrorEvent, FormEvent, FormFieldEvent, ValidateFormEvent, ValidateFormFieldEvent } from './../events/index'; import {
FormErrorEvent, FormEvent, FormFieldEvent,
ValidateDynamicTableRowEvent, ValidateFormEvent, ValidateFormFieldEvent
} from './../events/index';
import { EcmModelService } from './ecm-model.service'; import { EcmModelService } from './ecm-model.service';
@Injectable() @Injectable()
@ -44,6 +47,7 @@ export class FormService {
validateForm = new Subject<ValidateFormEvent>(); validateForm = new Subject<ValidateFormEvent>();
validateFormField = new Subject<ValidateFormFieldEvent>(); validateFormField = new Subject<ValidateFormFieldEvent>();
validateDynamicTableRow = new Subject<ValidateDynamicTableRowEvent>();
executeOutcome = new Subject<FormOutcomeEvent>(); executeOutcome = new Subject<FormOutcomeEvent>();