#726 dropdown and typeahead validation

fixes #743
This commit is contained in:
Denys Vuika
2016-09-14 18:01:53 +01:00
parent 1adc87bc67
commit e99b48a6da
10 changed files with 96 additions and 27 deletions

View File

@@ -30,7 +30,9 @@ export class RequiredFieldValidator implements FormFieldValidator {
private supportedTypes = [ private supportedTypes = [
FormFieldTypes.TEXT, FormFieldTypes.TEXT,
FormFieldTypes.MULTILINE_TEXT, FormFieldTypes.MULTILINE_TEXT,
FormFieldTypes.NUMBER FormFieldTypes.NUMBER,
FormFieldTypes.TYPEAHEAD,
FormFieldTypes.DROPDOWN
]; ];
isSupported(field: FormFieldModel): boolean { isSupported(field: FormFieldModel): boolean {
@@ -41,6 +43,15 @@ export class RequiredFieldValidator implements FormFieldValidator {
validate(field: FormFieldModel): boolean { validate(field: FormFieldModel): boolean {
if (this.isSupported(field)) { if (this.isSupported(field)) {
if (field.type === FormFieldTypes.DROPDOWN) {
if (field.hasEmptyValue && field.emptyOption) {
if (field.value === field.emptyOption.id) {
return false;
}
}
}
if (!field.value) { if (!field.value) {
return false; return false;
} }

View File

@@ -66,6 +66,7 @@ export class FormFieldModel extends FormWidgetModel {
isVisible: boolean = true; isVisible: boolean = true;
visibilityCondition: WidgetVisibilityModel = null; visibilityCondition: WidgetVisibilityModel = null;
emptyOption: FormFieldOption;
validationSummary: string; validationSummary: string;
validators: FormFieldValidator[] = []; validators: FormFieldValidator[] = [];
@@ -137,9 +138,11 @@ export class FormFieldModel extends FormWidgetModel {
this.hyperlinkUrl = json.hyperlinkUrl; this.hyperlinkUrl = json.hyperlinkUrl;
this.displayText = json.displayText; this.displayText = json.displayText;
this.visibilityCondition = <WidgetVisibilityModel> json.visibilityCondition; this.visibilityCondition = <WidgetVisibilityModel> json.visibilityCondition;
this._value = this.parseValue(json); this._value = this.parseValue(json);
this.updateForm(); }
if (this.hasEmptyValue && this.options && this.options.length > 0) {
this.emptyOption = this.options[0];
} }
this.validators = [ this.validators = [
@@ -151,6 +154,8 @@ export class FormFieldModel extends FormWidgetModel {
new MaxValueFieldValidator(), new MaxValueFieldValidator(),
new RegExFieldValidator() new RegExFieldValidator()
]; ];
this.updateForm();
} }
parseValue(json: any): any { parseValue(json: any): any {
@@ -160,10 +165,15 @@ export class FormFieldModel extends FormWidgetModel {
This is needed due to Activiti issue related to reading dropdown values as value string This is needed due to Activiti issue related to reading dropdown values as value string
but saving back as object: { id: <id>, name: <name> } but saving back as object: { id: <id>, name: <name> }
*/ */
// TODO: needs review
if (json.type === FormFieldTypes.DROPDOWN) { if (json.type === FormFieldTypes.DROPDOWN) {
if (value === '') { if (json.hasEmptyValue && json.options) {
value = 'empty'; let options = <FormFieldOption[]> json.options || [];
if (options.length > 0) {
let emptyOption = json.options[0];
if (value === '' || value === emptyOption.id || value === emptyOption.name) {
value = emptyOption.id;
}
}
} }
} }

View File

@@ -2,6 +2,22 @@
width: 100%; width: 100%;
} }
.dropdown-widget > select { .dropdown-widget__select {
width: 100%; width: 100%;
} }
.dropdown-widget__invalid .dropdown-widget__select {
border-color: #d50000;
}
.dropdown-widget__invalid .dropdown-widget__label {
color: #d50000;
}
.dropdown-widget__invalid .dropdown-widget__label:after {
background-color: #d50000;
}
.dropdown-widget__invalid .mdl-textfield__error {
visibility: visible !important;
}

View File

@@ -1,6 +1,10 @@
<div class="dropdown-widget"> <div class="dropdown-widget"
<label [attr.for]="field.id">{{field.name}}</label> [class.dropdown-widget__invalid]="!field.isValid">
<select [(ngModel)]="field.value" (ngModelChange)="checkVisibility(field)"> <label class="dropdown-widget__label" [attr.for]="field.id">{{field.name}}</label>
<select class="dropdown-widget__select"
[(ngModel)]="field.value"
(ngModelChange)="checkVisibility(field)">
<option *ngFor="let opt of field.options" [value]="opt.id">{{opt.name}}</option> <option *ngFor="let opt of field.options" [value]="opt.id">{{opt.name}}</option>
</select> </select>
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
</div> </div>

View File

@@ -42,7 +42,8 @@ describe('DropdownWidget', () => {
}); });
widget.field = new FormFieldModel(form, { widget.field = new FormFieldModel(form, {
id: fieldId id: fieldId,
restUrl: '<url>'
}); });
spyOn(formService, 'getRestFieldValues').and.returnValue(Observable.create(observer => { spyOn(formService, 'getRestFieldValues').and.returnValue(Observable.create(observer => {

View File

@@ -36,18 +36,24 @@ export class DropdownWidget extends WidgetComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
this.formService if (this.field && this.field.restUrl) {
.getRestFieldValues( this.formService
this.field.form.taskId, .getRestFieldValues(
this.field.id this.field.form.taskId,
) this.field.id
.subscribe( )
(result: FormFieldOption[]) => { .subscribe(
this.field.options = result || []; (result: FormFieldOption[]) => {
this.field.updateForm(); let options = [];
}, if (this.field.emptyOption) {
this.handleError options.push(this.field.emptyOption);
); }
this.field.options = options.concat((result || []));
this.field.updateForm();
},
this.handleError
);
}
} }
handleError(error: any) { handleError(error: any) {

View File

@@ -9,6 +9,5 @@
(ngModelChange)="checkVisibility(field)" (ngModelChange)="checkVisibility(field)"
[disabled]="field.readOnly"> [disabled]="field.readOnly">
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label> <label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
<!--<span class="mdl-textfield__error">Input is not a number!</span>-->
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span> <span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
</div> </div>

View File

@@ -27,3 +27,21 @@
.typeahead-autocomplete > ul > li { .typeahead-autocomplete > ul > li {
opacity: 1; opacity: 1;
} }
.typeahead-widget__invalid .mdl-textfield__input {
border-color: #d50000;
}
.typeahead-widget__invalid .mdl-textfield__label {
color: #d50000;
}
.typeahead-widget__invalid .mdl-textfield__label:after {
background-color: #d50000;
}
.typeahead-widget__invalid .mdl-textfield__error {
visibility: visible !important;
}

View File

@@ -1,5 +1,6 @@
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label typeahead-widget" <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label typeahead-widget"
[class.is-dirty]="value"> [class.is-dirty]="value"
[class.typeahead-widget__invalid]="!field.isValid">
<input class="mdl-textfield__input" <input class="mdl-textfield__input"
type="text" type="text"
[attr.id]="field.id" [attr.id]="field.id"
@@ -9,8 +10,8 @@
(blur)="onBlur()" (blur)="onBlur()"
[disabled]="field.readOnly"> [disabled]="field.readOnly">
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label> <label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
</div> </div>
<div class="typeahead-autocomplete mdl-shadow--2dp" *ngIf="options.length > 0 && popupVisible"> <div class="typeahead-autocomplete mdl-shadow--2dp" *ngIf="options.length > 0 && popupVisible">
<ul> <ul>
<li *ngFor="let item of options" <li *ngFor="let item of options"

View File

@@ -91,7 +91,9 @@ export class TypeaheadWidget extends WidgetComponent implements OnInit {
this.popupVisible = false; this.popupVisible = false;
let options = this.field.options || []; let options = this.field.options || [];
let field = options.find(item => item.name.toLocaleLowerCase() === this.value.toLocaleLowerCase()); let lValue = this.value ? this.value.toLocaleLowerCase() : null;
let field = options.find(item => item.name && item.name.toLocaleLowerCase() === lValue);
if (field) { if (field) {
this.field.value = field.id; this.field.value = field.id;
this.value = field.name; this.value = field.name;
@@ -100,6 +102,7 @@ export class TypeaheadWidget extends WidgetComponent implements OnInit {
this.value = null; this.value = null;
} }
// TODO: seems to be not needed as field.value setter calls it
this.field.updateForm(); this.field.updateForm();
} }