2018-05-21 15:03:46 -05:00
|
|
|
// License: LGPL-3.0-or-later
|
2018-07-12 10:44:30 -05:00
|
|
|
import {Field, FieldDefinition, Form, initializationDefinition} from "mobx-react-form";
|
2018-06-29 13:13:31 -05:00
|
|
|
import {action, computed, IValueDidChange, observable, runInAction} from 'mobx'
|
2018-05-21 15:03:46 -05:00
|
|
|
import * as _ from 'lodash'
|
|
|
|
import {ValidationErrorsException} from "../../api";
|
2018-06-29 13:13:31 -05:00
|
|
|
import validator = require("validator");
|
2018-05-21 15:03:46 -05:00
|
|
|
|
|
|
|
|
|
|
|
export class HoudiniForm extends Form {
|
2018-07-12 10:44:30 -05:00
|
|
|
constructor(definition?:initializationDefinition, options?:any){
|
|
|
|
//correct the bug where field initializations with just names don't work
|
|
|
|
if (definition && definition.fields){
|
|
|
|
definition.fields = definition.fields.map((i:FieldDefinition) =>
|
|
|
|
{
|
|
|
|
if (_.entries(i).length == 1)
|
|
|
|
{
|
|
|
|
i.extra = null
|
|
|
|
}
|
|
|
|
return i
|
|
|
|
})
|
|
|
|
}
|
|
|
|
super(definition, options)
|
|
|
|
}
|
2018-06-29 12:42:56 -05:00
|
|
|
|
|
|
|
@observable
|
|
|
|
private $serverError:string
|
|
|
|
|
2018-05-21 15:03:46 -05:00
|
|
|
plugins() {
|
|
|
|
return {
|
|
|
|
vjf: validator
|
2018-06-29 12:42:56 -05:00
|
|
|
};
|
|
|
|
}
|
2018-05-21 15:03:46 -05:00
|
|
|
|
2018-06-29 12:42:56 -05:00
|
|
|
public makeField(key:any, path:any, data:any, props:any, update:boolean, state:any) {
|
|
|
|
return new HoudiniField(key, path, data, props, update, state);
|
|
|
|
}
|
2018-05-21 15:03:46 -05:00
|
|
|
|
2018-06-29 12:42:56 -05:00
|
|
|
@computed
|
|
|
|
public get serverError():string {
|
|
|
|
return this.$serverError
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
public get hasServerError():boolean{
|
|
|
|
return (this.$serverError && this.$serverError !== null && this.$serverError !== "") &&
|
|
|
|
!this.submitting
|
|
|
|
}
|
|
|
|
|
|
|
|
@action
|
|
|
|
invalidateFromServer(message:string) {
|
|
|
|
this.invalidate()
|
|
|
|
this.$serverError = message
|
2018-05-21 15:03:46 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-29 12:42:56 -05:00
|
|
|
export class HoudiniField extends Field {
|
|
|
|
constructor(...props:any[]) {
|
|
|
|
super(...props)
|
|
|
|
|
|
|
|
|
|
|
|
this.observe({
|
|
|
|
key: 'areWeOrAnyParentSubmitting', call: (obj: { form: HoudiniForm,
|
|
|
|
field: HoudiniField,
|
|
|
|
change: IValueDidChange<boolean> }) => {
|
|
|
|
if (obj.change.newValue) {
|
|
|
|
this.$serverError = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
@observable private $serverError:string
|
|
|
|
|
|
|
|
@action
|
|
|
|
invalidateFromServer(message:string) {
|
|
|
|
this.$serverError = message
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
public get serverError():string {
|
|
|
|
return this.$serverError
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed get areWeOrAnyParentSubmitting() :boolean {
|
|
|
|
return areWeOrAnyParentSubmitting(this)
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
public get hasServerError():boolean{
|
|
|
|
return (this.$serverError && this.$serverError !== null && this.$serverError !== "")
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-06-28 13:24:09 -05:00
|
|
|
export function areWeOrAnyParentSubmitting(f:Field|Form ) : boolean
|
|
|
|
{
|
|
|
|
let currentItem: Field|Form = f
|
|
|
|
let isSubmitting:boolean = f.submitting
|
|
|
|
while (!isSubmitting && currentItem && !(currentItem instanceof Form)){
|
|
|
|
currentItem = currentItem.container()
|
|
|
|
isSubmitting = currentItem.submitting
|
|
|
|
}
|
|
|
|
|
|
|
|
return isSubmitting
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-05-21 15:03:46 -05:00
|
|
|
|
|
|
|
interface PathToFormField {
|
|
|
|
[props: string]: string
|
|
|
|
}
|
|
|
|
|
|
|
|
type FormFieldToPath = PathToFormField
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tool for converting between the form's data structure
|
|
|
|
* to the AJAX datastructure and properly assigning
|
|
|
|
* errors if AJAX request fails
|
|
|
|
* As an example for the, consider the the following form structure:
|
|
|
|
* {
|
|
|
|
* // a tab in a Wizard
|
|
|
|
* nonprofitTab: {
|
|
|
|
* organization_name: {some field info}
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* We want to create a data structure for AJAX like so:
|
|
|
|
*
|
|
|
|
* In the database
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
export class StaticFormToErrorAndBackConverter<T> {
|
|
|
|
|
|
|
|
pathToForm: PathToFormField
|
|
|
|
formToPath: FormFieldToPath
|
|
|
|
|
|
|
|
|
|
|
|
constructor(pathToForm: PathToFormField) {
|
|
|
|
this.pathToForm = pathToForm
|
|
|
|
this.formToPath = _.invert(pathToForm)
|
|
|
|
}
|
|
|
|
|
2018-06-29 12:42:56 -05:00
|
|
|
convertFormToObject(form: HoudiniForm|Form): T {
|
2018-05-21 15:03:46 -05:00
|
|
|
let output = {}
|
2018-06-29 12:42:56 -05:00
|
|
|
let hForm = form as HoudiniForm
|
2018-05-21 15:03:46 -05:00
|
|
|
for (let pathToFormKey in this.pathToForm) {
|
|
|
|
if (this.pathToForm.hasOwnProperty(pathToFormKey)) {
|
|
|
|
let formPath = this.pathToForm[pathToFormKey]
|
2018-06-29 12:42:56 -05:00
|
|
|
if (hForm.$(formPath).value && _.trim(hForm.$(formPath).value) !== "")
|
|
|
|
_.set(output, pathToFormKey, hForm.$(formPath).value)
|
2018-05-21 15:03:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return output as T
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@action.bound
|
2018-06-29 12:42:56 -05:00
|
|
|
convertErrorToForm(errorException: ValidationErrorsException, form: HoudiniForm|Form): void {
|
2018-05-21 15:03:46 -05:00
|
|
|
runInAction(() => {
|
2018-06-29 12:42:56 -05:00
|
|
|
let hForm = form as HoudiniForm
|
2018-05-21 15:03:46 -05:00
|
|
|
_.forEach(errorException.item.errors, (error) => {
|
|
|
|
let message = error.messages.join(", ")
|
|
|
|
_.forEach(error.params, (p) => {
|
2018-06-29 12:42:56 -05:00
|
|
|
if (this.pathToForm[p]) {
|
|
|
|
(hForm.$(this.pathToForm[p]) as HoudiniField).invalidateFromServer(message)
|
|
|
|
}
|
2018-05-21 15:03:46 -05:00
|
|
|
else {
|
|
|
|
console.warn(`We couldn't find a form element for path: "${p}"`)
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2018-06-28 13:24:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|