Validations in Vue using Vuelidate are not working - javascript

Hey guys
I wonder if I miss something here, iv'e trying to figure it out for a few hours and didn't came up with a solution.
I'm trying to use form validations using Vuelidate in vue, everything seems in place but after the custom alert I created, the form is still proceeding to the next stage.
I declared Vuelidate like this:
import useVuelidate from '#vuelidate/core'
import { required } from '#vuelidate/validators'
Inside Data() I did as follows:
data: () => ({
v$: useVuelidate(),
currentStage: 1,
traffic: {
unique: '',
unique1: '',
unique2: '',
unique3: '',
unique4: ''
},
}),
Declared validations outside of data(), like this:
validations() {
return {
traffic: {
unique1: { required },
unique2: { required },
unique3: { required },
unique4: { required },
unique5: { required }
},
}
},
Last thing is the computed, which I have function that is creating the input fields in stage 3 of the form:
computed: {
appendFields() {
this.v$.$validate()
if(!this.v$.$error){
if(this.jsonStatham.hasOwnProperty(this.traffic.unique1)){
this.integrationParams.push(...Object.keys(this.jsonStatham[this.traffic.unique1]))
}
} else {
alert("Error, Not all fields are filled in.")
}
}
},
So here is the problem, when appendFields() called, I do get this alert: alert("Error, Not all fields are filled in.")
But After I press "ok" in the alert, the form is still proceeding to the next stage.
What am I missing?
Edit:
This is the button who execute the "appendFields" method:
<button #click="buttonClicked(0); appendFields;">Next Stage</button>
And this is buttonClicked function:
buttonClicked(value) {
if(value === 0){
this.currentStage++;
return this.currentStage;
}
if(value === 1){
this.currentStage--;
return this.currentStage;
}
},

The click-handler updates currentStage without first validating the form. However, the validation occurs in appendFields, which is computed after buttonClicked(). The validation should be the first step, which can block the proceeding steps.
I would refactor it like this:
Make appendFields a component method, since it's not really a computed property (especially because it returns nothing).
Move the currentStage update into its own function for clarity.
Move the form validation from appendFields() to the button's click-handler.
In the click-handler, call the functions created in step 1 and 2 if the form is valid.
export default {
methods: {
// 1️⃣
appendFields() {
if (this.jsonStatham.hasOwnProperty(this.traffic.unique1)) {
this.integrationParams.push(...Object.keys(this.jsonStatham[this.traffic.unique1]))
}
},
// 2️⃣
updateStage(value) {
if (value === 0) {
this.currentStage++
} else if (value === 1) {
this.currentStage--
}
},
buttonClicked(value) {
// 3️⃣
this.v$.$validate()
if (!this.v$.$error) {
// 4️⃣
this.appendFields()
this.updateStage(value)
} else {
alert("Error, Not all fields are filled in.")
}
}
}
}
Also be aware that useValidate() is intended for the Composition API, so it should be called inside setup():
export default {
setup() {
return {
v$: useValidate()
}
}
}

Related

How can use computed for v-model?

I got data from API using computed. "UserModule.userInfo.usrEmail" is the state in my vuex. Like below:
data() {
return {
vModel: {
email: {
value: "",
},
}
}
}
computed: {
email:{
get: function(){
return UserModule.userInfo ? UserModule.userInfo.usrEmail : "";
},
set : function(email){
this.vModel.email.value = email
}
},
}
And then show it to user like below:
<input v-model="email"></input>
User can edit email and also cancel their edit process and return to their previous data but in edit everything was correct but when i want to cancel this process my previous data did not show in the input and i saw my new data which is not correct i want to cancel it. This is my cancel method on input:
resetInput(input) {
this.vModel.email.value = this.email
},
"this.email" refer to my computed which is get data from API.
How can i write this cancel process correctly and see my previous data in input tag?
so you can use this solution:
data() {
return {
useGet :{
email: true,
},
}
}
in your method:
resetInput(input) {
this.useGet.email = true
},
and in your computed:
email: {
get: function () {
if (this.useGet.email) {
return UserModule.userInfo ? UserModule.userInfo.usrEmail : ""
}
return ""
},
set: function (email) {
this.useGet.email = false
}
},
this is because of your UserModule.userInfo.usrEmail.
this state does not update.
your get in computed will be work when your UserModule.userInfo.usrEmail changes.

Javascript method to be able see if a value changes without re rendering the page

I am trying to be able to read a value that is boolean to see if a user did a specific action or not and I am using the ReactJS functional component style. I am trying to read the runValue in my code to see if the run() method changed the value and I want to be able to read this value without recalling the function.
I want to be able to put in my useEffect method this line of code;
Run.RunFunction().run((index) => {
if (index) {
\\\ do stuff here if index is true
} else {
///if index is false
}
}
my code
const Run = {
RunFunction: () => {
let runValue = false;
return {
run() {
runValue = true
},
listener: function(val) {},
checkStatus: function(listen) {
this.listener = listen
}
}
},
}
Run.RunFunction().checkStatus((index) => {
if (index) {
console.log('running')
} else {
console.log('not running')
}
});
I am having trouble having this code to work and I want to be able to see the value of the runValue initially and if it changes.
Thank you

How can I refactor repetitive conditional Vue.js code?

I have this code in my Vue.js component:
mounted() {
if (localStorage.dobDate) {
this.form.dobDate = localStorage.dobDate;
}
if (localStorage.dobMonth) {
this.form.dobMonth = localStorage.dobMonth;
}
if (localStorage.dobYear) {
this.form.dobYear = localStorage.dobYear;
}
},
watch: {
"form.dobDate": {
handler: function(after, before) {
localStorage.dobDate = after;
},
deep: true
},
"form.dobMonth": {
handler: function(after, before) {
localStorage.dobMonth = after;
},
deep: true
},
"form.dobYear": {
handler: function(after, before) {
localStorage.dobYear = after;
},
deep: true
}
Ask you can see it can get very repetitive, if for example I had a large form, and I don't want to do this for every field. Is there a way I can approach this to make it more DRY? Is there a way I can make it more dynamic for any field in a form for example?
In the mounted hook create an array of localStorage fields ["dobDate","dobMonth","dobYear"] and loop through it using forEach method, for each field localStorage[fieldName] check if it's defined using conditional operator, if it's defined assign it to the correspondant field name in the form data property else pass to the next element:
mounted(){
["dobDate","dobMonth","dobYear"].forEach(field=>{
localStorage[field]?this.form[field]=localStorage[field]:{};
})
}
In the watch property watch the form object deeply (watch its nested fields) then loop through its keys by doing the reciprocal operation made in mounted hook :
watch: {
form: {
handler: function(after, before) {
Object.keys(after).forEach(key=>{
localStorage[key]=after[key]
})
},
deep: true
}
}
Here is another approach with multiple (no deep) watchers.
data: {
form: {},
dateFields: ['dobDate', 'dobMonth', 'dobYear']
},
mounted() {
for (const dateField of this.dateFields) {
if (localStorage[dateField])
this.$set(this.form, dateField, localStorage[dateField])
}
},
created() {
for (const dateField of this.dateFields) {
this.$watch('form.' + dateField, function(after, before) {
localStorage[dateField] = after;
});
}
}
I ignore if it's more or less efficient than only one deep watcher. It may depends on the way your data change.
I'm sure you must have reasons for using localStorage for saving form data in localStorage, so with this code, you can pass the whole form object to localStorage and can retrieve that. in this case, any change in form would make this watch run
mounted() {
if (localStorage.form) {
this.form = localStorage.form
}
},
watch: {
"form": {
handler: function(after, before) {
localStorage.form = after;
},
deep: true
}
}

How to watch dynamic data fields in Vue.js

so i am trying to figure out whether this is possible, i am using a prop to dynamically name a field in data. So essentially i want to add a watcher to the dynamically added data field. Is this possible?
export default {
props: {
type: {
type: String,
required: true
}
},
data() {
return {
[this.type]: {
place_type: "house"
}
}
},
watch: {
"[this.type].place_type": function(val) {
console.log(val);
}
}
}
Here is my attempt, but it does not work with that syntax. Any ideas? Thanks

How to fire an event on Vue switch change

I have a Vue component that has a vue-switch element. When the component is loaded, the switch has to be set to ON or OFF depending on the data. This is currently happening within the 'mounted()' method. Then, when the switch is toggled, it needs to make an API call that will tell the database the new state. This is currently happening in the 'watch' method.
The problem is that because I am 'watching' the switch, the API call runs when the data gets set on mount. So if it's set to ON and you navigate to the component, the mounted() method sets the switch to ON but it ALSO calls the toggle API method which turns it off. Therefore the view says it's on but the data says it's off.
I have tried to change the API event so that it happens on a click method, but this doesn't work as it doesn't recognize a click and the function never runs.
How do I make it so that the API call is only made when the switch is clicked?
HTML
<switcher size="lg" color="green" open-name="ON" close-name="OFF" v-model="toggle"></switcher>
VUE
data: function() {
return {
toggle: false,
noAvailalableMonitoring: false
}
},
computed: {
report() { return this.$store.getters.currentReport },
isBeingMonitored() { return this.$store.getters.isBeingMonitored },
availableMonitoring() { return this.$store.getters.checkAvailableMonitoring }
},
mounted() {
this.toggle = this.isBeingMonitored;
},
watch: {
toggle: function() {
if(this.availableMonitoring) {
let dto = {
reportToken: this.report.reportToken,
version: this.report.version
}
this.$store.dispatch('TOGGLE_MONITORING', dto).then(response => {
}, error => {
console.log("Failed.")
})
} else {
this.toggle = false;
this.noAvailalableMonitoring = true;
}
}
}
I would recommend using a 2-way computed property for your model (Vue 2).
Attempted to update code here, but obvs not tested without your Vuex setup.
For reference, please see Two-Way Computed Property
data: function(){
return {
noAvailableMonitoring: false
}
},
computed: {
report() { return this.$store.getters.currentReport },
isBeingMonitored() { return this.$store.getters.isBeingMonitored },
availableMonitoring() { return this.$store.getters.checkAvailableMonitoring },
toggle: {
get() {
return this.$store.getters.getToggle;
},
set() {
if(this.availableMonitoring) {
let dto = {
reportToken: this.report.reportToken,
version: this.report.version
}
this.$store.dispatch('TOGGLE_MONITORING', dto).then(response => {
}, error => {
console.log("Failed.")
});
} else {
this.$store.commit('setToggle', false);
this.noAvailableMonitoring = true;
}
}
}
}
Instead of having a watch, create a new computed named clickToggle. Its get function returns toggle, its set function does what you're doing in your watch (as well as, ultimately, setting toggle). Your mounted can adjust toggle with impunity. Only changes to clickToggle will do the other stuff.

Categories