Formik Form validation in react - formik

I have implemented form validation with formik and react. I am using material-UI.
<Formik
initialValues={{ name: '' }}
onSubmit={values => {
console.log('submitting', values);
}}
validate={values => {
alert();
let errors = {};
if (!values.name) {
errors.name = 'Name is required';
}
return errors;
}}>
{({
handleSubmit,
handleChange,
values,
errors
}) => (
<form onSubmit={handleSubmit}>
<div>
<input name="name"
onChange={handleChange}
name="name"
value={values.name}
type="text"
placeholder="Name">
</input>
{errors.name &&
<span style={{ color: "red", fontWeight: "bold" }}>
{errors.name}
</span>
}
</div>
<div>
<button>Submit</button>
</div>
</form>
)}
</Formik>
Above code is working fine for normal input tags but it is not working for Select and TextField material widgets.
Is there a compatibility issue with material UI ?
Please help.

As Chris B. commented, the solution is to wrap each desired element inside a React component that has what Formik requires. In the case of Material-UI, Gerhat on GitHub has created some of those components.
You can use those by downloading the source from the github link above. There is also a usage example there, showing how to use Gerhat's "wrapper" for a Material TextField and a Select.
In Gerhat's usage example, TextField is a component in that github repo; it isn't the Material UI TextField directly; it is a Formik-compatible "wrapper" around the Material TextField widget.
By looking at gerhat's source code, you can learn how to do the same for other Material widgets you need.
HOWEVER, gerhat's implementation may not be the easiest for a beginner to understand. Those wrappers are easy to use, but it may not be obvious from that code how to write your own wrappers for other widgets or components.
See my answer here for a discussion of Formik's <Field> and useField. Those are easier ways to "wrap" existing React components. (Not specifically Material-UI widgets, but AFAIK, you can wrap those like any other React component.)
If you do want to understand gerhat's approach, here are some comments about the code you'll see at github.
This is the source to TextField.jsx in that repo.
The "heart" of TextField.jsx, is its wrapping around Material's TextField. The generated virtual-DOM element (representing a Material TextField) is seen in these lines:
return (
<TextField
label={label}
error={hasError}
helperText={hasError ? errorText : ''}
{...field}
{...other}
/>
)
See the source link above, for the details of how this is made compatible with Formik. IMHO, a fairly advanced understanding of both React and Formik is required, to understand what is being done there. This is why I mentioned Field and useField as the place to start, for writing your own "Formik-compatible wrappers".
One detail I'll mention. The implementation relies on a file index.js in the same folder as TextField.jsx, to map the default of TextField.jsx, which is FTextField, to the name TextField, which is how you refer to it in the "usage" code at the start of this answer. SO Q&A about index.js file of a React component.
index.js contents:
export { default as TextField } from './TextField'
The other two files in the TextField source folder are ".d.ts" files. See this Q&A to understand those. You don't need them unless you are using TypeScript.

Related

Get Data back from Vue Component

Is it possible to get Data back from a vue component?
Laravel blade.php code:
...
<div>
<component1></component1>
</div>
...
In component1 is a selectbox which i need (only the selected item/value) in the blade.php
A vue component, when rendered in the browser, is still valid HTML. If you make sure your component is wrapped in a form element and has a valid input element, and the form can be submitted, the PHP endpoint can consume the form’s data without problems. It could look like this:
Layout/view:
<form method="post" action="/blade.php">
<component1></component1>
<button type="submit">Submit form</button>
</form>
Component (<component1/>):
<fieldset>
<input type="checkbox" name="my_option" id="my_option">
<label for="my_option">I have checked this checkbox</label>
</fieldset>
PHP script (blade.php):
echo $_POST["my_option"] // if checked, should print "on"
If you are looking for a JavaScript centered approach, you may want to serialize the form and fetch the endpoint; it could look similar to this:
import serialize from 'form-serialize';
const formData = serialize(form)
fetch(form.action, { method: 'POST' }, body: JSON.stringify(formData) })
.then(response => {
// update page with happy flow
})
.catch(error => {
// update page with unhappy flow
})
Building from an accessible and standardized basis using proper HTML semantics will likely lead to more understandable code and easier enhancements down the road. Good luck!
(Edit: if you require a complete, working solution to your question, you should post more code, both from the Vue app as well as the PHP script.)

Manually trigger validations(Validate Formik Form ) on button click even when fields are untouched

In our React app, We have multiple forms in a page. We use Formik and Yup for forms and validations respectively
Currently validations are fired only when field/form are touched. We have build wrapper on Formik that submits form on blur/focus out event.
Now, the requirement is to show error for all the required fields in a page that includes multiple Formik forms on click of a button.
To clarify further, my wrapper looks like
const { onSubmit, className, style, ...restProps } = this.props;
return (
<div
ref={this.setContainerRef}
onBlur={this.handleBlur}
onFocus={this.handleFocus}
tabIndex={-1}
className={className}
style={style}
>
<Formik
{...restProps}
ref={this.setFormikRef}
initialValues={this.props.currentValues}
validateOnBlur={true}
validateOnChange={false}
render={(formikProps: FormikProps<T>) => (
<>
<DirtyFormReporter onChange={this.handleDirtyFormReport} isDirty={formikProps.dirty} />
{this.props.render(formikProps, this.createEditBlurFormActions())}
</>
)}
onSubmit={this.handleSubmit}
/>
{ReactDOM.createPortal(<InputBlocker text="Updating" isEnabled={this.state.isSubmitting} />, document.body)}
</div>
);
There are multiple ways to handle this situation but you can use fieldarray of Formik here.
https://formik.org/docs/api/fieldarray
When you will submit you will get an array and a list of forms in it then you can simple add and remove forms and you can easily do your validation using Yup.

Can I use CKEditor 5 Inline or Balloon Editor on a form submission?

I'd like to use the CKEditor 5 Inline or Balloon editor on a form submission, but I'm having difficulty.
I can get the submission to work perfectly w/ Classic Editor but the Inline Editor prevents me from typing in the field.
Here is my code:
<script type="text/javascript">
InlineEditor.create( document.querySelector( '#ck' ) );
</script>
Here is the HTML:
<div class="form-group">
<label>Comment</label>
<textarea cols="80" rows="10" name="comment" class="form-control" id="ck">foo
</div>
On the page, the editor shows up, but I am unable to type into it on Safari (Mac).
I looks like this was possible in CKEditor 4, it is possible in 5?
Unfortunately, InlineEditor and BalloonEditor are not meant to replace <textarea> element. ClassicEditor works in this case because it just replaces the whole element with its own containers, but this is not the case for other editors.
CKEditor 4 was kind of one solution to suit all the needs. There were a lot of things happening under the hood. With CKEditor 5 we give you builds and an API, but the integration needs to be done by the outside developer. I am not saying this will never change, although this is the status for now.
Additionally, at the moment, neither editor will replace <textarea> value as you type.
If you want to use ClassicEditor, you might want to replace <textarea>'s value with editor data on form submission.
const textarea = document.querySelector( '#ck' );
ClassicEditor
.create( textarea )
.then( editor => { window.editor = editor } );
document.getElementById( 'submit' ).onclick = () => {
textarea.value = editor.getData();
}
If you would like to use InlineEditor or BalloonEditor, you need to use <div> instead of <textarea>. You could create a hidden input field and set its value to editor data in a similar fashion as above.

Binding Vue.js to all instances of an element, without(?) using Components

Today I'm learning Vue.js, and I have a few ideas of where it might be really useful in a new project that's an off-shoot of an existing, live project.
I like the idea of trying to replace some of my existing functionality with Vue, and I see that Components may be quite handy as quite a lot of functionality is re-used (e.g. Postcode lookups).
Once of the pieces of functionality I've used for an age is for invalid form elements - currently in jQuery when a form input or textarea is blurred I add a class of form__blurred, and that is coupled with some Sass such as:
.form__blurred {
&:not(:focus):invalid {
border-color:$danger;
}
}
This is to avoid styling all required inputs as errors immediately on page load.
I'm totally fine with doing this in jQuery, but I figured maybe it could be done in Vue.
I have an idea of how I might do it with components thanks to the laracasts series, but my form inputs are all rendered by Blade based on data received from Laravel and it doesn't seem like a neat solution to have some of the inputs rendered in Javascript, for a number of reasons (no JS, confusion about where to find input templates, etc).
I figured something like the following simplified example would be handy
<input type="text" class="form__text" v-on:blur="blurred" v-bind:class="{ form__blurred : isBlurred }" />
<script>
var form = new Vue({
el : '.form__input',
data : {
isBlurred : false
},
methods : {
blurred : function() {
this.isBlurred = true;
}
}
});
</script>
That actually works great but, as expected, it seems like using a class selector only selects the first instance, and even if it didn't, I'm guessing changing the properties would apply to all elements, not each one individually.
So the question is - is this possible with pre-rendered HTML, in a way that's smarter than just looping through a selector?
If it is, is there a way to create the Vue on a .form element and apply this function to both .form__input and .form__textarea?
Or, as is probably the case, is this just not a good use-case for Vue (since this is actually a lot more code than the jQuery solution).
Sounds like a great use case for a Custom Directive.
Vue allows you to register your own custom directives. Note that in Vue 2.0, the primary form of code reuse and abstraction is components - however there may be cases where you just need some low-level DOM access on plain elements, and this is where custom directives would still be useful.
<div id="app">
<input type="text" name="myforminput" v-my-directive>
</div>
<script>
Vue.directive('my-directive', {
bind: function (el) {
el.onblur = function () {
el.classList.add('form__blurred');
}
}
});
var app = new Vue({
el: '#app'
});
</script>
You can also add the directive locally to a parent component, if it makes sense for your application.

React JSX style guide

What is the standard style for jsx? Specifically when HTML is intertwined with js. Are the parenthesis after return in the correct spot? Anyone know of any good formatters that don't mess everything up?
render: function(){
return (
<li>
<input id={this.props.id} type="checkbox" />
<label htmlFor={this.props.id}>{this.props.tag}</label>
</li>
);
}
I like to use Airbnb code style
with Web Storm Editor provide good support for jsx syntax.
Note: you can apply Airbnb style automatically inside Web Storm from Settings->Code Quality
render() {
return (
<MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>
);
}
Found a good style guide here
https://github.com/airbnb/javascript/blob/master/react/README.md

Resources