Vue3: How to get error messages with Bootstrap and VeeValidate 4? - forms

I want to use vue3 together with bootstrap 4/5 with veevalidate 4.
<template>
<Form as="form" #submit.prevent="onFormSubmit" class="needs-validation" :class="{ 'was-validated': wasValidated }">
<div class="form-group">
<label for="firstNameId">First Name *</label>
<Field name="firstname" as="input" id="firstNameId" type="text" rules="required|firstname" class="form-control" placeholder="First Name" v-model="firstName" aria-describedby="input-true input-false input-help" aria-invalid="true" />
<ErrorMessage as="div" name="firstname" v-slot="{ message }">
{{ message }}
<div class="invalid-feedback">
{{ message }}
</div>
</ErrorMessage>
<div class="valid-feedback">Good!</div>
</div>
<Form>
</template>
<script>
import { Field, Form, ErrorMessage, defineRule } from 'vee-validate';
defineRule('required', value => {
if (!value || !value.length) {
return 'This field is required.';
}
return true;
});
defineRule("firstname", (value) => {
if (!/^[a-zA-Z0-9( ),'.:/-]+$/i.test(value)) {
return "Please use only letters, numbers and the following special characters: ( ),'.:/-";
}
return true;
});
export default {
components: {
Form,
Field,
ErrorMessage
},
data () {
return {
firstName: "",
wasValidated: false,
},
methods: {
onFormSubmit(values) {
alert(JSON.stringify(values, null, 2));
console.log("Submitted");
console.log(values);
var forms = document.getElementsByClassName('needs-validation');
// Loop over them and prevent submission
var validation = Array.prototype.filter.call(forms, function(form) {
form.addEventListener('submit', function(event) {
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
this.wasValidated = true;
}, false);
});
},
},
};
</script>
The problem is that I can't activate the div with the class invalid-feedback or valid-feedback.
I could add the class was-validated to the <form>-tag, but I get feedback first after the second click on the submit button.

<template>
<Form as="form"
#submit="onFormSubmit"
class="needs-validation"
:validation-schema="schema"
v-slot="{ errors }"
>
<div class="form-group">
<label for="firstNameId">First Name *</label>
<Field
name="firstName"
type="text"
placeholder="First Name"
v-model="firstName"
aria-describedby="input-true input-false input-help"
aria-invalid="true"
v-slot="{ meta, field }"
>
<input
v-bind="field"
name="firstName"
type="text"
class="form-control"
:class="{
'is-valid': meta.valid && meta.touched,
'is-invalid': !meta.valid && meta.touched,
}"
/>
</Field>
<ErrorMessage as="div" name="firstname" v-slot="{ message }" class="invalid-feedback">
{{ message }}
</ErrorMessage>
<Form>
</template>
<script setup>
import { markRaw } from 'vue';
import { Field, Form, ErrorMessage} from 'vee-validate';
import * as yup from 'yup';
</script>
<script>
export default {
components: {
Form,
Field,
ErrorMessage
},
data () {
return {
schema: markRaw(yup.object().shape({
firstName: yup.string().min(0).max(20).label('First Name'),
})),
};
},
methods: {
onFormSubmit(values) {
console.log(JSON.stringify(values, null, 2));
},
},
};
</script>

Related

Semantic UI Custom Rule Validation for Select Drop-Down Field

I am trying to add some custom logic to my semantic ui validation but can't figure out what I am doing wrong.
Basically, when the user selects "Yes" from the drop-down, I would like to make the "input_field" mandatory. If the user selects "No", the "input_field" becomes optional and the form can be submitted.
I tried searching for examples and followed some code from the Semantic Ui website but can't figure out what I am missing. Any advice would be appreciated as I am on a deadline for a project I am working on.
Form:
<div class="ui dimmer">
<div class="ui huge text loader">Loading</div>
</div>
<form method="post" action="" class="ui form" autocomplete="on">
<div class="ui segment">
<div class="ui two fields">
<div class='field'>
<div class="ui selection dropdown">
<input type="hidden" class="selectOption" name="select">
<i class="dropdown icon"></i>
<div class="default text">Select an option</div>
<div class="menu">
<div class="item" data-value="Yes">Yes</div>
<div class="item" data-value="No">No</div>
</div>
</div>
</div>
<div class="field">
<input id="input_field" name="input_field" type="text"/>
</div>
</div>
</div>
<button id="submit" class="ui green button" name="submit" type="submit">Submit</button>
</form>
Validation:
<script>
$('.ui.form').form({
keyboardShortcuts: false,
on: 'blur',
inline: 'true',
fields: {
selectOption: {
identifier: 'select',
rules: [
{
type: 'empty',
prompt: 'Please select an option'
}]
},
input_field: {
identifier: 'input_field',
depends: 'select',
rules: [
{
type: 'empty',
prompt: function() {
$('.select').on('change', function() {
if( this.value == 'Yes') {
return "Custom Validation";
}
return false;
}).trigger("change");
}
}]
}
},
onSuccess: function() {
$('.ui.dimmer').dimmer('show');
},
onFailure: function() {
event.preventDefault();
}
}
);
});
</script>
Figured out a solution for this! It might not be the best answer but it works and does what I am looking for.
<div class="ui dimmer">
<div class="ui huge text loader">Loading</div>
</div>
<form method="post" action="" class="ui form" autocomplete="on">
<div class="ui segment">
<div class="ui two fields">
<div class='field'>
<div class="ui selection dropdown">
<input type="hidden" class="selectOption" name="select">
<i class="dropdown icon"></i>
<div class="default text">Select an option</div>
<div class="menu">
<div class="item" data-value="Yes">Yes</div>
<div class="item" data-value="No">No</div>
</div>
</div>
</div>
<div class="field">
<input id="input_field" name="input_field" type="text"/>
</div>
</div>
</div>
<button id="submit" class="ui green button" name="submit" type="submit">Submit</button>
</form>
<script>
$('.ui.form').form({
keyboardShortcuts: false,
on: 'blur',
inline: 'true',
fields: {
selectOption: {
identifier: 'select',
rules: [
{
type: 'empty',
prompt: 'Please select an option'
}]
}
},
onSuccess: function() {
$('.ui.dimmer').dimmer('show');
},
onFailure: function() {
event.preventDefault();
}
}
);
$('.selectOption').on('change', function() {
if ( this.value == 'Yes' ) {
$('.ui.form').form('add rule', 'input_field', ['empty', 'integer']);
$('.ui.form').form('add prompt', 'input_field', 'Enter an integer');
}
if ( this.value == 'No' ) {
$('.ui.form').form('remove prompt', 'input_field');
$('.ui.form').form('remove rule', 'input_field');
}
}).trigger("change");
});
</script>
I was able to implement the validation rule by extending Semantic UI setting rules.
See below example:
$.fn.form.settings.rules.dependsOnFieldValue = function (value, dependFieldValue) {
var identifier = dependFieldValue.split('[')[0];
var dependValue = dependFieldValue.match(/\[(.*)\]/i)[1];
if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
matchingValue = $('[data-validate="'+ identifier +'"]').val();
}
else if($('#' + identifier).length > 0) {
matchingValue = $('#' + identifier).val();
}
else if($('[name="' + identifier +'"]').length > 0) {
matchingValue = $('[name="' + identifier + '"]').val();
}
else if( $('[name="' + identifier +'[]"]').length > 0 ) {
matchingValue = $('[name="' + identifier +'[]"]');
}
return (matchingValue !== undefined)
? !( dependValue.toString() === matchingValue.toString() && value === '')
: false
;
};
Then in the form validation initializer you will pass the desired values as below:
$(".ui.form").form({
fields: {
select: {
identifier: 'select',
rules : [
{
type : 'empty'
}
]
},
input_field: {
identifier : 'input_field',
rules : [
{
type : 'dependsOnFieldValue[select[Yes]]',
}
]
},
...
}
});
Notice that we pass the <select> identifier (in this case also called select) within the first [] and then the value that we want to see to make the input_field mandatory ("Yes" in this case).

How to enable required validation in vuelidate based on onChange event of select field

I have to enable required validation for the input field based on the onChange event of select field. I'm using vuelidate package for form validation in my project. Kindly provide solution to accomplish it.
My Template Code Below:
<template>
<section class="page_blk">
<form #submit="submitForm($event)" class="cryp_form">
<div class="input_ctrl_wrp">
<label for="username">Template</label>
<div class="input_select">
<select #change="getTemplate" v-model="$v.adForm.tnc.$model" name="" id="">
<option value="">Select</option>
<option value="New">New</option>
<option :value="term.idTnCTemplate"
v-for="term in termsList"
:key="term.idTnCTemplate">{{term.title}}</option>
</select>
<i class="fal fa-angle-down"></i>
</div>
</div>
<div class="input_ctrl_wrp">
<label for="username">Title</label>
<div class="input_text">
<input v-model="$v.adForm.title.$model" placeholder="" type="text">
</div>
</div>
<div class="input_ctrl_wrp">
<label for="username">Terms Of Trade</label>
<div class="input_textarea">
<textarea v-model="$v.adForm.content.$model" name="" rows="10"></textarea>
</div>
</div>
</form>
</section>
</template>
My Script Below:
<script>
import { required,requiredIf, decimal, numeric } from "vuelidate/lib/validators";
export default {
data() {
return {
adForm: {
tnc: '',
title: '',
content: '',
}
}
},
validations: {
adForm: {
tnc: {
required
},
title: {
required
},
content: {
required: requiredIf( (abc) => {
console.log('abc',abc)
return true;
})
},
schedule: {
required
}
}
},
methods: {
submitForm(e) {
},
getTemplate(e) {
}
},
mounted() {
}
}
</script>
I want to toggle the validation to required for the title and content field, if the user select new and other option from dropdown. Please provide solution to accomplish it. Thanks in advance.

Algolia - DateTimePicker - Select Date

I would like to implement a date timepicker with Algolia. If I choose a date, all elements should be displayed from this date.
Unfortunately, I have no idea how I can make this with Agolia.
I hope you can help me.
const datePicker = instantsearch.connectors.connectRange(
(options, isFirstRendering) => {
if (!isFirstRendering) return;
new Calendar({
element: $('.daterange--single'),
current_date: new Date(),
format: {input: 'DD.MM.YYYY'},
required: false,
callback: function() {
const start = new Date().getTime();
refine([start]);
},
});
}
);
search.addWidget(
datePicker({
attributeName: 'date',
})
);
<div class="daterange daterange--single"></div>
now i have a working code. Now a have the problem.. how i can change in my custom widget the searchParameters?
const search = instantsearch({
appId: '0000000',
apiKey: '000000000000',
indexName: 'Events',
routing: true,
searchParameters:{
filters: 'dateNumeric >= 1531591200'
}
});
var customWidget = {
init: function(options) {
$( "#datetimepickerNotime" ).focusout(function() {
var date = $('#datefromdatetimepicker').val();
var date = new Date (date);
alert("dateNumeric >= 1512752400");
});
}
};
search.addWidget(customWidget);
<div class='input-group date' id="datetimepickerNotime">
<input type='text' id="datefromdatetimepicker" name="date" class="form-control" autocomplete="off" value="<f:format.date date='now' format='d.m.Y' />"/>
<span class="input-group-addon">
<span class="fa fa-calendar"></span>
</span>
</div>
import { Formik, Field } from 'formik';
import { connectRange } from 'react-instantsearch-dom';
import * as Yup from 'yup';
const RangeFilter = ({ currentRefinement, min, max, refine }) => {
const validationSchema = Yup.object().shape({
minValue: Yup.number().min(min, `Minimum value must be at least ${min}`),
maxValue: Yup.number().max(max, `Maximum value must be at most ${max}`),
});
const onSubmit = (values) => {
refine({ min: values.minValue, max: values.maxValue });
};
return (
<Formik
onSubmit={onSubmit}
validationSchema={validationSchema}
initialValues={{
minValue: currentRefinement?.min,
maxValue: currentRefinement?.max,
}}>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Field name="minValue">
{({ field, form: { errors, touched } }) => (
<div>
<label htmlFor="price-min">To price:</label>
<input
className="input is-shadowless"
{...field}
id="price-min"
type="number"
placeholder="Min price"
/>
{errors.minValue && touched.minValue && (
<p className="help is-danger">{errors.minValue}</p>
)}
</div>
)}
</Field>
<Field name="maxValue">
{({ field, form: { errors, touched } }) => (
<div className="mt-2">
<label htmlFor="price-max">From price:</label>
<input
{...field}
id="price-max"
type="number"
className="input is-shadowless"
placeholder="Max price"
/>
{errors.maxValue && touched.maxValue && (
<p className="help is-danger">{errors.maxValue}</p>
)}
</div>
)}
</Field>
<button type="submit" className="ais-RefinementList-showMore mt-2">
Get Result
</button>
</form>
)}
</Formik>
);
};
export default connectRange(RangeFilter);

Can't clear form/state after input in React.js

I have a form which ultimately will be used as the UI to make some API calls to Open weather map.
Right now when I submit the a zip code in the input field, upon submission [object Object] propagates the field like in the screen shot below.
The call to the API is working as I am getting the JSON for the correct zip code...
But shouldn't this in the handleSubmit take care of everything i.e. using Object.assign to create new state and then using form.zipcode.value = ''; to clear out the input?
Thanks in advance!!
handleSubmit(event) {
event.preventDefault();
var form = document.forms.weatherApp;
api.getWeatherByZip(this.state.zipcode).then(
function(zip) {
console.log('zip', zip);
this.setState(function() {
return {
zipcode: Object.assign({}, zip),
};
});
}.bind(this)
);
form.zipcode.value = '';
}
I have enclosed all of the component's code here.
import React, { Component } from 'react';
import * as api from '../utils/api';
import '../scss/app.scss';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
zipcode: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
zipcode: event.target.value,
});
}
handleSubmit(event) {
event.preventDefault();
var form = document.forms.weatherApp;
api.getWeatherByZip(this.state.zipcode).then(
function(zip) {
console.log('zip', zip);
this.setState(function() {
return {
zipcode: Object.assign({}, zip),
};
});
}.bind(this)
);
form.zipcode.value = '';
}
render() {
return (
<div className="container">
<form name="weatherApp" onSubmit={this.handleSubmit}>
<h2>Open Weather App</h2>
<div className="row">
<div className="one-half column">
<label htmlFor="insertMode">Insert your location</label>
<input
name="zipcode"
className="u-full-width"
placeholder="please enter your zipcode"
type="text"
autoComplete="off"
value={this.state.zipcode}
onChange={this.handleChange}
/>
</div>
<div className="one-half column">
<label htmlFor="showMin">show minimum</label>
<input type="checkbox" />
<label htmlFor="showMax">show maximum</label>
<input type="checkbox" />
<label htmlFor="showMean">show mean</label>
<input type="checkbox" />
</div>
</div>
<div className="row">
<div className="two-half column">
<input type="submit" value="Submit" />
</div>
</div>
</form>
</div>
);
}
}
You should let react manage the changes to the DOM rather that editing it manually. As the value of your input field is already bound to this.state.zipcode to reset it just invoke this.setState({zipcode: ''}) instead of form.zipcode.value='';.

React updating state in two input fields from form submission

I am trying to make a simple contact form using React. Eventually I will send the data collected from the state to a database, but for right now I am trying to just get it to console log the correct values.
Right now, the email field overrides the name field and when I console log both states, name shows up and email is undefined. Here is my React Component
import React, { Component, PropTypes } from 'react';
import ContactData from '../data/ContactData.js';
class FormContact extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
email: '',
textArea: ''
}
}
handleChange(event) {
event.preventDefault();
this.setState({
name: event.target.value,
email: event.target.email
})
}
handleSubmit(event) {
event.preventDefault();
console.log(this.state.name + ' ' + this.state.email);
}
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<label> Name:
<input type="text" placeholder="Name" value={this.state.name} onChange={this.handleChange.bind(this)} />
</label><br />
<label> Email:
<input type="text" placeholder="Email" value={this.state.email} onChange={this.handleChange.bind(this)}/>
</label><br />
<input className="btn btn-primary" type="submit" value="Submit" />
</form>
)
}
}
FormContact.PropTypes = {
subName: PropTypes.string,
subEmail: PropTypes.string
}
FormContact.defaultProps = {
subName: 'Sam',
subEmail: ''
}
class Contact extends Component {
render() {
return (
<div>
<h1>CONTACT PAGE</h1>
<FormContact />
</div>
)
}
}
export default Contact;
If I understand what you want, you could do it as follows :
Add an empty object in your state for the form values
formValues: {}
Add the name attribute to your fields
<input name="name" .... />
<input name="email" .... />
then depending on that name update your state in handleChange function
let formValues = this.state.formValues;
let name = event.target.name; // Field name
let value = event.target.value; // Field value
formValues[name] = value;
this.setState({formValues})
And if the values go one level deeper, you could use
value={this.state.formValues["name"]} instead of value={this.state.name} - where name is the value of the name attribute of your input field
Thus, everything together should be as follows :
class Test extends React.Component {
constructor(props) {
super(props)
this.state = {
formValues: {}
}
}
handleChange(event) {
event.preventDefault();
let formValues = this.state.formValues;
let name = event.target.name;
let value = event.target.value;
formValues[name] = value;
this.setState({formValues})
}
handleSubmit(event) {
event.preventDefault();
console.log(this.state.formValues);
}
render(){
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<label> Name:
<input type="text" name="name" placeholder="Name" value={this.state.formValues["name"]} onChange={this.handleChange.bind(this)} />
</label><br />
<label> Email:
<input type="text" name="email" placeholder="Email" value={this.state.formValues["email"]} onChange={this.handleChange.bind(this)}/>
</label><br />
<input className="btn btn-primary" type="submit" value="Submit" />
</form>
)
}
}
React.render(<Test />, document.getElementById('container'));
Here is fiddle.
Hope this helps.
The reference to event.target.email does not exist on the event element. The value of a text input from an inline-event handler would be event.target.value for both email and name. The quick solution is to create a separate handler for each input:
handleChangeName(event) {
event.preventDefault();
this.setState({ name: event.target.value }); //<-- both use the same reference
} // to get the contextual value
handleChangeEmail(event) { // from the inputs v
event.preventDefault(); // |
this.setState({ email: event.target.value }); //<--------------------
}