VJs watching array element - vue.js

I have problem with vm.$watch method it can watch a single variable.
I can't make it to watch array elements.
in the example below i can't watch the change on the two select box bellow:
Vue.directive('select', {
twoWay: true,
bind: function() {
var optionsData
var optionsExpression = this.el.getAttribute('options')
if (optionsExpression) {
optionsData = this.vm.$eval(optionsExpression)
}
// initialize select2
var self = this
$(this.el)
.select2({
data: optionsData
})
.on('change', function() {
self.set(this.value)
})
},
update: function(value) {
$(this.el).val(value).trigger('change')
},
unbind: function() {
$(this.el).off().select2('destroy')
}
})
var vm = new Vue({
el: '#el',
data: {
selected: [{
country: 2
}, {
country: 1
}],
options: [{
id: 1,
text: 'USA'
}, {
id: 2,
text: 'UK'
}]
}
})
vm.$watch('selected', function(newvalue, oldval) {
alert('changed: ' + newvalue)
})
select {
min-width: 300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="http://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet" />
<script src="http://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/vue/0.12.9/vue.min.js"></script>
<div id="el">
<p>Selected: {{selected[0].country}}</p>
<select v-select="selected[0].country" options="options">
<option value="0">default</option>
</select>
<p>Selected: {{selected[1].country}}</p>
<select v-select="selected[1].country" options="options">
<option value="0">default</option>
</select>
</div>

You need to specify the deep option on your watch. The array isn't being modified, an object that it contains is being modified.
Vue.directive('select', {
twoWay: true,
bind: function() {
var optionsData
var optionsExpression = this.el.getAttribute('options')
if (optionsExpression) {
optionsData = this.vm.$eval(optionsExpression)
}
// initialize select2
var self = this
$(this.el)
.select2({
data: optionsData
})
.on('change', function() {
self.set(this.value)
})
},
update: function(value) {
$(this.el).val(value).trigger('change')
},
unbind: function() {
$(this.el).off().select2('destroy')
}
})
var vm = new Vue({
el: '#el',
data: {
selected: [{
country: 2
}, {
country: 1
}],
options: [{
id: 1,
text: 'USA'
}, {
id: 2,
text: 'UK'
}]
}
})
vm.$watch('selected', function(newvalue, oldval) {
alert('changed: ' + newvalue)
}, {
deep: true
})
select {
min-width: 300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="http://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet" />
<script src="http://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/vue/0.12.9/vue.min.js"></script>
<div id="el">
<p>Selected: {{selected[0].country}}</p>
<select v-select="selected[0].country" options="options">
<option value="0">default</option>
</select>
<p>Selected: {{selected[1].country}}</p>
<select v-select="selected[1].country" options="options">
<option value="0">default</option>
</select>
</div>

Related

How do you select a single array item from a data object and pass it to another component?

I have some data that I get from axios and pass to a Bootstrap table. In my computed properties where I declare the nameOfPerson field, I have made a click event, so that when a user clicks on the name, a modal opens. This modal also contains the data shown in the table.
However, I would like to change it so that when you click on the name of a person, ONLY the data for THAT single person gets passed to the modal. So instead of passing a prop containing data of ALL users the modal, I just want the data related to the name that I actually click on.
How would I accomplish this?
The parent:
<template>
<b-container>
<b-card class="mt-4">
<b-table
:items="dataItems"
:fields="fields"
:per-page="[5, 10]"
sort-desc
primary-key="id"
/>
</b-card>
<data-modal ref="dataModal" :selected-name="dataItems"/>
</b-container>
</template>
<script>
import {axiosComponent} from '#/axios/services';
import DataModal from '#/components/DataModal';
export default {
components: {
DataModal
},
data() {
return {
dataItems: null,
};
},
computed: {
fields() {
return [
{
key: 'nameOfperson',
label: 'name',
sortable: true
click: () => this.$refs.dataModal.show(),
},
{
key: 'ageOfPerson',
label: 'Age',
sortable: true
},
]
},
},
methods: {
load(){
axiosComponent.getData().then(result => {
this.dataItems = result.data
})
}
},
created() {
this.load()
}
};
</script>
The child (modal)
<template>
<b-modal v-model="showModal">
<div v-for="log in selectedName">
{{ log }}
</div>
</b-modal>
</template>
<script>
export default {
props: {
selectedName: Array
},
data() {
return {
showModal: false,
};
},
methods: {
show(){
this.showModal = true
}
}
};
</script>
You can use #row-selected method, take a look at following demo:
Vue.component('child', {
template: `
<b-modal v-model="showModal">
<div v-for="log in selectedName">
{{ log }}
</div>
</b-modal>
`,
props: {
selectedName: Array,
},
data() {
return {
showModal: false,
};
},
methods: {
show(){
this.showModal = true
}
}
})
new Vue({
el: "#demo",
data() {
return {
dataItems: null,
selected: null,
};
},
computed: {
fields() {
return [
{
key: 'nameOfperson',
label: 'name',
sortable: true,
},
{
key: 'ageOfPerson',
label: 'Age',
sortable: true
},
]
},
},
methods: {
load(){
// axiosComponent.getData().then(result => {
this.dataItems = [{id: 1, nameOfperson: 'aaa', ageOfPerson: 5}, {id: 2, nameOfperson: 'bbb', ageOfPerson: 25}, {id: 3, nameOfperson: 'ccc', ageOfPerson: 35}, {id: 4, nameOfperson: 'ddd', ageOfPerson: 45}]
// })
},
onRowSelected(items) {
this.selected = items
this.$refs.dataModal.show()
},
},
created() {
this.load()
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.css" />
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue-icons.min.js"></script>
<div id="demo">
<b-container>
<b-card class="mt-4">
<b-table
:items="dataItems"
:fields="fields"
:per-page="5"
sort-desc
primary-key="id"
selectable
:select-mode="'single'"
#row-selected="onRowSelected"
/>
</b-card>
<child ref="dataModal" :selected-name="selected"></child>
</b-container>
</div>

Get object keyname vuejs

I have a data with object, when im using v-for:
data: {
comp: {
title: {
...
},
title2: {
...
},
title3: {
...
},
}
..
div(v-for="item in comp" #click="method(item)")
How i can get name of item (title, title2, title3) in method?
Try this:
new Vue({
el:'#app',
data: {
comp: {
title: {},
title2: {},
title3: {},
}
},
methods: {
itemClicked(item) {
console.log(item);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(val,key) in comp" #click="itemClicked(key)">
{{ key }} - {{ val }}
</div>
</div>

Unable to show updated v-model value in input

I have the following code:
const BasicInput = {
template: '<input v-model="content" #input="handleInput" />',
prop: ["value"],
data() {
return {
content: this.value
};
},
methods: {
handleInput(e) {
this.$emit("input", this.content);
}
}
};
new Vue({
el: "#app",
data: { name: "" },
mounted() {
self = this;
setTimeout(() => {
self.name = "test";
}, 2000);
},
components: { BasicInput }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<basic-input v-model="name"></basic-input>
<p>
<strong>Name:</strong> {{ name }}
</p>
</div>
How do I get the updated v-model value to also show in the input tag ?
You need to v-bind the property for the BasicInput component. The short form of it is :.
See <basic-input :value="name"></basic-input> usage below, only changed it.
const BasicInput = {
template: '<input v-model="content" #input="handleInput" />',
prop: ["value"],
data() {
return {
content: this.value
};
},
methods: {
handleInput(e) {
this.$emit("input", this.content);
}
}
};
new Vue({
el: "#app",
data: { name: "" },
mounted() {
self = this;
setTimeout(() => {
self.name = "test";
}, 2000);
},
components: { BasicInput }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<basic-input :value="name"></basic-input>
<p>
<strong>Name:</strong> {{ name }}
</p>
</div>

Vue does not delete proper component from list

I got :key="index" but it still does not do it as I need.
If you add for example 6 items and select something from select on the third item and hit remove button on that item, it will delete another item, but not this one.
Here is a link to fiddle
https://jsfiddle.net/benderlio/ecyskz83/1/
Vue.config.devtools = true
Vue.component('select-component', {
data: function () {
return {
currentSelectedData: undefined,
}
},
template: `<div>
<select v-model="currentSelectedData">
<option v-for="(item,key) in dataArray" :key="key" :value="key">{{item}}</option>
</select>
<select v-if="currentSelectedData >= 0">
<option v-for="(item,key) in tempData" :key="key" :value="key">{{item}}</option>
</select>
</div>`,
watch: {
currentSelectedData(newValue, oldValue) {
console.log('', newValue, oldValue);
this.$emit("got-selected-value", newValue)
}
},
props: {
'data-array': {
type: Array,
required: true,
},
'temp-data': {
type: Array,
required: true,
},
},
})
new Vue({
el: '#app',
data: {
components: [],
dataFirstSelect: ["sheet3", "sheet4", "sheet5"
],
dataSecondSelect: [1, 2, 3]
},
methods: {
emitedValue(payload) {
console.log('Got the value from child: ', payload);
},
addComp() {
const comp = {
id: new Date().getTime()
};
this.components.push(comp)
},
remove(id) {
// this.components = this.components.filter(i => i.id !== id)
console.log('Remove', id, this.components);
//this.$delete(this.components, id)
this.components.splice(id, 1)
},
log() {
console.log('---------', this.components.map(i => i.id));
}
},
})
It works fine if you use item.id as your key instead of index and put your key on a real DOM element, not a template. Using index as a key is an anti-pattern.
Vue.config.devtools = true
Vue.component('select-component', {
data: function() {
return {
currentSelectedData: undefined,
selectedTemp: undefined
}
},
template: `<div>
<select v-model="currentSelectedData">
<option v-for="(item, key) in dataArray" :value="key">{{item}}</option>
</select>
<select v-if="currentSelectedData >= 0" v-model="selectedTemp">
<option v-for="(item, key) in tempData" :value="key">{{item}}</option>
</select>
<div>{{currentSelectedData}}</div>
<div>{{selectedTemp}}</div>
</div>`,
watch: {
currentSelectedData(newValue, oldValue) {
console.log('', newValue, oldValue);
this.$emit("got-selected-value", newValue)
}
},
props: {
'data-array': {
type: Array,
required: true,
},
'temp-data': {
type: Array,
required: true,
},
},
})
new Vue({
el: '#app',
data: {
components: [],
dataFirstSelect: ["sheet3", "sheet4", "sheet5"],
dataSecondSelect: [1, 2, 3]
},
methods: {
emitedValue(payload) {
console.log('Got the value from child: ', payload);
},
addComp() {
const comp = {
id: new Date().getTime()
};
this.components.push(comp)
},
remove(id) {
// this.components = this.components.filter(i => i.id !== id)
console.log('Remove', id, this.components);
//this.$delete(this.components, id)
this.components.splice(id, 1)
},
log() {
console.log('---------', this.components.map(i => i.id));
}
},
})
.select-component {
display: block;
margin: 10px;
}
.item {
padding: 10px;
margin: 10px;
border: 1px solid gray;
}
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<button #click="addComp">Add</button>
<template v-for="(item,index) in components">
<div class="item" :key="item.id">
id: {{item.id}} {{index}}
<button #click="remove(index)">Remove</button>
<select-component class="select-component" v-on:got-selected-value="emitedValue" :data-array='dataFirstSelect'
:temp-data='dataSecondSelect'>
</select-component>
</div>
</template>
</div>

apply jquery plugin on vue json data

I'm using a vue component to fetch and show data as below:
export default {
data() {
return {
posts: [],
post: {
id: '',
title: '',
rating: '',
ver: ''
},
pagination: {},
}
},
created() {
this.fetchRecentPosts();
},
methods: {
fetchRecentPosts() {
fetch('api/recentposts')
.then(res => res.json())
.then(res => {
this.posts = res.data;
})
}
},
}
I'm also using rateYo plugin to show posts rating.
without using vue.js i'm able to turn my div attribute into star rating by :
$(".rateYo").each(function () {
var rating = $(this).data("rating");
$(this).rateYo({
rating: rating,
starWidth: "13px",
spacing: "3px",
ratedFill: "#ffb300",
normalFill: "#d3d8e4",
readOnly: true
});
});
So, How should I apply it work on evey div inside a v-for loop using vue.js ?
Thanks
var app = new Vue({
el: '#app',
data() {
return {
posts: [
{
id: 1,
title: 'Post One',
rating: 2.5
},
{
id: 2,
title: 'Post Two',
rating: 4.1
}
],
post: {
id: '',
title: '',
rating: '',
ver: ''
},
pagination: {},
}
},
mounted() {
$('.rateYo').each(function() {
$(this).rateYo({
rating: $(this).data('rating'),
starWidth: "13px",
spacing: "3px",
ratedFill: "#ffb300",
normalFill: "#d3d8e4",
readOnly: true
});
});
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rateYo/2.3.2/jquery.rateyo.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/rateYo/2.3.2/jquery.rateyo.min.css">
<div id="app">
<div v-for="(post,index) in posts" :key="index">
<h3>{{ post.title }}</h3>
<div class="rateYo" :data-rating="post.rating"></div>
</div>
</div>