Vue onEnable/onDisable event - vue.js

In my vue application I need to observe an element getting enabled/disabled (It binds to a function) and by looking that I need to trigger an onEnabled/onDisabled event which will clean up some other data nodes.
So is there a listener like #click, #enabled or something?
Eg:
<v-checkbox :value="getValue(layout.responseNode)" #change="setValue(layout.responseNode, $event)" :label="expression(layout.label)" :disabled="expression(layout.enableIf)" ></v-checkbox>
This is the code so far with me. here enableIf will be a dynamic expression from server.
Its properly working now.
Now I need to run some more expression like
<v-checkbox :value="getValue(layout.responseNode)" #change="setValue(layout.responseNode, $event)" :label="expression(layout.label)" :disabled="expression(layout.enableIf)" #onDisabled="expression(layout.disableCommand)" ></v-checkbox>
Is there an event matching onDisabled?

i would recommend watchers you can bind a variable/computed to :disabled of the checkbox and watch the value changing
exp.
<template>
<div>
<p>{{ checkboxState }}</p>
<input type="checkbox" :disabled="checkboxState" />
<button #click="checkboxChanged()">Disable Checkbox!</button>
</div>
</template>
<script>
export default {
name: "App",
data: () => {
return {
checkboxState: true,
};
},
methods: {
checkboxChanged() {
this.checkboxState = !this.checkboxState;
},
},
watch: {
checkboxState() {
// this is fired when the checkboxState changes
console.log("fired when checkboxState changes");
},
},
};
</script>
note: the function name and the variable must have the same name for watchers to work.
Like this Sandbox

Related

How do you use buefy's b-taginput in a custom component so that it works like v-model, It is only working one way binding?

I'm new to vue and decided to try out buefy for some useful components.
To try and keep my code organized I'm trying to make a custom component using the b-taginput.
I have it so that the component loads with the tags in someArrayofTags, but when I'm typing into the b-taginput, it does not add new tags into someArrayofTags. Hence I lose the two-way binding / updating. I would like to know where I am going wrong and how I could adjust this.
I'm not too well versed to understand how they have implemented it, but i do see that it is composed of autocomplete and b-tags https://github.com/buefy/buefy/blob/dev/src/components/taginput/Taginput.vue
I'm trying to use the custom component as such
<mytaglist v-model="someArrayofTags"></mytaglist>
I know v-model is just v-bind on value and #input events. My code is below.
<template>
<b-field label="tag inputs">
<b-taginput
:value="value"
#input=someMethod($event.target.value)
size="is-small"
ref="ti"
>
<template slot="selected" slot-scope="value">
<b-tag
v-for="(tag, index) in value.tags"
:key="index"
:type="getType(tag)"
rounded
:tabstop="false"
ellipsis
closable
#close="$refs.ti.removeTag(index, $event)"
>
{{ tag }}
</b-tag>
</template>
</b-taginput></b-field
>
</template>
<script>
export default {
props: ['value'],
data() {
return {
normal: ["wnl","clear"]
};
},
methods: {
someMethod(tags) {
alert(tags)
this.$emit("input", tags)
},
getType(tag) {
if (this.normal.includes(tag)) {
return "is-danger";
} else {
return "is-success";
}
},
},
};
</script>
Thanks
After going through the source for buefy, I found that I could watch and update the values based on a v-model within the new component.
The code below works for me, but if anyone could provide a better solution I will leave it open.
<template>
<b-field label="tag inputs">
<b-taginput
v-model="newValue"
size="is-large"
ref="ti"
>
<template slot="selected" slot-scope="value">
<b-tag
v-for="(tag, index) in value.tags"
:key="index"
:type="getType(tag)"
rounded
:tabstop="false"
ellipsis
closable
#close="$refs.ti.removeTag(index, $event)"
>
{{ tag }}
</b-tag>
</template>
</b-taginput></b-field
>
</template>
<script>
export default {
props: ['value'],
data() {
return {
normal: ["wnl","clear","deep & quiet"],
newValue: this.value
};
},
methods: {
getType(tag) {
if (this.normal.includes(tag)) {
return "is-danger";
} else {
return "is-success";
}
},
},
watch: {
newValue(value) {
this.$emit('input', value)
},
value(value) {
this.newValue = value
}
}
};
</script>
<style>
</style>

Why v-model is not updating in text

<template>
<div class="card-deck mb-3 text-center">
<div class="card mb-3 box-shadow">
<div class="card-header">
Numbers Checked
</div>
<div class="card-body card-info color-accent" v-model="numbers_checked" v-text="numbers_checked">
</div>
</div>
</div>
</template>
<script>
export default {
props:
[
'overviewAnalytics',
],
data() {
return {
numbers_checked: this.overviewAnalytics.numbers_checked
};
},
created() {
this.channelTemperatureReading.listen('TemperatureReadingUpdate', reading => {
axios.get('/home/get-overview-analytics').then(resp => {
this.numbers_checked = 12; //resp.data.numbers_checked + 100;
});
});
},
computed: {
channelTemperatureReading() {
return window.Echo.channel('temperature-reading');
},
},
};
</script>
I've tried everything but text is not updating. Confirmed from every aspect that data does change.
Changes from AXIOS are coming just ok. I even tried to put custom value but no avail.
I don't what is issue here.
v-model only works on input, textarea, and select elements
You appear to be misusing computed properties which rely on reactive dependencies to execute however yours is wrapping window.Echo.channel('temperature-reading') which Vue knows nothing about.
I suggest you remove the computed property and use something like this
created() {
const channel = window.Echo.channel('temperature-reading')
channel.listen('TemperatureReadingUpdate', reading => {
axios.get('/home/get-overview-analytics').then(({ data }) => {
// console.log('get-overview-analytics', data.numbers_checked)
this.numbers_checked = data.numbers_checked + 100
})
})
}
As others have mentioned, v-model is not appropriate here so you should also remove that.
Don't use v-model with div, it's for inputs.
<div v-text="numbers_checked"></div>
From the documentation on v-model:
Usage: Create a two-way binding on a form input element or a
component.

How to avoid Infinite Loop in vue.js watcher and socket.io

I have bulma slider on client side with vue.js and socket.io. In my case I would have few instances of clients - if somebody moves his slider socket.io start sending message to server (json file) server saves this status and broadcasting this json to others. Problem is when others recives messages - they started sending this json too, because data value on their instances changes too.
Thanks for help
<template>
<div>
<input class="slider is-fullwidth is-large is-danger" step="3" min="0" max="255" v-model="value" type="range" v-bind:disabled="disabled">
<p>{{value}}</p>
</div>
</template>
<script>
export default {
name: 'app',
data: function () {
return {
value: 0,
disabled: false
}
},
methods: {
sendChange: function () {
this.$socket.send({io: "io1", mode: "brightness", user: "tomek", param: this.value})
},
},
watch: {
value: function (val) {
this.sendChange()
}
},
sockets: {
dataFromServer: function (data) {
this.value = data.param
},
},
}
</script>
Bind action to an event, like #input or #change insted of v-model value, get rid of watcher and try something like this:
<input class="..."
step="3"
min="0"
max="255"
v-model="value"
type="range"
:disabled="disabled"
#input="sendChange()">
This way you send request only when user intentionally changes his value, not when value updates by any mean.

how to pass value in v-select in vue

i m new to vue (version 2.5) in my project i had install v-select i go through document and got confuse on few thing
here is my code
<template>
<div class="wrapper">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="name">Category</label>
<v-select label="category_desc" options="category"></v-select>
</div>
</div>
</div>
<button type="button" variant="primary" class="px-4" #click.prevent="next()">next</button>
</div>
</template>
<script>
export default {
name: 'Addproduct',
data () {
return {
category:[]
}
},
mounted() {
this.$http.get('http://localhost:3000/api/categories') .then(function (response) {
console.log(response.data);
this.category=response.data
})
.catch(function (error) {
console.log("error.response");
});
},
method:{
next(){
// console.log('value of v-selection not option' ) eg id 1 has 'country' so i want id 1 in method next() i.e in console.log
}
}
now my problem is that how i can pass this axios responded success value to v-select option and second this how i can get selected value of v-select for eg;- when user click on next button how i can get which value is selected in v-select
You need to bind options to category. i.e. v-bind:options or short hand :options I would also suggest you use a method to make your ajax call, and then call the method in mounted() instead. Also you probably want a v-model on that select so I added that in the example.
First in your template...
<v-select v-model="selectedCategory" label="category_desc" :options="category"></v-select>
Then in your script definition...
data(){
return{
category:[],
selectedCategory: null,
}
},
methods:{
getCategories(){
this.$http.get('http://localhost:3000/api/categories')
.then(function (response) {
this.category=response.data
}.bind(this))
.catch(function (error) {
console.log("error.response");
});
}
},
mounted(){
this.getCategories();
}
Here is a working jfiddle https://jsfiddle.net/skribe/mghbLyva/3/
Are we talking about this select component? The simplest usage is to put a v-model attribute on your v-select. Then this variable will automatically reflect the user selected value.
If you need to specify options, which you might, then you also need to provide a value prop and listen for #change.

Conditional <router-link> in Vue.js dependant on prop value?

Hopefully this is a rather simple question / answer, but I can't find much info in the docs.
Is there a way to enable or disable the anchor generated by <router-link> dependent on whether a prop is passed in or not?
<router-link class="Card__link" :to="{ name: 'Property', params: { id: id }}">
<h1 class="Card__title">{{ title }}</h1>
<p class="Card__description">{{ description }}</p>
</router-link>
If there's no id passed to this component, I'd like to disable any link being generated.
Is there a way to do this without doubling up the content into a v-if?
Thanks!
Assuming you want to disable anchor tag as in not clickable and look disabled the option is using CSS. isActive should return true by checking prop id.
<router-link class="Card__link" v-bind:class="{ disabled: isActive }" :to="{ name: 'Property', params: { id: id }}">
<h1 class="Card__title">{{ title }}</h1>
<p class="Card__description">{{ description }}</p>
</router-link>
<style>
.disabled {
pointer-events:none;
opacity:0.6;
}
<style>
If you want to just disable the navigation , you can use a route guard.
beforeEnter: (to, from, next) => {
next(false);
}
If you need to use it often, consider this:
Create new component
<template>
<router-link
v-if="!disabled"
v-bind="$attrs"
>
<slot/>
</router-link>
<span
v-else
v-bind="$attrs"
>
<slot/>
</span>
</template>
<script>
export default {
name: 'optional-router-link',
props: {
params: Object,
disabled: {
type: Boolean,
default: false,
},
},
};
</script>
Optional, register globally:
Vue.component('optional-router-link', OptionalRouterLink);
Use it as follows:
<optional-router-link
:disabled="isDisabled"
:to="whatever"
>
My link
</optional-router-link>
The problem is that router-link renders as an html anchor tag, and anchor tags do not support the disabled attribute. However you can add tag="button" to router-link:
<router-link :to="myLink" tag="button" :disabled="isDisabled" >
Vue will then render your link as a button, which naturally supports the disabled attribute. Problem solved! The downside is that you have to provide additional styling to make it look like a link. However this is the best way to achieve this functionality and does not rely on any pointer-events hack.
I sometimes do stuff like this:
<component
:is="hasSubLinks ? 'button' : 'router-link'"
:to="hasSubLinks ? undefined : href"
:some-prop="computedValue"
#click="hasSubLinks ? handleClick() : navigate"
>
<!-- arbitrary markup -->
</component>
...
computed: {
computedValue() {
if (this.hasSubLinks) return 'something';
if (this.day === 'Friday') return 'tgif';
return 'its-fine';
},
},
But I basically always wrap router-link, so you can gain control over disabled state, or pre-examine any state or props before rendering the link, with something like this:
<template>
<router-link
v-slot="{ href, route, navigate, isActive, isExactActive }"
:to="to"
>
<a
:class="['nav-link-white', {
'nav-link-white-active': isActive,
'opacity-50': isDisabled,
}]"
:href="isDisabled ? undefined : href"
#click="handler => handleClick(handler, navigate)"
>
<slot></slot>
</a>
</router-link>
</template>
<script>
export default {
name: 'top-nav-link',
props: {
isDisabled: {
type: Boolean,
required: false,
default: () => false,
},
to: {
type: Object,
required: true,
},
},
data() {
return {};
},
computed: {},
methods: {
handleClick(handler, navigate) {
if (this.isDisabled) return undefined;
return navigate(handler);
},
},
};
</script>
In my app right now, I'm noticing that some combinations of #click="handler => handleClick(handler, navigate)" suffer significantly in performance.
For example this changes routes very slow:
#click="isDisabled ? undefined : handler => navigate(handler)"
But the pattern in my full example code above works and has no performance issue.
In general, ternary operator in #click can be very dicey, so if you get issues, don't give up right away, try many different ways to bifurcate on predicates or switch over <component :is="" based on state. navigate itself is an ornery one because it requires the implicit first parameter to work.
I haven't tried, but you should be able to use something like Function.prototype.call(), Function.prototype.apply(), or Function.prototype.bind().
For example, you might be able to do:
#click="handler => setupNavigationTarget(handler, navigate)"
...
setupNavigationTarget(handler, cb) {
if (this.isDisabled) return undefined;
return this.$root.$emit('some-event', cb.bind(this, handler));
},
...
// another component
mounted() {
this.$root.$on('some-event', (navigate) => {
if (['Saturday', 'Sunday'].includes(currentDayOfTheWeek)) {
// halt the navigation event
return undefined;
}
// otherwise continue (and notice how downstream logic
// no longer has to care about the bound handler object)
return navigate();
});
},
You could also use the following:
<router-link class="Card__link" :to="id ? { name: 'Property', params: { id: id }} : {}">
<h1 class="Card__title">{{ title }}</h1>
<p class="Card__description">{{ description }}</p>
</router-link>
If id is undefined the router won't redirect the page to the link.
I've tried different solutions but only one worked for me, maybe because I'm running if from Nuxt? Although theoretically nuxt-link should work exactly the same as router-link.
Anyway, here is the solution:
<template>
<router-link
v-slot="{ navigate }"
custom
:to="to"
>
<button
role="link"
#click="onNavigation(navigate, $event)"
>
<slot></slot>
</button>
</router-link>
</template>
<script>
export default {
name: 'componentName',
props: {
to: {
type: String,
required: true,
},
},
methods: {
onNavigation(navigate, event) {
if (this.to === '#other-action') {
// do something
} else {
navigate(event);
}
return false;
},
};
</script>