Trim White Spaces From Beginning and End of Form Fields in React-JsonSchema-Form - react-jsonschema-forms

I'm using react-jsonschema-form 1.2.1 to build a form based on a JsonSchema (v7). I want to automatically trim leading and trailing white spaces from certain text box input fields when a user presses submit on the form. Since the form is completely rendered by the <Form> element of the react-jsonschema-form module, I don't know how I can do this with JavaScript code.
Does react-jsonschema-form have trim capabilities?
Thanks in advance!

There a few different ways to do this, none as simple as flag to trim all string data though.
You can define a custom widget and use the uiSchema to designate specific fields to use this widget. This widget can then trim the value before using the native onChange function to notify the form that its value was changed, refer to: https://react-jsonschema-form.readthedocs.io/en/latest/advanced-customization/#custom-widget-components
You can define your own TextWidget (reserved name, refer to: https://github.com/mozilla-services/react-jsonschema-form/blob/master/src/components/widgets/TextWidget.js => https://github.com/mozilla-services/react-jsonschema-form/blob/master/src/components/widgets/BaseInput.js) and then use this TextWidget to replace all string-type fields:
const myWidgetOverrides = { TextWidget };
render() {
return (
<Form schema={schema}
widgets={this.myWidgetOverrides}
/>
);
}
You can override the validate function inside a new class component that extends the React JSONSchema Form class:
class TrimmedStringForm extends Form {
validate(formData, schema) {
formData = trimAllStrings(formData);
return super.validate(formData, schema);
}
}
or define your own validation function (refer to:
https://react-jsonschema-form.readthedocs.io/en/latest/validation/#custom-validation) to trim all/specific string-type fields from formData before it is passed into the submit function.

Related

Custom ui:widget will not alter the formData

When using ui:widget the field will not change the formData of the form, unlike when not using ui:widget any change in the field will be seen in the formData when the form is submitted.
Shall I change the formData of the form manually when the field text changes? if so, is there an example to do so?
Steps to Reproduce
Create the class that represent the custom UI, and use the following for render:
return (
<div >
{this.props.children}
</div>
)
Add to the schema.properties "City": {type: "string", title:"City"}
Add to the schema.properties "City": { "ui:widget": DefaultInput, classNames: "col-md-4"}
Where City is the name of the custom component.
and DefaultInput is the class that represent the custom ui of field.
Expected behavior
To see the value of the custom text field when submit the form:
onSubmit = ({formData}) => console.log(formData);
What I see is:
{City: undefined}
Any idea?
if you use custom 'ui:widgets' you have to use the json-schema-form onChange method when any item's value gets changed and after that you can get changed value from form.

How to remove a field after inserting it?

I am building a custom form, I have successfully been able to add new fields at run time into the form as:
Options.schema.properties= {...Options.schema.properties, [key]: {type: "string"} }
Options.uiSchema= {...Options.uiSchema, [key]: { "ui:widget": DefaultInput, classNames: "col-md-4"} }
Where key is the field id, and Options is observable via Mobx
In the form, I am using observable pattern via Mobx to update the schema.properties
Something like this:
class StudentsTab extends Component {
render() {
return (
<MyForm schema={Options.schema} uiSchema={Options.uiSchema} widgets={Options.widgets}
fields={this.customFields}
onChange={log("changed")}
onSubmit={log("submitted")}
onError={log("errors")}
/>
)
}
}
export default observer(StudentsTab);
Although a new fields can be added in this way, however, I could not remove them, my attempt was like:
delete Options.schema.properties[key]
delete Options.uiSchema[key]
I can see that the field id is removed, but its not removed from the DOM
Any idea? How would I remove a field after adding it?

Make flatpickr input required

I'm using the amazing flatpickr on a project and need the calendar date to be mandatory.
I'm trying to have all the validation in native HTML, so I was naively trying with just adding the required attribute to the input tag, but that doesn't appear to be working.
Is there a way of natively making a date mandatory with flatpickr or do I need to write some custom checks?
You can easily achieve this by:
Passing allowInput:true in flatpickr config.
As example:
flatpickrConfig = {
allowInput: true, // prevent "readonly" prop
};
From the documentation:
Allows the user to enter a date directly into the input field. By
default, direct entry is disabled.
The downside of this solution is that you should enable the direct entry (but ideally form validation should occur whether or not direct entry is enabled).
But if you don't want to enable the direct entry to solve this problem, you can use the code below as a workaround:
flatpickrConfig = {
allowInput:true,
onOpen: function(selectedDates, dateStr, instance) {
$(instance.altInput).prop('readonly', true);
},
onClose: function(selectedDates, dateStr, instance) {
$(instance.altInput).prop('readonly', false);
$(instance.altInput).blur();
}
};
This code remove the readonly property when it is not in focus so that html validation can occur and add back the readonly prop when it is in focus to prevent manual input. More details about it here.
This is what I came up with to make as complete of a solution as possible. It prevents form submission (when no date selected and input is required), ensures browser native "field required" message pops up and prevents the user typing in the value directly.
flatpickrConfig = {
allowInput: true, // prevent "readonly" prop
onReady: function(selectedDates, dateStr, instance) {
let el = instance.element;
function preventInput(event) {
event.preventDefault();
return false;
};
el.onkeypress = el.onkeydown = el.onkeyup = preventInput; // disable key events
el.onpaste = preventInput; // disable pasting using mouse context menu
el.style.caretColor = 'transparent'; // hide blinking cursor
el.style.cursor = 'pointer'; // override cursor hover type text
el.style.color = '#585858'; // prevent text color change on focus
el.style.backgroundColor = '#f7f7f7'; // prevent bg color change on focus
},
};
There is one disadvantage to this: Keyboard shortcuts are disabled when the flatpickr is open (when the input has focus). This includes F5, Ctrl + r, Ctrl + v, etc. but excludes Ctrl + w in Chromium 88 on Linux for some reason. I developed this using a rather old flatpickr version 3.1.5, but I think it should work on more recent ones too.
In case you want to use altFormat (display one date format to user, send other date format to server), which also implies setting altInput: true, you have to also change the onReady function to use instance.altInput instead of instance.element.
The onReady event listener can probably be attached to the instance after initializing it. However, my intention of using flatpickr with vue-flatpickr-component where you cannot elegantly access the individual flatpickr instances, made me use the config field instead.
I haven't tested it on mobile devices.
After digging a bit into the GitHub repo, I found a closed issue that points out that the issue will not be addressed.
In the same Issue page there is a workaround that seems to do the trick:
$('.flatpickr-input:visible').on('focus', function () {
$(this).blur()
})
$('.flatpickr-input:visible').prop('readonly', false)
copy attr name from prior input type hidden to rendered flatpickr input
just do this
$('[name=date_open]').next('input').attr("name","date_open");
$('[name=date_close]').next('input').attr("name","date_close");
Have been working on this for a couple of days now, finally getting the result I was after.
NOTE: I am using flatpickr with jQuery validation
As you would know flatpickr uses an alternative field for the date input, the actual field where the date is stored is hidden, and this is the key.
jQuery validation has a set of defaults, and by default hidden fields are not subject to validation, which normally makes perfect sense. So we just have to turn on the validation of hidden fields to make this work.
$.validator.setDefaults({
ignore: []
});
So my validator rules are then fairly normal:
var valid = {
rules: { dateyearlevel: {required: true} },
messages: { dateyearlevel: {required: "The date is required"} }
};
$("#myform").validate(valid);
That should allow you to ensure the date is required.
In my situation I wanted my date to only be required is a checkbox was checked. To do this we changed the rule above:
var valid = {
rules: { dateyearlevel: {
required: function() { return $("#mycheckbox").is(":checked") }
} },
messages: { dateyearlevel: {required: "The date is required"} }
};
$("#myform").validate(valid);
In case this helps someone, I'm using parsley.js for frontend validation and it works good with flatpickr
enter image description here
Just to expand a bit more on this, I found the ignore value set as an empty array did the trick for me also. You can just add this to your validate call back. Also displaying was a bit of an issue so I updated the errorPlacement to allow for flatpickr inputs like so.
$('#my-form').validate({
errorPlacement: function (error, element) {
if (element.hasClass('js-flatpickr') && element.next('.js-flatpickr').length) {
error.insertAfter(element.next('.js-flatpickr'));
} else if (element.parent('.input-group').length) {
error.insertAfter(element.parent());
} else {
error.insertAfter(element);
}
},
ignore: [],
rules: {
'startdate': { required: true }
},
messages: {
'startdate': {required: "Start Date is required"}
},
submitHandler: function(form) {
// ajax form post
}
});
in my case vue ( dunno why ) , i would like to comment for comment by #mik13ST
fyi: the default allowInput i think is true, no need to define, i didnt set the properties and my flat-pickr also work on testing.
i use
// this work in flat-pickr || #code_01
<small class="text-danger">
{{ validationContext.errors[0] }}
</small>
instead of
// work for all element except <flat-pickr #code_02 , dunno why not work
<b-form-invalid-feedback>
{{ validationContext.errors[0] }}
</b-form-invalid-feedback>
full code
<validation-provider
#default="validationContext"
name="Waktu Selesai Berkegiatan *"
vid="Waktu Selesai Berkegiatan *"
rules="required"
>
<flat-pickr
id="Waktu Selesai Berkegiatan *"
v-model="item.pip_time_end_rl"
placeholder="Waktu Selesai Berkegiatan *"
class="form-control"
static="true"
:config="dpconfig"
:state="getValidationState(validationContext)"
/>
// put here the message of error ( required ) #code_01 instead of #code_02
</validation-provider>
if younot use composite,
just use
#default="{ errors }" // in validation provider
:state="errors.length > 0 ? false : null" // in element for example flat-pickr
{{ errors[0] }} // to print out the message

Redux form - how to set fields as touched

I'm working with form that consists of multiple pages and I want to solve validation.
When I hit Submit button all fields on the present page shows error messages beneath, but if I change the page then I need to hit submit again because these fields weren't set as touched.
My problem would be solved if I could for example set all fields on the page as touched, once the form has flag anyTouched: true.
I'm using redux-form: '^6.0.0-rc.4' and I have one container where I include redux-form and multiple components consisting of fields.
I think your problem was the opposite way around, but in case anyone lands here as I did looking for a way to have anyTouched set after any field in the form is touched...
In redux-form 6 and above you have to explicitly choose the behaviour you want with the form-level configurations touchOnChange and touchOnBlur - see the docs here - by default nothing is configured and so nothing happens.
const Form = reduxForm({
form: 'my-form',
touchOnChange: true,
touchOnBlur: true
})(...)
These flags make it so that any given field is marked as touched (and therefore anyTouched is marked true on the form) when that field's onChange or onBlur handler is called, respectively.
I should have looked better:
Redux form returns touch as a prop to the component. The function takes names of fields as a parameter, so I'm checking in componentWillUpdate when submitFailed will change and then I'm gonna touch all fields that are not valid.
componentWillUpdate(nextProps) {
const {
formName: { syncErrors },
submitFailed,
touch
} = this.props
if (submitFailed !== nextProps.submitFailed) {
const toTouch = []
for (const key in syncErrors) {
syncErrors.hasOwnProperty(key) && toTouch.push(key)
}
touch(...toTouch)
}
}
In redux-form 7.4.2. This can be achieved by checking to see if the form is valid.
If valid you can can load one of your other pages.
If the form is not valid, use reduxForms getFormSyncErrors selector and pass in the keys returned by this object to the reduxForm touch property.
import React, { Component } from 'react'
import { compose } from 'redux';
import { connect } from 'react-redux';
import { reduxForm, getFormSyncErrors } from 'redux-form';
class MyComponent extends Component {
...
this.props.valid ?
// navigate away
: this.props.touch(...Object.keys(this.props.formErrors))
...
}
function mapStateToProps(state) {
return {
formErrors: getFormSyncErrors('myForm')(state)
}
}
export default compose(
connect(mapStateToProps, null),
reduxForm({form: 'myForm'})
)(MyComponent)

Selected option tag using Meteor

I have an Items collection with a boxId field (and a Boxes collection), and I want to be able to, through a select tag, change an item's boxId.
Here's my template:
And this is how I define the boxOptions helper:
How can I get an item's boxId and use it to find the proper option tag, and then give it the selected attribute?
Create an event
Template.item.helpers({
"change select": function(event){
const boxId = event.target.value;
items.update({_id: this._id}, {$set: {boxId: boxId}});
}
})
Note this assumes you are using the packages insecure and autopublish. If you don't use these and you really should not, then you best read about:
Parameter validation
Publications
Meteor Methods
Use Template.parentData() to have access to the item's id. Here's the helper:
selected: function () {
if (this._id == Template.parentData().boxId) {
return "selected";
}
}

Resources