Skip to content

Commit

Permalink
Resolves #605: Added ability to override the default validators via c…
Browse files Browse the repository at this point in the history
…onfiguration. Added conditional validation. Modified default form definitions to validate contributor emails when a name is entered.
  • Loading branch information
shilob committed Jun 21, 2019
1 parent ff2f0dc commit 7d35fde
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 5 deletions.
4 changes: 2 additions & 2 deletions angular/shared/form/field-contributor.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
<span class='text-right'>{{ field.nameColHdr }}</span>
</div>
<div [ngClass]="getGroupClass('text_full_name', true)">
<ng2-completer #ngCompleter (blur)="onBlur()" (keydown)="onKeydown($event)" [overrideSuggested]="!field.forceLookupOnly" [inputClass]="'form-control'" [placeholder]="field.vocabField.placeHolder" [clearUnselected]="field.forceLookupOnly" (selected)="onSelect($event)" [datasource]="field.vocabField.dataService" [minSearchLength]="0" [initialValue]="field.vocabField.initialValue"></ng2-completer>
<ng2-completer #ngCompleter (blur)="onBlur()" (keyup)="onKeyUp($event)" (keydown)="onKeydown($event)" [overrideSuggested]="!field.forceLookupOnly" [inputClass]="'form-control'" [placeholder]="field.vocabField.placeHolder" [clearUnselected]="field.forceLookupOnly" (selected)="onSelect($event)" [datasource]="field.vocabField.dataService" [minSearchLength]="0" [initialValue]="field.vocabField.initialValue"></ng2-completer>
<div class="text-danger" *ngIf="field.formModel.controls['text_full_name'].hasError('required')">{{field.validationMessages.required.text_full_name}}</div>
</div>
<div class='col-xs-1'>
Expand All @@ -152,7 +152,7 @@
<span class='text-right'>{{ field.nameColHdr }}</span>
</div>
<div [ngClass]="getGroupClass('text_full_name', true)">
<ng2-completer #ngCompleter (blur)="onBlur()" (keydown)="onKeydown($event)" [overrideSuggested]="!field.forceLookupOnly" [inputClass]="'form-control'" [placeholder]="field.vocabField.placeHolder" [clearUnselected]="field.forceLookupOnly" (selected)="onSelect($event)" [datasource]="field.vocabField.dataService" [minSearchLength]="0" [initialValue]="field.vocabField.initialValue"></ng2-completer>
<ng2-completer #ngCompleter (blur)="onBlur()" (keyup)="onKeyUp($event)" (keydown)="onKeydown($event)" [overrideSuggested]="!field.forceLookupOnly" [inputClass]="'form-control'" [placeholder]="field.vocabField.placeHolder" [clearUnselected]="field.forceLookupOnly" (selected)="onSelect($event)" [datasource]="field.vocabField.dataService" [minSearchLength]="0" [initialValue]="field.vocabField.initialValue"></ng2-completer>
<div class="text-danger" *ngIf="field.formModel.controls['text_full_name'].hasError('required')">{{field.validationMessages.required.text_full_name}}</div>
</div>
<div class='col-xs-1'>
Expand Down
88 changes: 86 additions & 2 deletions angular/shared/form/field-contributor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class ContributorField extends FieldBase<any> {
findRelationship: any;
findRelationshipFor: string;
relationshipFor: string;
activeValidators: any;

constructor(options: any, injector: any) {
super(options, injector);
Expand All @@ -85,6 +86,9 @@ export class ContributorField extends FieldBase<any> {

this.roles = options['roles'] || [];
this.value = options['value'] || this.setEmptyValue();

this.activeValidators = options['activeValidators'];

this.fieldNames = options['fieldNames'] || [];
const textFullNameFieldName = _.find(this.fieldNames, fieldNameObject => {
return fieldNameObject['text_full_name'] != undefined;
Expand Down Expand Up @@ -125,6 +129,21 @@ export class ContributorField extends FieldBase<any> {
this.validators['family_name'] = [Validators.required];
this.validators['given_name'] = [Validators.required];
}
// Resolves: #605
// now that we've set the default validators... we read the config to override
if (!_.isEmpty(this.activeValidators)) {
_.forOwn(this.activeValidators, (vConfig, fName) => {
// expects to be an array of Validator method names
if (_.isArray(vConfig)) {
this.validators[fName] = _.map(vConfig, (vConfigEntry) => {
return _.get(Validators, vConfigEntry);
});
} else if (vConfig == null) {
// remove the default...
_.unset(this.validators, fName);
}
});
}
this.findRelationshipFor = options['findRelationshipFor'] || '';
this.findRelationship = options['findRelationship'] || null;
this.relationshipFor = options['relationshipFor'] || '';
Expand Down Expand Up @@ -168,11 +187,38 @@ export class ContributorField extends FieldBase<any> {
if (this.required) {
this.enableValidators();
} else {
// TODO: cherry pick validators, like email, etc.
// if splitting names, attach handler to individual input form control
if (this.splitNames) {
const reqFields = ['family_name', 'given_name'];
const handler = this.getToggleConditionalValidationHandler(reqFields, true);
_.each(reqFields, (reqField:any) => {
this.formModel.controls[reqField].valueChanges.subscribe(handler);
});
// install the validators if needed
const hasValue = !_.isEmpty(this.value.family_name) || !_.isEmpty(this.value.given_name);
this.toggleConditionalValidation(hasValue);
} else {
// install the validators if needed
this.toggleConditionalValidation(!_.isEmpty(this.value.text_full_name));
}
}
return this.formModel;
}

getToggleConditionalValidationHandler(requiredFields:any[], useFormControl:boolean) {
return ((value:any) => {
let hasValue = true;
if (useFormControl) {
_.each(requiredFields, (reqField:any) => {
hasValue = hasValue || !_.isEmpty(this.formModel.controls[reqField].value);
});
} else {
hasValue = !_.isEmpty(value);
}
this.toggleConditionalValidation(hasValue);
});
}

setValue(value:any, emitEvent:boolean=true, updateTitle:boolean=false) {
this.setMissingFields(value);
if (!this.hasInit) {
Expand All @@ -190,6 +236,32 @@ export class ContributorField extends FieldBase<any> {
if (updateTitle && !this.freeText) {
this.component.ngCompleter.ctrInput.nativeElement.value = this.vocabField.getTitle(value);
}
// install the validators if needed
if (this.splitNames) {
const hasValue = !_.isEmpty(value.family_name) || !_.isEmpty(value.given_name);
this.toggleConditionalValidation(hasValue);
} else {
this.toggleConditionalValidation(!_.isEmpty(value.text_full_name));
}

}

toggleConditionalValidation(hasValue) {
// Resolves #605: Add config flag for each field to enable conditional validation
// Only one condition for now: names must be set
if (hasValue) {
_.forOwn(this.activeValidators, (vConfig, fName) => {
if (!_.isUndefined(this.validators[fName])) {
this.formModel.controls[fName].setValidators(this.validators[fName]);
}
});
} else {
_.forOwn(this.activeValidators, (vConfig, fName) => {
if (!_.isUndefined(this.validators[fName])) {
this.formModel.controls[fName].clearValidators();
}
});
}
}

toggleValidator(c:any) {
Expand All @@ -208,7 +280,10 @@ export class ContributorField extends FieldBase<any> {
}
this.enabledValidators = true;
_.forEach(this.groupFieldNames, (f:any) => {
this.formModel.controls[f].setValidators(this.validators[f]);
// Resolves #605: check if there is a validator, because we can disable/remove validators now
if (!_.isUndefined(this.validators[f])) {
this.formModel.controls[f].setValidators(this.validators[f]);
}
});
}

Expand Down Expand Up @@ -411,6 +486,7 @@ export class ContributorComponent extends SimpleComponent {
console.log(`Forced lookup, clearing data..`)
this.field.setEmptyValue(true);
this.lastSelected = null;
this.field.toggleConditionalValidation(false);
}
});
}
Expand Down Expand Up @@ -490,19 +566,27 @@ export class ContributorComponent extends SimpleComponent {
if (this.lastSelected && this.emptied) {
const that = this;
setTimeout(() => {
const value = that.lastSelected.title;
that.ngCompleter.ctrInput.nativeElement.value = that.lastSelected.title;
}, 40);
} else {
if (this.emptied && this.field.forceLookupOnly) {
console.log(`Forced lookup, clearing data..`)
this.field.setEmptyValue(true);
this.lastSelected = null;
this.field.toggleConditionalValidation(false);
}
}
} else {
const val = this.field.vocabField.getValue({text_full_name: this.ngCompleter.ctrInput.nativeElement.value });
this.field.setValue(val, true, false);
}

}

public onKeyUp(event) {
const val = this.ngCompleter.ctrInput.nativeElement.value
this.field.toggleConditionalValidation(!_.isEmpty(val));
}

public onBlur() {
Expand Down
3 changes: 3 additions & 0 deletions form-config/dataRecord-1.0-draft.js
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,9 @@ module.exports = {
onValueUpdate: {
modelEventSource: 'valueChanges'
}
},
activeValidators: {
email: ['required', 'email']
}
}
}],
Expand Down
5 changes: 4 additions & 1 deletion form-config/default-1.0-draft.js
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,10 @@ module.exports = {
nameColHdr: '@dmpt-people-tab-name-hdr',
emailColHdr: '@dmpt-people-tab-email-hdr',
orcidColHdr: '@dmpt-people-tab-orcid-hdr',
showRole: false
showRole: false,
activeValidators: {
email: ['required', 'email']
}
}
}]
}
Expand Down
3 changes: 3 additions & 0 deletions form-config/publication/tab-citation-1.0.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ module.exports = [
onValueUpdate: {
modelEventSource: 'valueChanges'
}
},
activeValidators: {
email: ['required', 'email']
}
}
}],
Expand Down

0 comments on commit 7d35fde

Please sign in to comment.