Reputation:
I'm working on a small project using Vue.Js, I followed a video tutorial to create the wizard, but now I try to validate the form using two css classes that I have already created 'is-valid' and 'is-invalid' but my validation doesn't works, this my code
<div class="modal-content tx-14">
<div class="p-0 bg-ui-01">
<ul class="nav nav-tabs nav-justified" id="myTab3" role="tablist">
<li class="nav-item m-0">
<a class="nav-link border-0 rounded-0" id="home-tab3" data-toggle="tab" href="#home3" role="tab" aria-controls="home" aria-selected="true" :class="{'active':current_step == 1}" @click.prevent="goToStep(1)">Website</a>
</li>
<li class="nav-item m-0">
<a class="nav-link border-0 rounded-0" id="profile-tab3" data-toggle="tab" href="#profile3" role="tab" aria-controls="profile" aria-selected="false" :class="{'disabled':max_step < 2, 'active': current_step == 2}" @click.prevent="goToStep(2)">Location</a>
</li>
<li class="nav-item m-0">
<a class="nav-link border-0 rounded-0" id="contact-tab3" data-toggle="tab" href="#contact3" role="tab" aria-controls="contact" aria-selected="false" :class="{'disabled':max_step < 3, 'active': current_step == 3}" @click.prevent="goToStep(3)">Keywords</a>
</li>
<li class="nav-item m-0">
<a class="nav-link border-0 rounded-0" id="contact-tab3" data-toggle="tab" href="#contact4" role="tab" aria-controls="contact" aria-selected="false" :class="{'disabled':max_step < 4, 'active': current_step == 4}" @click.prevent="goToStep(4)">Crawler</a>
</li>
<li class="nav-item m-0">
<a class="nav-link border-0 rounded-0" id="contact-tab3" data-toggle="tab" href="#contact4" role="tab" aria-controls="contact" aria-selected="false" :class="{'disabled':max_step < 5, 'active': current_step == 5}" @click.prevent="goToStep(5)">Competition</a>
</li>
</ul>
</div>
<div class="modal-body">
<div class="tab-content" id="myTabContent">
<div class="fade show active" id="home" role="tabpanel" aria-labelledby="home-tab" v-show="current_step == 1">
<h4 class="text-center">Create Your Project</h4>
<p class="text-center">Enter your website informations, so you can track and improve your SEO traffic and keep an eye on the competition.</p>
<div class="row row-sm">
<div class="col-sm-12">
<div class="form-group">
<input type="url" id="project_url" v-model="rules.project_url.string" class="form-control" placeholder="Project URL">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<input type="text" class="form-control" id="project_name" v-model="rules.project_name.string" placeholder="Project name">
</div>
</div>
</div>
</div>
<div id="location" role="tabpanel" aria-labelledby="profile-tab" v-show="current_step == 2">
<h4 class="text-center">Choose Your Location</h4>
<p class="text-center">
Enter all of the countries or cities you do business in or want traffic from. We recommend that you add at least 3 locations.
</p>
<div class="row row-sm">
<div class="col-sm-4">
<div class="form-group">
<select class="custom-select">
<option selected>Select Engine</option>
<option value="1">Google</option>
<option value="2">Google Mobile</option>
</select>
</div>
</div>
<div class="col-sm-8">
<div class="form-group">
<input type="text" class="form-control" placeholder="Target Location">
</div>
</div>
</div>
<div class="row row-sm">
<div class="col-sm-4">
<div class="form-group">
<select class="custom-select">
<option selected>Select Engine</option>
<option value="1">Google</option>
<option value="2">Google Mobile</option>
</select>
</div>
</div>
<div class="col-sm-8">
<div class="form-group">
<input type="text" class="form-control" placeholder="Target Location">
</div>
</div>
</div>
</div>
<div id="keywords" role="tabpanel" aria-labelledby="contact-tab" v-show="current_step == 3">
<h4 class="text-center">Add Keywords</h4>
<p class="text-center">
Choose the keywords you would like to track across the selected search engines. Track on the national level, or add locations to track on the local level. Add labels to group keywords by topic.
</p>
<textarea class="form-control" cols="30" rows="10" placeholder="Add separated keywords by comma ex: pasta,fish"></textarea>
</div>
<div id="crawling" role="tabpanel" aria-labelledby="contact-tab" v-show="current_step == 4">
<h4 class="text-center">Set Crawl Limit</h4>
<p class="text-center">
We crawl your site weekly to surface technical site issues that may be impacting your SEO performance. Select the number of pages you’d like to have crawled on this site.
</p>
<div class="row row-sm">
<div class="col-sm-4">
<div class="form-group">
<select class="custom-select">
<option value="5000">5,000</option>
<option value="10000">10,000</option>
<option value="20000">20,000</option>
<option value="30000">30,000</option>
<option value="40000">40,000</option>
<option value="50000">50,000</option>
<option value="75000">75,000</option>
<option value="100000">100,000</option>
<option value="250000">250,000</option>
<option value="450000">450,000</option>
</select>
</div>
</div>
<div class="col-sm-8">
<div class="form-group">
<select class="custom-select">
<option selected>Whole Website</option>
<option value="1">Exclude sub-domains</option>
<option value="2">Follow sitemap</option>
</select>
</div>
</div>
</div>
</div>
<div id="crawling" role="tabpanel" aria-labelledby="contact-tab" v-show="current_step == 5">
<h4 class="text-center">Benchmark vs Competitors</h4>
<p class="text-center">
We will track competitive link and keyword metrics for these sites.
</p>
<div class="row row-sm">
<div class="col-sm-12">
<div class="form-group">
<input type="text" class="form-control" placeholder="Target Location">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<input type="text" class="form-control" placeholder="Target Location">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<input type="text" class="form-control" placeholder="Target Location">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<input type="text" class="form-control" placeholder="Target Location">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary tx-13" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary tx-13" @click="advanceStep">
<span v-if="max_step === 5">Submit</span>
<span v-else>Next</span>
</button>
</div>
</div>
Javascript
<script>
new Vue({
el: "#page",
delimiters: ['[[', ']]'],
data (){
return {
rules: {
project_url: {
pattern: /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,
string: ''
},
project_name: {
pattern: /^(?!\s*$).+/,
string: ''
}
},
current_step: 1,
max_step: 1
}
},
methods:{
is_valid(rules){
$.each(rules, function(index, value){
if (value.string.match(value.pattern)) {
$('#' + index).removeClass('is-invalid')
$('#' + index).addClass('is-valid')
nextStep = true
} else {
$('#' + index).removeClass('is-valid')
$('#' + index).addClass('is-invalid')
nextStep = false
}
})
return nextStep
},
validate(){
if(this.current_step === 1){
if(this.is_valid(this.rules))
return true
}
if(this.current_step === 2)
return true
if(this.current_step === 3)
return true
if(this.current_step === 4)
return true
if(this.current_step === 5)
return true
},
goToStep(value){
if(!this.validate())
return
this.current_step = value
},
submitForm(){
alert("submit")
},
advanceStep(){
if(!this.validate())
return
if(this.max_step === 5)
return this.submitForm()
this.current_step++
if(this.max_step < this.current_step)
this.max_step = this.current_step
}
}
})
</script>
can anyone clean my code and help me to add form validation
Upvotes: 1
Views: 1451
Reputation: 8329
The basic idea is that you don't validate the form itself, but the data that is displayed in the form.
You might think that "but they are the same" or "it's a subtle difference", but no.
With jQuery you are going through the form UI - a part of the application, that is THE END RESULT of whole lot of other things. Don't do that. Store your form data in Vue's data option, and only display the result of validations.
Vue.component('InputField', {
props: ['error', 'value'],
computed: {
inputValue: {
get() {
return this.value
},
set(val) {
this.$emit("update:value", val)
}
},
},
template: `
<div
class="input-field-wrapper"
:class="{ error: error }"
>
<label>
INPUT:
<input
type="text"
placeholder="Type in something"
v-model="inputValue"
/>
<span v-if="error">this field is required!</span>
</label>
</div>
`
})
new Vue({
el: "#app",
data() {
return {
form: [],
canSubmit: false,
}
},
methods: {
handleAddInput(id) {
this.form.push({
id,
error: null,
value: null,
})
},
handleFormValidate() {
this.form = this.form.map(({ id, error, value }) => {
return {
id,
error: !value,
value,
}
})
return this.form.every(({
error
}) => !error)
},
handleResetValidation() {
this.form = this.form.map(field => {
return {
...field,
error: false
}
})
},
handleSubmitForm() {
this.canSubmit = this.handleFormValidate()
},
},
template: `
<div>
<button
@click="handleAddInput(form.length + 1)"
>ADD INPUT FIELD +</button>
<button
@click="handleFormValidate"
>VALIDATE FORM</button>
<button
@click="handleResetValidation"
>RESET VALIDATION</button>
<hr />
<form>
<input-field
v-for="(inputField, i) in form"
:key="inputField.id"
:error="inputField.error"
:value.sync="inputField.value"
></input-field>
<hr />
<button type="submit" @click.prevent="handleSubmitForm">SUBMIT FORM</button>
</form>
FORM CAN BE SUBMITTED: {{ canSubmit }}
</div>
`
})
.input-field-wrapper {
padding: 8px 16px;
color: black;
}
.input-field-wrapper.error {
color: white;
background: red;
transition: all 0.1s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
The snippet above is pretty simple:
form
) that stores all the data we want to get through our form & the very important error stateform
attributeInputField
component for every object that it finds in the form
data attributeInputField
component does nothing, really, only renders a text input element (with an error class, if the input field doesn't validate) & emits the value of the text input back to the parent (so that can store it in its form
data)form
data, check the value
of every element, and set the error
if the value
doesn't validate (here the rule is a simple "does it have a value at all?", but any validation could be put in place). The error
state is passed down on the InputField
component (:error="inputField.error"
) & that component reacts immediately to the state changeform
item (thus resetting the error display in the InputFields
component)You can see that it's not necessary to directly touch the DOM if you set up your components in a "data-driven" way (it is very rare, that you really can't avoid meddling with the DOM with a reasonable amount of work). You have much more freedom to work out the logic of your application if you decouple data & UI.
And you don't need jQuery to do the work.
Upvotes: 1