Server invalidations are now sticky
This commit is contained in:
parent
6eeb2639f2
commit
e5861d41f5
6 changed files with 116 additions and 21 deletions
|
@ -12,18 +12,28 @@ export interface LabeledFieldComponentProps
|
||||||
labelText: string
|
labelText: string
|
||||||
inError:boolean
|
inError:boolean
|
||||||
error?:string
|
error?:string
|
||||||
|
inStickyError?:boolean
|
||||||
|
stickyError?:string
|
||||||
className?:string
|
className?:string
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export default class LabeledFieldComponent extends React.Component<LabeledFieldComponentProps, {}> {
|
export default class LabeledFieldComponent extends React.Component<LabeledFieldComponentProps, {}> {
|
||||||
render() {
|
render() {
|
||||||
let className = this.props.className || ""
|
let classNames:string[] = []
|
||||||
|
if (this.props.className)
|
||||||
|
classNames.push(this.props.className)
|
||||||
|
|
||||||
let inError = this.props.inError && this.props.error !== null && this.props.error !== "";
|
let inError = this.props.inError && this.props.error !== null && this.props.error !== "";
|
||||||
className += " form-group"
|
let inStickyError = this.props.inStickyError && this.props.stickyError !== null && this.props.stickyError !== ""
|
||||||
className += inError ? " has-error" : ""
|
|
||||||
return <fieldset className={className}><label htmlFor={this.props.inputId} className="control-label">{this.props.labelText}</label>
|
classNames.push("form-group")
|
||||||
<StandardFieldComponent inError={inError} error={this.props.error} >{this.props.children}</StandardFieldComponent>
|
if(inError || inStickyError){
|
||||||
|
classNames.push("has-error")
|
||||||
|
}
|
||||||
|
|
||||||
|
return <fieldset className={classNames.join(" ")}><label htmlFor={this.props.inputId} className="control-label">{this.props.labelText}</label>
|
||||||
|
<StandardFieldComponent inError={inError} error={this.props.error} inStickyError={inStickyError} stickyError={this.props.stickyError}>{this.props.children}</StandardFieldComponent>
|
||||||
</fieldset>;
|
</fieldset>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ export interface StandardFieldComponentProps
|
||||||
{
|
{
|
||||||
inError:boolean
|
inError:boolean
|
||||||
error?:string
|
error?:string
|
||||||
|
inStickyError?:boolean
|
||||||
|
stickyError?:string
|
||||||
children?:React.ReactNode
|
children?:React.ReactNode
|
||||||
[additional_properties:string]: any
|
[additional_properties:string]: any
|
||||||
}
|
}
|
||||||
|
@ -24,9 +26,13 @@ export default class StandardFieldComponent extends React.Component<StandardFiel
|
||||||
let errorMessage = this.props.inError ? this.props.error : undefined
|
let errorMessage = this.props.inError ? this.props.error : undefined
|
||||||
let errorDiv = this.props.inError? <div className="help-block" role="alert">{errorMessage}</div> : ""
|
let errorDiv = this.props.inError? <div className="help-block" role="alert">{errorMessage}</div> : ""
|
||||||
|
|
||||||
|
let stickyErrorMessage = this.props.inStickyError ? this.props.stickyError : undefined
|
||||||
|
let stickyErrorDiv = this.props.inStickyError ? <div className="help-block" role="alert">{stickyErrorMessage}</div> : ""
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
{this.renderChildren()}
|
{this.renderChildren()}
|
||||||
{errorDiv}
|
{errorDiv}
|
||||||
|
{stickyErrorDiv }
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,15 @@ import * as _ from 'lodash'
|
||||||
import {Field} from "../../../../types/mobx-react-form";
|
import {Field} from "../../../../types/mobx-react-form";
|
||||||
import LabeledFieldComponent from "./LabeledFieldComponent";
|
import LabeledFieldComponent from "./LabeledFieldComponent";
|
||||||
import {injectIntl, InjectedIntl} from 'react-intl';
|
import {injectIntl, InjectedIntl} from 'react-intl';
|
||||||
|
import {HoudiniField} from "../../lib/houdini_form";
|
||||||
|
|
||||||
|
|
||||||
export const BasicField = injectIntl(observer((props:{field:Field, intl?:InjectedIntl, wrapperClassName?:string}) =>{
|
export const BasicField = injectIntl(observer((props:{field:Field, intl?:InjectedIntl, wrapperClassName?:string}) =>{
|
||||||
|
let field = props.field as HoudiniField
|
||||||
return <LabeledFieldComponent
|
return <LabeledFieldComponent
|
||||||
inputId={props.field.id} labelText={props.field.label} inError={props.field.hasError} error={props.field.error} className={props.wrapperClassName} >
|
inputId={props.field.id} labelText={field.label} inError={field.hasError} error={field.error}
|
||||||
|
inStickyError={field.hasServerError} stickyError={field.serverError}
|
||||||
|
className={props.wrapperClassName} >
|
||||||
|
|
||||||
<input {...props.field.bind()} className="form-control"/>
|
<input {...props.field.bind()} className="form-control"/>
|
||||||
</LabeledFieldComponent>
|
</LabeledFieldComponent>
|
||||||
|
|
|
@ -90,6 +90,8 @@ export class RegistrationPageForm extends HoudiniForm {
|
||||||
if (e instanceof ValidationErrorsException) {
|
if (e instanceof ValidationErrorsException) {
|
||||||
this.converter.convertErrorToForm(e, f)
|
this.converter.convertErrorToForm(e, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.invalidateFromServer(e['error'])
|
||||||
//set error to the form
|
//set error to the form
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,91 @@
|
||||||
// License: LGPL-3.0-or-later
|
// License: LGPL-3.0-or-later
|
||||||
import {Field, Form} from "mobx-react-form";
|
import {Field, Form} from "mobx-react-form";
|
||||||
import {action, runInAction} from 'mobx'
|
import {observable, action, runInAction, computed, IValueDidChange} from 'mobx'
|
||||||
import validator = require("validator")
|
import validator = require("validator")
|
||||||
import * as _ from 'lodash'
|
import * as _ from 'lodash'
|
||||||
import {ValidationErrorsException} from "../../api";
|
import {ValidationErrorsException} from "../../api";
|
||||||
|
import {initializationDefinition} from "../../../types/mobx-react-form";
|
||||||
|
|
||||||
|
|
||||||
export class HoudiniForm extends Form {
|
export class HoudiniForm extends Form {
|
||||||
|
|
||||||
|
|
||||||
|
@observable
|
||||||
|
private $serverError:string
|
||||||
|
|
||||||
plugins() {
|
plugins() {
|
||||||
return {
|
return {
|
||||||
vjf: validator
|
vjf: validator
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public makeField(key:any, path:any, data:any, props:any, update:boolean, state:any) {
|
||||||
|
return new HoudiniField(key, path, data, props, update, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 !== "")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function areWeOrAnyParentSubmitting(f:Field|Form ) : boolean
|
export function areWeOrAnyParentSubmitting(f:Field|Form ) : boolean
|
||||||
{
|
{
|
||||||
|
@ -66,13 +136,14 @@ export class StaticFormToErrorAndBackConverter<T> {
|
||||||
this.formToPath = _.invert(pathToForm)
|
this.formToPath = _.invert(pathToForm)
|
||||||
}
|
}
|
||||||
|
|
||||||
convertFormToObject(form: Form): T {
|
convertFormToObject(form: HoudiniForm|Form): T {
|
||||||
let output = {}
|
let output = {}
|
||||||
|
let hForm = form as HoudiniForm
|
||||||
for (let pathToFormKey in this.pathToForm) {
|
for (let pathToFormKey in this.pathToForm) {
|
||||||
if (this.pathToForm.hasOwnProperty(pathToFormKey)) {
|
if (this.pathToForm.hasOwnProperty(pathToFormKey)) {
|
||||||
let formPath = this.pathToForm[pathToFormKey]
|
let formPath = this.pathToForm[pathToFormKey]
|
||||||
if (form.$(formPath).value && _.trim(form.$(formPath).value) !== "")
|
if (hForm.$(formPath).value && _.trim(hForm.$(formPath).value) !== "")
|
||||||
_.set(output, pathToFormKey, form.$(formPath).value)
|
_.set(output, pathToFormKey, hForm.$(formPath).value)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -82,13 +153,15 @@ export class StaticFormToErrorAndBackConverter<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@action.bound
|
@action.bound
|
||||||
convertErrorToForm(errorException: ValidationErrorsException, form: Form): void {
|
convertErrorToForm(errorException: ValidationErrorsException, form: HoudiniForm|Form): void {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
|
let hForm = form as HoudiniForm
|
||||||
_.forEach(errorException.item.errors, (error) => {
|
_.forEach(errorException.item.errors, (error) => {
|
||||||
let message = error.messages.join(", ")
|
let message = error.messages.join(", ")
|
||||||
_.forEach(error.params, (p) => {
|
_.forEach(error.params, (p) => {
|
||||||
if (this.pathToForm[p])
|
if (this.pathToForm[p]) {
|
||||||
form.$(this.pathToForm[p]).invalidate(message)
|
(hForm.$(this.pathToForm[p]) as HoudiniField).invalidateFromServer(message)
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
console.warn(`We couldn't find a form element for path: "${p}"`)
|
console.warn(`We couldn't find a form element for path: "${p}"`)
|
||||||
}
|
}
|
||||||
|
|
8
types/mobx-react-form/index.d.ts
vendored
8
types/mobx-react-form/index.d.ts
vendored
|
@ -31,6 +31,7 @@ interface FieldHooks{
|
||||||
interface Base extends SharedFieldFormMethods, SharedFormFieldProperties{}
|
interface Base extends SharedFieldFormMethods, SharedFormFieldProperties{}
|
||||||
|
|
||||||
export declare class Field implements Base, FieldProperties, FieldMethods, FieldHandlers {
|
export declare class Field implements Base, FieldProperties, FieldMethods, FieldHandlers {
|
||||||
|
constructor(...props:any[])
|
||||||
readonly bindings: string;
|
readonly bindings: string;
|
||||||
readonly changed: boolean;
|
readonly changed: boolean;
|
||||||
readonly default: boolean;
|
readonly default: boolean;
|
||||||
|
@ -145,8 +146,6 @@ export declare class Field implements Base, FieldProperties, FieldMethods, Field
|
||||||
sync(e: Field): any;
|
sync(e: Field): any;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FormHooks {
|
interface FormHooks {
|
||||||
|
@ -256,7 +255,6 @@ interface initializationDefinition {
|
||||||
fields?:FieldDefinitions[]
|
fields?:FieldDefinitions[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class Form implements Base {
|
export class Form implements Base {
|
||||||
|
|
||||||
|
|
||||||
|
@ -290,7 +288,7 @@ export class Form implements Base {
|
||||||
|
|
||||||
intercept(obj: any);
|
intercept(obj: any);
|
||||||
|
|
||||||
invalidate(msg: string);
|
invalidate(msg?: string);
|
||||||
|
|
||||||
map(callback: (i: Field) => void);
|
map(callback: (i: Field) => void);
|
||||||
|
|
||||||
|
@ -307,6 +305,8 @@ export class Form implements Base {
|
||||||
|
|
||||||
readonly submitting: boolean;
|
readonly submitting: boolean;
|
||||||
|
|
||||||
|
protected validator :any
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue