I want to add checkboxes to Vuejs Treeview. So I added a checkbox to the template as this code:
<script type="text/x-template" id="item-template">
<li>
<input type="checkbox" id="checkbox">
<span
:class="{bold: isFolder}"
#click="toggle"
#dblclick="changeType">
{{ model.name }}
<span v-if="isFolder">[{{ open ? '-' : '+' }}]</span>
</span>
<ul v-show="open" v-if="isFolder">
<item
class="item"
v-for="(model, index) in model.children"
:key="index"
:model="model">
</item>
</ul>
</li>
</script>
But I don't know how to link those checkboxes to a model like this example. Please help! Thank you very much!
I think the simplest way to do that is by using VueX.
The store has a common variable for all checkBoxes accessible from everywhere.
When you click on a checkbox, it update the store.
https://jsfiddle.net/u91mxc58/14/
const store = new Vuex.Store({
state: {
checkBoxes:[],
},
mutations: {
updateCheckBoxes(state, value) {
state.checkBoxes = value;
}
}
})
// demo data
var data = {
name: 'My Tree',
children: [
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
},
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
}
]
}
]
}
// define the item component
Vue.component('item', {
template: '#item-template',
props: {
model: Object,
},
data: function () {
return {
open: false
}
},
computed: {
isFolder: function () {
return this.model.children &&
this.model.children.length
},
checkBoxes: {
// accesseur
get: function () {
return store.state.checkBoxes;
},
// mutateur
set: function (newValue) {
store.commit('updateCheckBoxes', newValue);
}
}
},
methods: {
toggle: function () {
if (this.isFolder) {
this.open = !this.open
}
},
changeType: function () {
if (!this.isFolder) {
Vue.set(this.model, 'children', [])
this.addChild()
this.open = true
}
},
addChild: function () {
this.model.children.push({
name: 'new stuff'
})
}
}
})
// boot up the demo
var demo = new Vue({
el: '#demo',
data: function() {
return {
treeData: data
};
},
computed: {
checkBoxes() {
return store.state.checkBoxes;
}
}
})
body {
font-family: Menlo, Consolas, monospace;
color: #444;
}
.item {
cursor: pointer;
}
.bold {
font-weight: bold;
}
ul {
padding-left: 1em;
line-height: 1.5em;
list-style-type: dot;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.js"></script>
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<!-- item template -->
<script type="text/x-template" id="item-template">
<li>
<input v-model="checkBoxes" type="checkbox" :value="model.name" id="checkbox">
<span
:class="{bold: isFolder}"
#click="toggle"
#dblclick="changeType">
{{ model.name }}
<span v-if="isFolder">[{{ open ? '-' : '+' }}]</span>
</span>
<ul v-show="open" v-if="isFolder">
<item
class="item"
v-for="(model, index) in model.children"
:key="index"
:model="model">
</item>
<li class="add" #click="addChild">+</li>
</ul>
</li>
</script>
<p>(You can double click on an item to turn it into a folder.)</p>
<!-- the demo root element -->
<ul id="demo">
<div>{{ checkBoxes }}</div>
<item
class="item"
:model="treeData">
</item>
</ul>
Related
I have a menu and would like to highlight the currently active menu item.
I think I should write in the first template the following: v-if(selected) and some function maybe.
<template>
<MenuTreeView #selected='collapsedMenuSelected' :items='filteredFirstLevelNavigation' :filter="false"/>
</template>
MenuTreeView:
<template>
<Tree :value="items" filterPlaceholder="Suche" :expandedKeys="expandedKeys" filterMode="lenient" :filter="filter"
selectionMode="single" #nodeSelect="onNodeSelect"></Tree>
</template>
In such cases, you need to store the id of the active element in the data of the parent component. This can be done by saving the id when selecting one of the rows.
Parent
<template>
<div class="menu">
<MenuItem
v-for="row in rows"
:active-id="activeId"
:key="row.id"
#select="handleSelect"
/>
</div>
</template>
<script>
import MenuItem from "./MenuItem";
export default {
name: "TheMenu",
components: {
MenuItem,
},
data() {
return {
activeId: null,
rows: [
{
id: 1,
label: "First",
},
{
id: 2,
label: "Second",
},
{
id: 3,
label: "Third",
},
],
};
},
methods: {
handleSelect(id) {
this.activeId = id;
},
},
};
</script>
Child
<template>
<div
class="menu-item"
:class="{
'menu-item_active': activeId === row.id,
}"
#click="handleSelect"
>
{{ row.label }}
</div>
</template>
<script>
export default {
name: "MenuItem",
props: {
row: {
type: Object,
required: true,
},
activeId: {
type: Number,
required: true,
},
},
methods: {
handleSelect() {
this.$emit("select", this.row.id);
},
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.menu-item {
/* your styles */
display: flex;
width: 100%;
}
.menu-item_active {
background-color: red;
}
</style>
i have two tasks:
Displaying the items of the item list 3 per row
Add a input field used to edit the title field in the currently selected element (selection should be made by clicking).
So, I made the first task based on this solving V-if inside v-for - display list of items in two columns and now i have a problem with my second task selecting method. It should be working for every item but on click selects an items from every list and can to edit only from first list. I think that problem can be in onItemClick(index) method but don't know why.
Any ideas about that?
Vue.component('item-list', {
template: '#item-list-template',
data() {
return {
items: [{
title: 'item 1'
},
{
title: 'item 2'
},
{
title: 'item 3'
},
{
title: 'item 4'
},
{
title: 'item 5'
},
{
title: 'item 6'
}
],
activeIndex: -1,
rowArray: []
}
},
mounted(){
this.fakeFetchData();
},
methods: {
// new method from example solving
fakeFetchData(){
var cloned = this.items.slice();
while (cloned.length > 0) {
let chunk = cloned.splice(0,3);
this.rowArray.push(chunk);
}
},
onItemClick(index) {
this.activeIndex = this.activeIndex === index ? -1 : index;
},
setActiveItemValue(event) {
const foundItem = this.items[this.activeIndex];
if (!foundItem) return;
return this.items[this.activeIndex].title = event.currentTarget.value;
}
},
computed: {
activeItemValue() {
return this.items[this.activeIndex]?.title ?? '';
}
}
});
Vue.component('item', {
template: '#item-template',
props: {
isActive: Boolean,
title: String
}
});
new Vue({
el: '#app'
});
li.active {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<item-list></item-list>
</div>
<script type="text/x-template" id="item-list-template">
<div>
<input type="text" placeholder="Edit selected items" :value="activeItemValue" #input="setActiveItemValue" />
<div class="items-col">
<ul class="items-list" v-for="(row, rowIndex) in rowArray" :key="rowIndex">
<item v-for="(item, i) in row" :key="i" :title="item.title" :isActive="activeIndex === i" #click.native="onItemClick(i)" />
</ul>
</div>
</div>
</script>
<script type="text/x-template" id="item-template">
<li class="item" :class="{ active: isActive }">{{ title }}</li>
</script>
<style>
.items-list {
display: flex;
}
</style>
I have modified and moved the fakeFetchData() from mounted to inside computed and modified the inner v-for inside the template. Check it out
Vue.component('item-list', {
template: '#item-list-template',
data() {
return {
items: [{
title: 'item 1'
},
{
title: 'item 2'
},
{
title: 'item 3'
},
{
title: 'item 4'
},
{
title: 'item 5'
},
{
title: 'item 6'
}
],
activeIndex: -1,
rowArray: []
}
},
methods: {
// new method from example solving
onItemClick(index) {
this.activeIndex = this.activeIndex === index ? -1 : index;
},
setActiveItemValue(event) {
const foundItem = this.items[this.activeIndex];
if (!foundItem) return;
return this.items[this.activeIndex].title = event.currentTarget.value;
}
},
computed: {
activeItemValue() {
return this.items[this.activeIndex]?.title ?? '';
},
fakeFetchData(){
// ********* Changes done below ************
var cloned = this.items.map((item, index) => {
return {title: item.title, id: index}
});
this.rowArray = [];
while (cloned.length > 0) {
let chunk = cloned.splice(0,3);
this.rowArray.push(chunk);
}
return this.rowArray;
// ******* End of changes ********
},
}
});
Vue.component('item', {
template: '#item-template',
props: {
isActive: Boolean,
title: String
}
});
new Vue({
el: '#app'
});
li.active {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<item-list></item-list>
</div>
<script type="text/x-template" id="item-list-template">
<div>
<input type="text" placeholder="Edit selected items" :value="activeItemValue" #input="setActiveItemValue" />
<div class="items-col">
<ul class="items-list" v-for="(row, rowIndex) in fakeFetchData" :key="rowIndex">
<!-- Changes done --><item v-for="item in row" :key="item.id" :title="item.title" :isActive="activeIndex === item.id" #click.native="onItemClick(item.id)" />
</ul>
</div>
</div>
</script>
<script type="text/x-template" id="item-template">
<li class="item" :class="{ active: isActive }">{{ title }}</li>
</script>
<style>
.items-list {
display: flex;
}
</style>
I'm trying to create a checkbox select only one.
<div id="app">
<div v-for="(question, index) in questions">
<input type="checkbox" value="question.value" v-model="additional_grouped" #change="uniqueCheck"> {{question.title}}
</div>
{{ result }}
</div>
My JS looks like the following:
new Vue({
el: '#app',
data() {
return {
additional: [],
additional_grouped: [],
questions: [
{
title: 'A',
value: 0
},
{
title: 'B',
value: 1
},
{
title: 'C',
value: 2
}
]
}
},
computed: {
result: function(){
return this.additional.concat(this.additional_grouped);
}
},
methods: {
uniqueCheck(e){
console.log(e)
this.additional_grouped = [];
if (e.target.checked) {
this.additional_grouped.push(e.target.value);
}
}
}
});
This is the old result.
I'm trying to get results like this.
I can do this by not the v-for method, but I want to do it this way. Because I have a lot of data, How can I checked value in v-for?
Here is my pen: enter link description here
You are missing the value binding (:value), here's your example fixed:
new Vue({
el: '#app',
data() {
return {
additional: [],
additional_grouped: [],
questions: [
{
title: 'A',
value: 0
},
{
title: 'B',
value: 1
},
{
title: 'C',
value: 2
}
]
}
},
computed: {
result: function(){
return this.additional.concat(this.additional_grouped);
}
},
methods: {
uniqueCheck(e){
this.additional_grouped = [];
if (e.target.checked) {
this.additional_grouped.push(e.target.value);
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(question, index) in questions">
<input type="checkbox" :value="question.value" v-model="additional_grouped" #change="uniqueCheck"> {{question.title}}
</div>
{{ result }}
</div>
Documentation
uses :value="question.value" instead of value="question.value"
new Vue({
el: '#app',
data() {
return {
additional: [],
additional_grouped: [],
questions: [
{
title: 'A',
value: 0
},
{
title: 'B',
value: 1
},
{
title: 'C',
value: 2
}
]
}
},
computed: {
result: function(){
return this.additional.concat(this.additional_grouped);
}
},
methods: {
uniqueCheck(e){
this.additional_grouped = [];
if (e.target.checked) {
this.additional_grouped.push(e.target.value);
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(question, index) in questions">
<input type="checkbox" :value="question.value" v-model="additional_grouped" #change="uniqueCheck"> {{question.title}}
</div>
{{ result }}
</div>
If you want to get an array of the values of checked boxes, you should just do this
<div id="app">
<div v-for="(question, index) in questions" :key="index">
<input type="checkbox" v-model="question.checked"> {{question.title}}
</div>
{{ result }}
</div>
and
new Vue({
el: '#app',
data() {
return {
questions: [
{
title: 'A',
value: 0
},
{
title: 'B',
value: 1
},
{
title: 'C',
value: 2
}
]
}
},
computed: {
result: function(){
return this.questions.filter(q => q.checked).map(q => q.value)
}
}
});
I have an object that will be modified on a click event, however it seems a refresh or clicking on another element is required for the object to actually update. What do I need to do so that the object is updated without a manual refresh or clicking on something else?
My code:
<template>
<div class="userTable">
<input type="text" v-model="query" placeholder="Search for a user" #focus="dimUsers()" #blur="undimUsers()" #keyup="selectedUser()">
<ul>
<li v-for="user in userList.users" :key="user.localized_name">
<img v-bind:src="user.portrait" class="userPortrait" :class="{'selectedUser': selectedUserPortrait.includes(user.portrait), 'dimmed': searching}" #click="loadUser(user)">
</li>
</ul>
</div>
</template>
....
props: {
userList
}
data: function () {
return {
userSelected: {
"name": '',
"portrait": '',
},
searching: false,
query: '',
selectedUserPortrait = []
}
}
methods: {
loadUser: function (user) {
this.userSelected = user
return;
}
dimUsers: function () {
this.searching = true
return;
}
undimUsers: function () {
this.searching = false,
this.selectedUserPortrait = []
this.query = ''
return;
},
selectedUser: function () {
this.selectedUserPortrait = [];
for (let i=0; i<userList.users.length; i++) {
if (this.userList.users[i].localized_name.toLowerCase().includes(this.query.toLowerCase())) {
this.selectedUserPortrait.push(this.userList.users[i].portrait)
} else {
this.selectedUserPortrait.splice(i, 1);
}
} return;
}
}
It's not entirely clear what you expect to see happen, but this code works as I expect. Does it work as you expect?
new Vue({
el: '#app',
data: {
userList: {
users: [{
portrait: 'https://via.placeholder.com/50?text=p1',
localized_name: 'one'
},
{
portrait: 'https://via.placeholder.com/50?text=p2',
localized_name: 'two'
},
{
portrait: 'https://via.placeholder.com/50?text=p3',
localized_name: 'three'
},
{
portrait: 'https://via.placeholder.com/50?text=p4',
localized_name: 'four'
},
{
portrait: 'https://via.placeholder.com/50?text=p5',
localized_name: 'five'
},
]
},
userSelected: {
"name": '',
"portrait": '',
},
searching: false,
query: ''
},
computed: {
selectedUserPortrait() {
return this.query ?
this.userList.users.filter((u) =>
u.localized_name.toLowerCase().includes(this.query.toLowerCase())
).map((u) => u.portrait) : [];
}
},
methods: {
loadUser: function(user) {
this.userSelected = user;
return;
},
dimUsers: function() {
this.searching = true
return;
},
undimUsers: function() {
this.searching = false;
this.query = '';
}
}
});
.selectedUser {
border: solid 2px red;
}
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
<input type="text" v-model="query" placeholder="Search for a user" #focus="dimUsers()" #blur="undimUsers()">
<ul>
<li v-for="user in userList.users" :key="user.localized_name">
<img v-bind:src="user.portrait" class="userPortrait" :class="{'selectedUser': selectedUserPortrait.includes(user.portrait), 'dimmed': searching}" #click="loadUser(user)">
</li>
</ul>
<div>{{userSelected.localized_name}}</div>
</div>
My App.vue looks as follows
<template>
<div id="app">
<home-central></home-central>
</div>
</template>
<script>
import HomeCentral from './components/HomeCentral';
export default {
name: 'App',
components: {
HomeCentral,
},
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
I have the following code in src/components/HomeCentral.vue
<template>
<div class="homecentral">
<input type="text" v-model="title"><br/>
<h1>{{title}}</h1>
<p v-if="showName">{{user.first_name}}</p>
<p v-else>Nobody</p>
<ul>
<li v-for="item in items" :key="item.id">{{item.title}}</li>it
</ul>
<button v-on:click="greet('Hello World')">Say Greeting</button>
<br/>
<input type="text" v-on:keyup="pressKey" v-on:keyup.enter="enterHit">
<label>First Name: </label><input type="text" v-model="user.firstName">
<br/>
<label>Last Name: </label><input type="text" v-model="user.lastName">
<h3></h3>
</div>
</template>
<script>
export default {
name: 'HomeCentral',
data() {
return {
title: 'Welcome',
user: {
firstName: 'John',
lastName: 'Doe',
},
showName: true,
items: [
{ title: 'Item One' },
{ title: 'Item Two' },
{ title: 'Item Three' },
],
};
},
methods: {
greet: function (greeting) {
alert(greeting);
},
pressKey: function (e){
console.log('pressed' + e.target.value);
},
enterHit() {
console.log('You hit enter');
},
computed: {
fullName: function() {
return this.user.firstName + ' ' + this.user.lastName;
}
},
},
};
</script>
<style scoped>
</style>
This throws the following error :
vue.runtime.esm.js?ff9b:205 Uncaught TypeError: fn.bind is not a function
at nativeBind (vue.runtime.esm.js?ff9b:205)
at initMethods (vue.runtime.esm.js?ff9b:3537)
at initState (vue.runtime.esm.js?ff9b:3305)
at VueComponent.Vue._init (vue.runtime.esm.js?ff9b:4624)
at new VueComponent (vue.runtime.esm.js?ff9b:4794)
at createComponentInstanceForVnode (vue.runtime.esm.js?ff9b:4306)
at init (vue.runtime.esm.js?ff9b:4127)
at createComponent (vue.runtime.esm.js?ff9b:5604)
at createElm (vue.runtime.esm.js?ff9b:5551)
at createChildren (vue.runtime.esm.js?ff9b:5678)
Things start to work fine if I remove the computed block :
computed: {
fullName: function() {
return this.user.firstName + ' ' + this.user.lastName;
}
},
Please help me figure out what I'm doing wrong.
The methods block should only contain javascript functions.
I also got this error when I had a nested object with methods inside the methods block.
Ie:
methods: {
namespace: {
methodName () {
}
}
}
Should be formatted to
methods: {
namespace-methodName () {
}
}
can you please add below code and try,
<template>
<div class="homecentral">
<input type="text" v-model="title"><br/>
<h1>{{title}}</h1>
<p v-if="showName">{{user.first_name}}</p>
<p v-else>Nobody</p>
<ul>
<li v-for="item in items" :key="item.id">{{item.title}}</li>it
</ul>
<button v-on:click="greet('Hello World')">Say Greeting</button>
<br/>
<input type="text" v-on:keyup="pressKey" v-on:keyup.enter="enterHit">
<label>First Name: </label><input type="text" v-model="user.firstName">
<br/>
<label>Last Name: </label><input type="text" v-model="user.lastName">
<h3></h3>
</div>
<script>
export default {
name: 'HomeCentral',
data() {
return {
title: 'Welcome',
user: {
firstName: 'John',
lastName: 'Doe',
},
showName: true,
items: [
{ title: 'Item One' },
{ title: 'Item Two' },
{ title: 'Item Three' },
],
};
},
methods: {
greet: function (greeting) {
alert(greeting);
},
pressKey: function (e){
console.log('pressed' + e.target.value);
},
enterHit() {
console.log('You hit enter');
}
},
computed: {
fullName: function() {
return this.user.firstName + ' ' + this.user.lastName;
}
},
};
</script>
<style scoped>
</style>
you accidentally nested computer inside method.
vue namespace bug & solutions
I will never recommended use vue & vue componets in this way
click event bind bug
solution (global this namespace bug)
just return an instance of vue, and everything is OK now.
more details
https://github.com/xgqfrms/vue/issues/49