I hava a form with WYSIWYG field and I use Quasar's q-editor element. I want to add validation to that field with rules and styles similar to other fields like q-input, q-select etc.
Sample for q-input validation is:
<q-input v-model="model" label="Name" :rules="[val => !!val || 'Field is required']" />
Result:
This example for required WYSIWYG field doesn't work:
<q-editor v-model="editor" min-height="5rem" :rules="[val => !!val || 'Field is required']" />
There are similar questions here and here (with one posible solution).
Actually Quasar logic is to use q-field wrapper element. Maybe it is a good idea to be added as desctription in q-editor page.
That I do is wrap q-editor in q-field. When q-editor is in template #control on q-field, val for validation val => !!val || 'Field is required' is the content of q-editor. Maybe the reason is that the v-model is same for the two elements.
The problem is when validation failed, then the border of the q-field becomes red, and not of the q-editor. For this problem I use solution from the second link in the question - added custom style to border of q-editor and q-field is borderless.
<q-field ref="fieldRef" v-model="editor" label-slot borderless
:rules="[val => (!!val && val !== '<br>') || 'Field is required']" >
<template #label>Description</template>
<template #control>
<q-editor v-model="editor" min-height="5rem" class="full-width"
:style="fieldRef && fieldRef.hasError ? 'border-color: #C10015' : ''"/>
</template>
</q-field>
Result:
Here is the CodePen example.
Related
I have a v-select, written like this :
<v-select
ref="v_select_folder"
dense
hide-details
v-model="selectedPlanFolder"
label="PlanFolder"
:items="PlannedFolder"
item-text="node.name"
return-object
#change="setFolderNameAndID"
>
The v-select gets the available options/values dropdown from :items, I would like to know how if there is a simple way I can add an extra aditional option , without touching the items prop. Something like :
<v-select
ref="v_select_folder"
dense
hide-details
v-model="selectedPlanFolder"
label="PlanFolder"
:items="PlannedFolder"
item-text="node.name"
return-object
#change="setFolderNameAndID"
>
<option>This is another select value</option>
</v-select>
You can create a new array in place with the spread operator and add an item to it. You may want to add additional properties to the added item depending on how you process your data.
<v-select
ref="v_select_folder"
dense
hide-details
v-model="selectedPlanFolder"
label="PlanFolder"
:items="[...PlannedFolder, { node: { name: 'name' } }]"
item-text="node.name"
return-object
#change="setFolderNameAndID"
>
</v-select>
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.
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.
I'm trying to test a component builded with vuetify. For that I'm using vue-test-utils. My goal is to perform a change in the v-select field, and assert hat a mutation is performed. here is my code:
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-layout row wrap>
<v-flex xs6>
<v-subheader>Standard</v-subheader>
</v-flex>
<v-flex xs6>
<v-select
:items="items"
v-model="e1"
label="Select"
single-line
></v-select>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
The first test is ok when i set the data:
componentWrapper.setData({items: storesList})
expect(componentWrapper.vm.$data.items).toHaveLength(2)
I now want to change the selected value I try:
componentWrapper.findAll('#option').at(1).setSelected()
And also
componentWrapper.find('el-select')
Can someone help me to retrieve the select and then change the selected value?
Thanks for your support.
Use wrapper.vm.selectItem('foo'). It works for me.
I found this in vuetify v-select tests:
Old: ~~https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/test/unit/components/VSelect/VSelect.spec.js#L533~~
New: https://github.com/vuetifyjs/vuetify/blob/b2abe9fa274feeb0c5033bf12cc48276d4ac5a78/packages/vuetify/test/unit/components/VSelect/VSelect.spec.js#L28
Edit: Updated link.
wrapper.findAll('.v-list__tile__title').at(0).trigger('click')
It works for me.
By this code, first option is selected.
Here is ref.
I used Vuetify v1.5.5.
My solution. It's very equivalent to YounSeon Ahn's response, just the options selector changed. I'm using Vuetify 2.2.11.
// Found the v-select
wrapper.find('[data-testid="mySelect"]').trigger('click');
// Wait for DOM updating after the click
await localVue.nextTick();
// Find all options and select the first. If you are multiple v-select in your form, the class ".menuable__content__active" represents the current opened menu
wrapper.find('.menuable__content__active').findAll('.v-list-item').at(0).trigger('click');
finally got this working for vuetify 1.5. If your v-select looks like this:
<v-select
:items="projectManagers"
:value="selectedProjectManager"
key="UserId"
label="Choose Name"
item-text="FirstName"
item-value="UserId"
id="nameSelect"
#input="(val)=>{this.handleNameChange(val)}"
/>
then do this:
wrapper.findAll('#nameSelect').at(0).trigger('input')
this works, but for some reason if i do at(1) it does not work. Thankfully for my test case the option at position 0 is fine
This is my final solution to test Vuetify VSelect component in cypressjs:
cy.get('#YourVSelectComponentId', { timeout: 20000 }).should('not.have.attr', 'disabled', 'disabled').click({ force: true }); //find v-select, verify it's visible and then click.
cy.get('.menuable__content__active').first().find('.v-list__tile').first().should('be.visible').click(); //find opened dropdown, then find first item in options, then click
I have been struggling with this as well and finally found a way to do it. I'm using Jest and vue-test-utils. Basically, you have to do it with two separate steps. Mark the option as selected and then trigger the change on the select.
The HTML part is:
<select id="groupid" v-model="groupID">
<option value="">-Select group-</option>
<option v-for="g in groupList"
v-bind:value="g.groupId">{{g.groupId}} - {{g.groupName}}
</option>
</select>
And the test is:
it('should populate the subgroup list when a group is selected', () => {
expect(cmp.vm.groupID).toBe('');
expect(cmp.findAll('select#groupid > option').length).toBe(3);
cmp.findAll('select#groupid > option').at(1).element.selected = true;
cmp.find('select#groupid').trigger('change');
expect(cmp.vm.groupID).not.toBe('');
});
Say I have a validation rules for some Model, for example validation for a person model will be:
'first_name' => ['required', 'string'],
'last_name' => ['required', 'string'],
'birthday' => ['before:today', 'date'],
'salary' => ['min:0', 'max:2000', numeric],
....
So if I wrote that rules, it feels wrong to write the same rules manually but for the HTML form fields like:
<input type="text" name="first_name" required />
<input type="text" name="last_name" required />
<input type="date" name="birthday" max="2016-06-09"/>
<input type="number" name="salary" min="0" max="2000"/>
So if the product owner ask me for change the rules like changing the mandatory fields, or even change the maximum salary from 2000 to 5000, I have to change it manually in the validation rules and the form itself.
So it makes me wonder, is there any automatic way to convert Laravel validation rules to the HTML form fields?
You have to parse your rules, then loop on the parsed datas for building a form. And then, I suggest you to use partial views for doing the trick.
I already did this for building automatic forms and documentations. So i wrote a Laravel package here : https://github.com/Ifnot/ValidationParser.
In the example of my package you just have to create two files :
A form blade view (contains the code for parsing the validation)
A field blade view (used for display a form item)
To have the validation rules be in one place, set the rules to variables. Then, pass the variables into the laravel validation page and in your blade template (html).
So, where you are setting the variable:
$MaxSalary = 2000;
Next, pass in your variable to the Laravel form validation rules:
'salary' => ['min:0', "max:$MaxSalary", numeric],
Then, pass it into your blade template form:
return view('form', ['MaxSalary' => $MaxSalary]);
Then, in your blade template, use the variable:
<input type="number" name="salary" min="0" max="{{ MaxSalary }}"/>
I had problems finding some one else thinking about this same idea. I must have not been using the right terms for the search. I implemented this same concept today 2023 in the following project: https://github.com/arielenter/ValidationRulesToInputAttributes
In it, I'm using laravel's Illuminate\Validation\ValidationRuleParser to explode and parse the rules, and later I used a 'translator' that convert applicable rules to input attributes.
My concern is why it seems nobody has made a laravel package that can do this on 2023. I'm not sure if I'm up to the task, but if nobody has done it I'll try. I just think it'll be very odd if nobody more capable has done it. The most difficult part I think would be to make an extend dictionary for every possible attribute that could be apply depending of the rule. I might end up leaving to the user to provide its own dictionary or something, but some cases are conditional so I'm not sure if that could work. For now I'll just keep adding translation every time I need it.