I have a checkbox with the following events and props:
<v-checkbox
v-for="planets in allPlanets" :key="`planets_${planets.id}`"
:label="planets.name"
:value="planets.id"
v-model="selectedPlanets"
/>
Given that all of the checkboxes are brought in using a v-for, how can I check whether a checkbox is selected using a method or mounted function in Vue JS?
For example:
methods: {
checkSelected() {
????
},
Add a planets.selected key.
allPlanets: [
{ name: 'Planet name', value: 'Planet value' , selected : false },
...,
...
],
}
And in your template:
<v-checkbox
v-for="planets in allPlanets" :key="`planets_${planets.id}`"
:label="planets.name"
:value="planets.id"
v-model="planets.selected"
/>
Similar to:
Display multiple checkbox in table format using <v-for> and <v-checkbox> in Vuetify?
you have to make such a structure, so you know for each id, whether it is checked or not
new Vue({
data: () => ({
allPlanets: [
{
id: 32,
name: "planent",
selected: false
},
{
id: 365,
name: "planet 2",
selected: false
}
],
}),
methods: {
checkSelectedByIndex(index) {
return this.allPlanets[index].selected
},
checkSelectedById(id) {
return this.allPlanets.find(p => p.id === id)?.selected ?? false
}
}
});
and in the you have to set the v-model="planets.selected"
Given your layout the simplest methods is:
methods: {
checkSelected(id) {
return this.selectedPlanets.includes(id)
},
}
Related
So, i tried to dynamically toggle className, based on computed property, but it looks like pug doesn't have access to computed properties. I tried to manually set true to a className, then it's working.
I tried to reassign computed property to pug variable, but it still doesn't work
When using pure html, classes dynamically toggle correctly
Pug:
main#app.container
- var isPinkDefinitely = isPink ? 'pink' : 'gray';
div.section(
v-bind:class=[
'custom-section',
{
'custom-section--pink': isPink
}
]
v-bind:style=[
{
'background-color': isPinkDefinitely
}
]
) {{ isPink }}
form(#submit.prevent="addItem")
label.label Add another
div.field.has-addons
div.control
input.input(required, autofocus, v-model="newItem", placeholder="Remake this in React")
button(type="submit").button.is-info
i.fa.fa-plus
span Add
transition(name="slide")
div(v-show="items.length > 0")
hr
ul
transition-group(name="slide")
li(v-for="(item, index) in items", :key="item.id")
button(#click="removeItem(index)").button.is-danger
i.fa.fa-trash
span(v-text="item.desc")
hr
span(v-text="'Total: ' + items.length")
JS:
new Vue({
el: '#app',
data: {
items: [
{id: 1, desc: "Lorem"},
{id: 2, desc: "Ipsum"},
{id: 3, desc: "Dolor"},
],
newItem: '',
},
computed: {
isPink() {
return true;
}
},
methods: {
addItem () {
const id = this.items.length + 1
this.items.push({id, desc: this.newItem})
this.newItem = ''
},
removeItem (index) {
this.items.splice(index, 1)
},
},
})
https://codepen.io/itprogressuz/pen/qBoePob?editors=1010
UPD:
SO the basically solution was to just write all classes in one line inside just an object. see solution of #aykut
I just tried to solve your problem and I think i successed. You could use variable like me. If you want change it in computed function, it will change dynamically. You could also change it in methods functions when get users events. Here, my solution.
main#app.container
div.section(
class="default-style"
:class="{'bg-pink': isPink }"
) {{ setIsPink }}
form(#submit.prevent="addItem")
label.label Add another
div.field.has-addons
div.control
input.input(required, autofocus, v-model="newItem", placeholder="Remake
this in React")
button(type="submit").button.is-info
i.fa.fa-plus
span Add
transition(name="slide")
div(v-show="items.length > 0")
hr
ul
transition-group(name="slide")
li(v-for="(item, index) in items", :key="item.id")
button(#click="removeItem(index)").button.is-danger
i.fa.fa-trash
span(v-text="item.desc")
hr
span(v-text="'Total: ' + items.length")
// css file
.default-style{
background-color: gray;
}
.bg-pink{
background-color: pink;
}
// js file
new Vue({
el: '#app',
data: {
isPink: false,
items: [
{id: 1, desc: "Lorem"},
{id: 2, desc: "Ipsum"},
{id: 3, desc: "Dolor"},
],
newItem: '',
},
computed: {
setIsPink() {
this.isPink = !this.isPink;
return this.isPink;
}
},
methods: {
addItem () {
const id = this.items.length + 1
this.items.push({id, desc: this.newItem})
this.newItem = ''
},
removeItem (index) {
this.items.splice(index, 1)
},
},
})
I have developed a custom component which renders dropdown with a tree like structure inside it and allows the user to search for values inside the dropdown. Somehow the search works only after two levels of the tree structure.
We would be able to search only on the inside of NextJS label. The previous levels do not render results.
My function looks like this:
const searchFunction = (menu: treeData[], searchText: string) => {
debugger; //eslint-disable-line no-debugger
for (let i = 0; i < menu.length; i++) {
if (menu[i].name.includes(searchText)) {
setFound(true);
return menu[i].name;
} else if (!menu[i].name.includes(searchText)) {
if (menu[i].children !== undefined) {
return searchFunction(menu[i].children, searchText);
}
} else {
return 'Not Found';
}
}
};
And My data is like this:
import { treeData } from './DdrTreeDropdown.types';
export const menu: treeData[] = [
{
name: 'Web Project',
children: [
{
name: 'NextJS',
children: [
{
name: 'MongoDB',
},
{
name: 'Backend',
children: [
{
name: 'NodeJS',
},
],
},
],
},
{
name: 'ReactJS',
children: [
{
name: 'Express',
},
{
name: 'mysql',
children: [
{
name: 'jwt',
},
],
},
],
},
],
},
{
name: 'lorem project',
children: [
{
name: 'Vue Js',
children: [
{
name: 'Oracle Db',
},
{
name: 'JDBC',
children: [
{
name: 'Java',
},
],
},
],
},
{
name: 'ReactJS',
children: [
{
name: 'Express',
},
{
name: 'mysql',
children: [
{
name: 'jwt',
},
],
},
],
},
],
},
];
The sandbox link of the component is here:
https://codesandbox.io/s/upbeat-feynman-89ozi?file=/src/styles.ts
I haven't looked at the context that this is used in, so apologies if I'm missing something about how this is supposed to work. I've assumed that you can call setFound after running this function based on whether it finds anything or not and that it only needs to return one value. But hopefully this helps.
const menu = [{"name":"Web Project","children":[{"name":"NextJS","children":[{"name":"MongoDB"},{"name":"Backend","children":[{"name":"NodeJS"}]}]},{"name":"ReactJS","children":[{"name":"Express"},{"name":"mysql","children":[{"name":"jwt"}]}]}]},{"name":"lorem project","children":[{"name":"Vue Js","children":[{"name":"Oracle Db"},{"name":"JDBC","children":[{"name":"Java"}]}]},{"name":"ReactJS","children":[{"name":"Express"},{"name":"mysql","children":[{"name":"jwt"}]}]}]}];
const searchFunction = (menu, searchText) => {
let result;
for(let i = 0; i < menu.length; i++) {
if(menu[i].name.includes(searchText)) {
return menu[i].name;
} else if(menu[i].children !== undefined) {
result = searchFunction(menu[i].children, searchText);
if(result) return result;
}
}
return null;
};
console.log(searchFunction(menu, 'NextJS'));
console.log(searchFunction(menu, 'jwt'));
console.log(searchFunction(menu, 'foo'));
Looking at why the current version doesn't work, I think it goes something like this:
Let's take 'jwt' as the searchText.
We start in the 'Web Project' object, the name does not match, so we go to the else if block (BTW, we can never reach the else block as the else if condition is the opposite of the if condition).
The 'Web Project' object does have children so we will return from the new call to searchFunction; notice that 'lorem project' can never be reached as we will (regardless of the result) return the value of searchFunction and skip the rest of the loop.
Inside of our new and subsequent calls to searchFunction the same is going to happen until we find either a matching item or an item without children.
If we get to an item without children the the loop will successfully carry on to the siblings of the item.
If it doesn't find a match or an item with children it will exit the for loop and return undefined up the chain to the caller of the initial searchFunction.
I want do create some some different components by looping through an array of components like in my example. But I want to create different event handlers for each component. How can I define them in my componentData Array and bind them while looping?
componentData: [
{ name: TestPane, props: { data: "hello" }, id: 1 },
{ name: TestPane, props: { data: "bye" }, id: 2 },
],
]
<div v-for="component in componentData" :key="component.id">
<component v-bind:is="component.name" v-bind="component.props">
</component>
</div>
You can use the v-on directive. Let's understand how Vue bind your event listeners to the component first:
When you add a #input to a componnet what you are actualy doing is v-on:input. Did you notice the v-on over there? This means that you are actually passing an 'object of listeners' to the component.
Why not pass all of them in one go?
<template>
<section>
<div v-for="component in componentData" :key="component.id">
<component v-bind:is="component.name" v-bind="component.props" v-on="component.on">
</component>
</div>
</section>
</template>
<script>
export default {
data: () => ({
componentData: [
{ name: TestPane, props: { data: "hello" }, id: 1, on: { input: (e) => { console.log(e) } } },
{ name: TestPane, props: { data: "bye" }, id: 2, on: { input: (e) => { console.log(e); } } },
],
})
}
</script>
As you could guess you can listen to the events now inside of on object. You can add more if you would like as well:
{
name: TestPane,
props: { data: "hello" },
id: 1,
on: {
input: (e) => { console.log(e) },
hover: (e) => { console.log('This component was hovered') }
}
}
Add method names to your array like :
componentData: [
{ name: TestPane, props: { data: "hello" }, id: 1, method:"method1" },
{ name: TestPane, props: { data: "bye" }, id: 2 ,method:"method2"},
],
in template :
<component ... #click.native="this[component.method]()">
or add another method called handler which runs the appropriate component method :
<component ... #click.native="handler(component.method)">
methods:{
handler(methodName){
this[methodName]();
}
...
}
if the events are emitted from components, you should add their names and bind them dynamically :
componentData: [
{ name: TestPane, props: { data: "hello" }, id: 1,event:'refresh', method:"method1" },
{ name: TestPane, props: { data: "bye" }, id: 2 ,event:'input',method:"method2"},
],
<component ... #[component.event]="handler(component.method)">
I have 2 buttons which when clicked should filter by novelty or offer , I am able to make it so that when novelty is clicked it will filter by this but I am unable to make it so that if both are click it will filter by both novelty and offer
How can I make it so that when both novelty and offer are clicked it will filter by both of these?
https://www.webpackbin.com/bins/-KpVGNEN7ZuKAFODxuER
import React from 'react'
export default class extends React.Component {
constructor() {
super()
this.state = {
products: [
{ id: 1, novelty: true, offer: false, name: 'test1' },
{ id: 2, novelty: true, offer: true, name: 'test2' },
{ id: 3, novelty: false, offer: true, name: 'test3' }
],
display: 'all',
filters: [
{novelty:'true'},
{offer: 'true'}
]
}
}
setCategory (category) {
this.setState({
display: category
});
}
render() {
return(
<div>
<button onClick={()=>this.setCategory(true)}>Akce</button>
<button onClick={()=>this.setCategory(true)}>Offer</button>
{
this.state.products.filter( product =>
products.offer === this.state.display ||
this.state.display==='all')
.map(product =>
<div>{product.name}</div>
)
}
</div>
)
}
}
Here is the final version I've come up with:
import React from 'react'
export default class extends React.Component {
constructor() {
super()
this.state = {
products: [
{ id: 1, novelty: true, offer: false, name: 'test1' },
{ id: 2, novelty: true, offer: true, name: 'test2' },
{ id: 3, novelty: false, offer: true, name: 'test3' }
],
filters: {
novelty: true,
offer: true
}
}
}
setCategory (category) {
this.setState((state) => ({
filters: Object.assign({}, state.filters, { [category]: !state.filters[category] })
}));
}
render() {
console.log(this.state.filters)
return(
<div>
<button onClick={()=>this.setCategory('novelty')}>Akce</button>
<button onClick={()=>this.setCategory('offer')}>Offer</button>
{ this.state.products
.filter(product => product.novelty === this.state.filters.novelty || product.offer === this.state.filters.offer)
.map(product =>
<div key={product.id}>{product.name}</div>
)}
</div>
)
}
}
https://www.webpackbin.com/bins/-KpVHqfkjeraq6pGvHij
A few things:
Using a boolean instead of a string in your case is more adapted. (true instead of 'true').
display: 'all' isn't required for your use case. You can compute this value from your filters if you need to.
setCategory receive which category you want to set as a param.
I would rename setCategory to setFilter
Also, I'm using the asycnhronous version of setState. This allows you to hand in a function.
this.setState((state) => ({
filters: Object.assign({}, state.filters, { [category]: !state.filters[category] })
}));
Here I'm using Object.assign to create a new Object. I populate him with state.filters and finally I update the filter you want to.
category will either be novelty or offer and thanks to that I'm using the shorthand version of [category].
To conclude, I also update your filter function to check the product.novelty against the filter.novelty or the product.offer with the filter.offer
I'm using Vue.js 2.0 and the Element UI library.
I want to use a multiple select to attribute some roles to my users.
The list of all roles available is received and assigned to availableRoles. Since it is an array of object and the v-model accepts only an array with value, I need to extract the id of the roles trough the computed property computedRoles.
The current roles of my user are received and assigned to userRoles: [{'id':1, 'name':'Admin'}, {'id':3, 'name':'User'}].
computedRoles is then equals to [1,3]
The preselection of the select is fine but I can't change anything (add or remove option from the select)
What is wrong and how to fix it?
http://jsfiddle.net/3ra1jscx/3/
<div id="app">
<template>
<el-select v-model="computedRoles" multiple placeholder="Select">
<el-option v-for="item in availableRoles" :label="item.name" :value="item.id">
</el-option>
</el-select>
</template>
</div>
var Main = {
data() {
return {
availableRoles: [{
id: 1,
name: 'Admin'
}, {
id: 2,
name: 'Power User'
}, {
id: 3,
name: 'User'
}],
userRoles: [{'id':1, 'name':'Admin'}, {'id':3, 'name':'User'}]
}
},
computed : {
computedRoles () {
return this.userRoles.map(role => role.id)
}
}
}
I agree mostly with #wostex answer, but he doesn't give you the userRoles property back. Essentially you should swap computedRoles and userRoles. userRoles becomes a computed property and computedRoles is a data property. In my update, I changed the name of computedRoles to selectedRoles.
var Main = {
data() {
return {
availableRoles: [{
id: 1,
name: 'Admin'
}, {
id: 2,
name: 'Power User'
}, {
id: 3,
name: 'User'
}],
selectedRoles:[1,2]
}
},
computed : {
userRoles(){
return this.availableRoles.reduce((selected, role) => {
if (this.selectedRoles.includes(role.id))
selected.push(role);
return selected;
}, [])
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
And here is the fiddle.
Check the solution: jsfiddle
The caveat here is that computed properties are getters mainly. You can define setter for computed property, but my approach is more vue-like in my opinion.
In short, instead of v-model on computed set v-model for data property.
Full code:
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui/lib/index.js"></script>
<div id="app">
<template>
<el-select v-model="ids" multiple placeholder="Select" #change="logit()">
<el-option v-for="item in availableRoles" :label="item.name" :value="item.id">
</el-option>
</el-select>
</template>
</div>
var Main = {
data() {
return {
availableRoles: [{
id: 1,
name: 'Admin'
}, {
id: 2,
name: 'Power User'
}, {
id: 3,
name: 'User'
}],
userRoles: [{'id':1, 'name':'Admin'}, {'id':3, 'name':'User'}],
ids: []
}
},
mounted() {
this.ids = this.userRoles.map(role => role.id);
},
methods: {
logit: function() {
console.log(this.ids);
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')