Capture events of underlying element in component - javascript

Trying to use this component.
<select2 v-model="value" :options="options" #change="onChange()"></select2>
The #change callback is not getting called. I know that I can use watch: { value: function () { ... } but, is there a way to capture underlying tag events?

In the current version, select2 component does not handle on-change function. For this, you have to modify the select2 component, you have to add one more prop: onChange and inside component execute the function passed in this prop, changes will be something like following:
Vue.component('select2', {
props: ['options', 'value', 'onChange'], //Added one more prop
template: '#select2-template',
mounted: function () {
var vm = this
$(this.$el)
.val(this.value)
// init select2
.select2({ data: this.options })
// emit event on change.
.on('change', function () {
vm.$emit('input', this.value)
//New addition to handle onChange function
if (this.onChange !== undefined) {
this.onChange(this.value)
}
})
},
watch: {
value: function (value) {
// update value
$(this.$el).select2('val', value)
},
options: function (options) {
// update options
$(this.$el).select2({ data: options })
}
},
destroyed: function () {
$(this.$el).off().select2('destroy')
}
})
Now, you can pass a function which will be executed onChange like following:
<select2 v-model="value" :options="options" :on-change="onChange()"></select2>

Related

Vue Watchers for multiple button clicks

I have multiple filters that are set to false and if clicked turn to true and must implement the specific filter. How can I use a watchers to look for the changing of my filter from false to true. Currently as I have all my functions in one watcher if one filter is clicked all filters are implemented. Whereas I need it to be only the specific filter that is related to the filter button.
<sankey-filter
label="string1"
v-model="filters.string1"
/>
<sankey-filter
label="string2"
v-model="filters.string2"
/>
<sankey-filter
label="string3"
v-model="filters.string3"
/>
data() {
return {
filters: {
statusString1: false,
statusString2: false,
statusString3: false,
}
watch: {
filters: {
handler: function(){
this.filterString1(),
this.filterString2(),
this.filterString2(),
},
deep: true
Vue $watch provide callback function two parameters (newValue, oldValue) so U can check new value each filter if true for each filter action
watch: {
filters: {
handler: function({statusString1, statusString2, statusString3}){
if (statusString1)
this.filterString1()
if (statusString2)
this.filterString2()
if (statusString3)
this.filterString3()
},
deep: true
}
}
Use a dot-delimited path as the watcher key.
watch: {
'filters.string1' () {
this.filterString1(),
},
'filters.string2' () {
this.filterString2(),
},
'filters.string3' () {
this.filterString3(),
},
}

Vue 3 Passing Additional Data as a Parameter with Object Syntax Dynamic Events

Is there a way to pass additional data to event handlers when using the object structure syntax?
Example:
MySFC.vue:
<template>
<template v-for="action in actions" : key="action.id">
<button v-on="action.eventHandlers"> <!--Looking for some way to pass both event and additional data here-->
{{ action.name }}
</button>
</template>
</template>
<script>
import { actions } from '#/lib/my-actions.js';
export default {
setup(props, ctx){
return{
actions,
}
},
}
</script>
my-actions.js:
export const actions = {
name: 'my action',
eventHandlers: {
click: (event, myData) => {
console.log(myData);
},
mousedown: (event, myData) => {
console.log(myData);
}
}
}
Normally if you have a specific event you are trying to handle you can just do something like:
<button #click="myClickHandler($event, myData)"> </button>
But in this case I am trying to build a wrapper of sorts for plugins and do not know what events may need to be handled, so I can't pre-define with v-on which events to look for.
I saw some options for attaching dynamic events, but I only see examples where the event itself is passed, not with additional parameters.
You could use Function.prototype.bind() to bind the function's initial arguments. This can only be done with regular functions (not arrow functions).
// #/lib/my-actions.js
const myClickData = {
id: 'my click data',
}
const myMouseDownData = {
id: 'my mousedown data',
}
export const actions = [
{
name: 'my action',
eventHandlers: {
click: function (myData, event) {
console.log('click', { event, myData })
}.bind(undefined, myClickData),
mousedown: function (myData, event) {
console.log('mousedown', { event, myData })
}.bind(undefined, myMouseDownData),
},
},
]
demo

How to pass a function to component to handle a drag event

I want to use v-for and is to render the components I need. So I create the component Cube.vue like this:
<script>
export default {
name: 'cube',
render: function(createElement) {
return createElement("div",{
props: {
style: {
type: Object
},
dragstartHandler: {
type: Function
},
classNames: {
type: Array|String|Object
}
},
style: this.style,
attrs: {
draggable: "true",
},
on: {
dragstart: this.dragstartHandler
},
'class': this.classNames
},[
createElement("div",{
class: ["resizer", "top-left"]
}),
createElement("div",{
class: ["resizer", "top-right"]
}),
createElement("div",{
class: ["resizer", "bottom-left"]
}),
createElement("div",{
class: ["resizer", "bottom-right"]
}),
])
}
}
</script>
And then, I use it like this
<component
v-for="component in components"
:class="component.classes"
:key="component.refValue"
:is="component.type"
:style="component.style"
:dragstartHandler="component.dragstartHandler"
:ref="component.refValue"
>
</component>
Everything as I expected, except the dragstartHandler. It throws an error
[Vue warn]: Invalid handler for event "dragstart": got undefined
I try to console.log() the components. The result is :
[{…}, __ob__: Observer]
0:
classes: Array(2)
dragstartHandler: ƒ ()
refValue: "cube-0"
style: Object
type: "cube"
It really is a function. But I don't know why it go to undefined in the render. I have checked I didn't spell wrong.
I just want pass the function to the component to handle the drag event. So how does it happened and what should I do to resolve it.
The dragHandler function is these:
dragstartCopyHandler(event) {
event.dataTransfer.setData("elementId", event.target.id);
event.dataTransfer.setData("componentOffsetX", event.offsetX);
event.dataTransfer.setData("componnetOffsetY", event.offsetY);
event.dataTransfer.setData("dropEffect", "copy");
event.dataTransfer.dropEffect = "copy";
},
dragstartMoveHandler(event) {
console.log("move start")
event.dataTransfer.setData("elementId", event.target.id);
event.dataTransfer.setData("componentOffsetX", event.offsetX);
event.dataTransfer.setData("componnetOffsetY", event.offsetY);
event.dataTransfer.setData("dropEffect", "move");
event.dataTransfer.dropEffect = "move";
},
And I pass the dragstartMoveHandler to the component.
this.components.push({
refValue: `${elementId}-${this.count}`,
type: elementId,
style: style,
dragstartHandler: this.dragstartMoveHandler,
classes: ["component", elementId]
});
I write the pages use js to control Dom before. And today I want to rewrite it with vue. So here might something wrong with the function, but the problem now is the function passed is undefined.
The cube component needs to define a prop for dragstartHandler so that it receives it from the parent for passing it on to the div. The same is true of the style and class bindings, which are also not working as you expect, but you don't see an error there because those are built-in bindings which get transferred to the root element.
Cube.vue
render() {
...
},
props: {
dragstartHandler: {
type: Function,
},
}

vue.js multi select change options inside axios GET

Hi I'm using this modified wrapper to handle a multiple select for vue.js. I'm trying to change value of this inside vue component. Here is my code.
<select2-multiple :options="car_options" v-model="input.classification">
<option disabled value="0">Select one</option>
</select2-multiple>
And this is my script,
Vue.component('select2Multiple', {
props: ['options', 'value'],
template: '#select2-template',
mounted: function () {
var vm = this
$(this.$el)
// init select2
.select2({ data: this.options })
.val(this.value)
.trigger('change')
// emit event on change.
.on('change', function () {
vm.$emit('input', $(this).val())
})
},
watch: {
value: function (value) {
alert(value);
if ([...value].sort().join(",") !== [...$(this.$el).val()].sort().join(","))
$(this.$el).val(value).trigger('change');
},
options: function (options) {
// update options
$(this.$el).select2({ data: options })
}
},
destroyed: function () {
$(this.$el).off().select2('destroy')
}
});
var vm = new Vue({
el: '#el',
delimiters: ["[[", "]]"],
data: {
input: {
classification: []
},
},
created: function () {
var vm = this;
axios.get('http://jsonplaceholder.typicode.com/todos')
.then(function (response) {
$.each(response.data, function (i, item) {
response.data[i].id = String(response.data[i].id);
response.data[i].text = String(response.data[i].title);
vm.car_options.push(response.data[i]);
});
vm.input.classification = ["2"];
})
.catch(function (error) {
console.log(error);
});
}
});
I need to get vm.input.classification = ["2"] selected by default. And it's not working and no error message displays. I'm no expert in vue components but I feel like issue relies on vue component.
Here is a js fiddle for my example,
Finally I found the answer. We need to swapp the positions of options and value of component watch.
watch: {
options: function (options) {
// update options
$(this.$el).select2({ data: options })
},
value: function (value) {
if ([...value].sort().join(",") !== [...$(this.$el).val()].sort().join(","))
$(this.$el).val(value).trigger('change');
}
},

vuejs - remount after update background url

I have a vue component with the binding style:
<div id="profile-icon" :style="{'background': 'url('+profile.icon+')'}"></div>
<input name="name" v-model="profile.name" type="text" placeholder="Name">
The image will only display if I update the profile.icon under beforeMount function:
props: ['profileprops'],
data: function() {
return {
profile: {
address:'',
name:'Name',
description:'Short description',
address:'address',
user_id:'',
background:'/images/admin/bg.png',
icon:''
},
}
},
beforeMount: function() {
console.log('profileedit mounted.')
var self = this;
if(self.profileprops){
self.profile = self.profileprops;
};
if(!self.profile.icon){
self.profile.icon = '/images/admin/icon-144.png';
},
mounted: function() {
var self = this;
self.$eventHub.$on('ImageSelected', (imagelink) => {///<-- I have another vue component for selecting images to emit event and to catch it here.
self.profile.icon = imagelink;
$('#profilemodal').modal('hide');
});
},
watch: {
'profile': {
handler: function(newVal, oldVal) {
console.log("changed")
},
deep: true
},
}
I did a few tests:
if I select the image, I can see the console.log within the $on has given the correct data from profile.icon. However, the :style did not update the background image with the new profile.icon. And watch.profile did not react at all.
If I start to type on input with v-model bind with profile.name, everything update immediately, including the background image in :style.
I believe I must have miss something to sync the inside and outside of the $on. How can I resolve this?
I found the solution here.
So I just $forceUpdate() inside $on:
self.$eventHub.$on('ImageSelected', (imagelink) => {
self.profile.icon = imagelink;
self.$forceUpdate();//<-- this does the magic.
});
then the data will update and re-render.

Categories

Resources