Vuetify - How to access data in form rule - javascript

Can I access a data element in a rule?
Here is my code running
I'm trying to flip the value of a data element on a text field rule in a Vuetify form.
The rule itself works fine, however I'm unable to access the data element, I'm getting this error:
TypeError: Cannot set property 'disabled' of undefined
Here is my code:
data: function() {
return {
disabled: false,
rules:{
sellerId(value){
if(value.length == 0){
this.disabled = true;
return "What are you trying to do here?";
}
else{
return true;
}
}
},
What am I doing wrong?

rules are an array of functions, and if you need the function to be able to access data property, you can define them as component methods:
data: function () {
return {
disabled: false
}
},
methods: {
sellerId (value) {
if (value.length === 0) {
this.disabled = true;
return "What are you trying to do here?";
} else {
return true;
}
}
}
And then in your Vuetify component:
<v-text-field :rules="[ sellerId ]"></v-text-field>

try to define rules as computed property :
data: function() {
return {
disabled: false,
...
}
},
computed: {
sellerIdRules() {
return [
(v) => {
if (value.length == 0) {
this.disabled = true;
return "What are you trying to do here?";
} else {
return true;
} ]
}
}
}

While this isn't available to a rule function you can accomplish this by assigning the vue instance to a variable, which will bring it into scope by closure.
vm = new Vue({
el: '#app',
data: () => ({
disabled: true,
rules: [
value => {
if (value.length == 0) {
vm.disabled = true;
return "What are you trying to do here?";
}
else {
return true;
}
}
],
'''

Related

Old prop keeps coming back when there's a change on Vue Currency Input

I'm using the Vue Currency Input to an input on the app that I'm working on, I'm not sure why I have this weird issue when the old prop comes back even thought no change was fired. The behaviour is: I edit the price and save, but when I click to be editable again the old price pops up.
This is my code:
<template>
<div>
<input ref="input" :value="val" v-currency="options" allow-negative="false" class="editableValue" #change="change" #keydown.enter.prevent>
</div>
</template>
<script>
import { parse } from "vue-currency-input";
import { UTILS } from "#/helpers/utils.js";
export default {
name: "EditableValue",
data(){
return {
val: this.value
}
},
props: {
currency: {
type: Object
},
value: {
type: Number
}
},
computed: {
options() {
return {
autoDecimalMode: true,
currency: {prefix: this.currency.currency + " "},
distractionFree: false
};
},
},
methods: {
change(){
console.log('chamou')
let newValue = parse(this.$refs.input.value, this.options);
this.val = newValue;
this.$emit('finishEditPrice', newValue)
},
},
watch: {
value(current) {
let inputValue = this.$refs.input.value;
let formattedValue = UTILS.getFormattedPrice(current, this.currency);
if (inputValue != formattedValue) {
this.val = formattedValue;
this.$refs.input.value = formattedValue;
}
},
},
updated(){
// if (inputValue != formattedValue) {
// this.val = formattedValue;
// this.$refs.input.value = formattedValue;
// }
}
};
</script>
The updated() and watch was a trial to change this behaviour but no luck. I'm using this version "vue-currency-input": "^1.22.6". Does anyone have any idea how to solve it? Thanks!

Vue metaInfo undefined in watch

I am inserting vue-meta logic inside the current code and there seems to be a problem that metaInfo is not available yet when watch is triggered.
export default {
metaInfo () {
return {
title: this.title,
meta: [],
}
},
watch: {
article() {
if (this.article) {
this.generatedHtml = this.applySnippets();
}
},
},
computed: {
article() {
return this.$store.getters.CONTENT;
},
title() {
if (this.article !== null) {
return this.article.info.caption;
}
return '';
},
}
created() {
this.$store.commit('CLEAR_CONTENT');
this.$store.dispatch('FETCH_CONTENT', { slug: this.slug, component: this });
},
methods: {
applySnippets() {
if (!this.article.snippets || this.article.snippets.length === 0) {
return this.article.data.content;
}
this.article.snippets.forEach(snippet => {
if (snippet.type === 'meta')
this.metaInfo.meta.push(snippet.object);
});
},
It fails that this.metaInfo is undefined during this Vue lifecycle phase. What to do?
To access the metaInfo result in the Options API, use this.$metaInfo (note the $ prefix):
if (snippet.type === 'meta')
this.$metaInfo.meta.push(snippet.object);
👆
demo

Vue, Vuex & remote storage

Well a have some value in remote storage (lets say x) and b-form-checkbox that should control this value. I want to inform user if value actually changed on storage and time when it happens.
So basically I want:
When user check/uncheck b-form-checkbox I want to change state of b-form-checkbox, send async request to the remote storage and show some b-spinner to indicate that state isn't actually changed yet.
When I receive answer from remote storage:
if change was successful just hide b-spinner.
if change was not successful (timeouted, error on server, etc) I want to change b-form-checkbox state back (since value actually doesn't changed on storage) and hide b-spinner
What is the silliest way to do int using Vue + Vuex?
Currently I'm doing it this way:
xChanger.vue:
<template>
<b-form-checkbox v-model="xComp" switch>
{{xComp ? 'On' : 'Off'}}
<b-spinner v-if="!xSynced"/>
</b-form-checkbox>
</template>
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
export default {
name: 'XChanger',
computed: {
...mapState(['x']),
...mapGetters(['xSynced']),
xComp: {
get() { return x.local },
set(value) {
if (value != this.x.local) {
this.setX(value)
}
},
},
},
methods: {
...mapActions(['setX']),
},
}
</script>
main.js
import Vuex from 'vuex'
import Axios from 'axios'
const store = new Vuex.Store({
state: {
x: {
remote: null,
local: null
},
getters: {
xSynced(state) {
state.x.local === state.x.remote
}
},
actions: {
async setX(store, value) {
store.state.x.local = value
try {
let response = await Axios.post('http://blah.blah/setX', {x: value});
if (response.status == 200) {
store.state.x.remote = value
}
} catch (error) {
store.state.x.local = !value
}
}
},
mutations: {
setX(state, value) {
state.x.local = value
state.x.remote = value
}
}
},
})
But it is too verbose for just one value to be controlled (especially computed property xComp). I'm sure that such a simple template should be already solved and has more simple way to implement.
Here is an example:
<template>
<b-form-checkbox v-model="x.local" switch>
{{x.local ? 'On' : 'Off'}}
<b-spinner v-if="saving"/>
</b-form-checkbox>
</template>
<script>
export default
{
name: 'XChanger',
data: () => ({
x:
{
local: false,
remote: false,
},
saving: false,
}),
watch:
{
'x.local'(newValue, oldValue)
{
if (newValue !== oldValue && newValue !== this.x.remote)
{
this.updateRemote(newValue);
}
}
}
methods:
{
async updateRemote(value)
{
try
{
this.saving = true;
const response = await Axios.post('http://blah.blah/setX', {x: value});
if (response.status == 200)
{
this.x.remote = value;
}
else
{
this.x.local = this.x.remote;
}
}
catch (error)
{
this.x.local = this.x.remote;
}
this.saving = false;
}
},
}
</script>

How to make it so when an object is called without calling any of its properties, it returns a default value

So I have an object called settings with a property called hex, which has its own properties:
var settings = {
hex: {
hex: "4fdaef",
validate: function(){
if(this.hex.length == 6){
return true
}
}
}
}
So currently to get the value of hex I would have to call settings.hex.hex, however ideally I would prefer to be able to call just settings.hex to get the value of the hex. How would I achieve this?
You'll have to rename hex to _hex, but this will work:
var settings = {
get hex() {
return this._hex.hex;
},
_hex: {
hex: "4fdaef",
validate: function () {
if (this.hex.length == 6) {
return true
}
}
}
}
console.log(settings.hex); // 4fdaef
With a Proxy you can allow for settings.hex.validate() to call _settings._hex._hex.validate(), but it's getting real ugly real quick, and we haven't even yet implemented the setter necessary for expected behavior of settings.hex = 'some other color'.
var _settings = {
_hex: {
_hex: new String('4fdaef'),
validate: function () {
if (this.length == 6) {
return true;
}
},
}
}
_settings._hex.hex = new Proxy(_settings._hex._hex, {
get(target, property) {
return property == 'validate' ? _settings._hex.validate : target[property];
}
});
const settings = new Proxy(_settings, {
get(target, property) {
return property == 'hex' ? target._hex.hex : target[property];
}
});
console.log(settings.hex); // [String: '4fdaef']
console.log(settings.hex.validate()); // true

Vue scope: how to delay handling of #mouseover

So I want to have an action only if the user has the mouse on the div for at least 1 second. Inside template:
<div #mouseover="trigger"></div>
Inside script:
data() {
return {
hovered: false
}
}
methods: {
trigger() {
setTimeout(function(){ this.hovered = true }, 1000)
}
}
My problem is that I don't understand the scope of Vue. Because this.hovered is inside another function, it does not find the actual hovered data variable. What's the solution to this?
Have you tried using an arrow function in your setTimeout? It will maintain this.
data() {
return {
hovered: false
}
}
methods: {
trigger() {
setTimeout(() => { this.hovered = true }, 1000)
}
}
<div #mouseover="activarOver" #mouseleave="resetOver "> eventos </div>
data: () => {
return {
countMouseOver: 0
}
},
methods: {
activarOver () {
this.countMouseOver++
if (this.countMouseOver < 2) {
this.setMostrarPopup()
}
console.log(this.countMouseOver)
},
resetOver () {
this.countMouseOver = 0
console.log('reset')
},
}
Implementation to show when hovered over for 1 second, then disappear when no longer hovered.
<span #mouseover="hover" #mouseleave="unhover">Hover over me!</span>
<div v-if="show">...</div>
data() {
return {
show: false;
hovering: false,
};
},
methods: {
hover() {
this.hovering = true;
setTimeout(() => this.show = this.hovering, 1000);
},
unhover() {
this.hovering = false;
this.show = false;
},
}
I have been solving this problem for selecting items in a list only if the user hovers for some time (to prevent menu flickering)
Template (pug):
.div(
#mouseover="select(identifier)"
#mouseout="clear()"
) {{content}}
Data:
hovered: false
selectedId: ""
and the methods
select(identifier) {
this.selectedId = identifier
setTimeout(() => {
if(this.selectedId === identifier )
this.hovered = true
},
1000
)
},
clear() {
this.selectedId = ''
}
the approach is to check if whatever user is hovering on is the same as they were hovering on a second ago.
If you want to use old syntax, bind 'this' to the setTimeout function
data() {
return {
hovered: false
}
}
methods: {
trigger() {
setTimeout(function(){ this.hovered = true }.bind(this), 1000)
}
}

Categories

Resources