I have a form select element and an array to display some device information on the page. I render all the devices with a v-for loop and I couldn't find a way to display them based on the selection from form-select element because I am not allowed to use v-if statements with v-for
Here is my form select;
<b-form-select v-model="selected" :options="options" size="sm" class="mr-10px"></b-form-select>
And here is how I display the devices in HTML;
<tr v-for="device in devices" :key="device.id">
<td style="width: 20px;">
<img :src="device.image"alt="..." />
</td>
<td>
<h6>{{device.name}}</h6></span>
<span>{{device.model}}</span>
</td>
<td>
<span
class="badge font-size-12"
:class="{
'bg-soft-danger': device.traffic >= '10' && device.traffic <= '30',
'bg-soft-warning': device.traffic >= '30' && device.traffic <= '60',
'bg-soft-success': device.traffic > '50',
}"
>
{{device.traffic}}
</span>
</td>
<td>
<span class="badge font-size-12" :class="[device.status == 'Active' ? 'bg-soft-success' : 'bg-soft-danger']">{{device.status}}</span>
</td>
</tr>
And now here is my javascript file for form-select options and device array...
export const devices = [
{
id: 1,
image: require("./mini.svg"),
name: "Username 1",
model: "Device Model",
traffic: "10mb",
status: "Active",
},
{
id: 2,
image: require("./mini.svg"),
name: "Username 2",
model: "Device Model 2",
traffic: "20mb",
status: "Active",
},
{
id: 3,
image: require("./mini.svg"),
name: "Username 3",
model: "Device Model 3",
traffic: "30mb",
status: "Deactive",
},
];
export const options = [
{
id: 1,
value: "All",
text: "All",
disabled: false,
},
{
id: 2,
value: "Location",
text: "Location",
disabled: true
},
{
id: 3,
value: "Traffic",
text: "Traffic",
disabled: false,
},
{
id: 4,
value: "Active",
text: "Active",
disabled: false,
},
{
id: 5,
value: "Deactive",
text: "Deactive",
disabled: false,
},
]
And here is how I import the javascript file and use these as data in my .vue file...
import { devices, options } from '../devices'
export default {
data() {
return {
devices: devices,
options: options,
selected: 'All',
};
},
};
Here is my question; how do I display these devices when the form-select is changed to Active or Deactive
I cant say v-if something equals to 'Active' because Vue doesn't let me use v-if with v-for. Is there any other way to do this?
Use computed property,
computed: {
computedDevices() {
// filter by conditions here
return this.devices.filter(device => device.status === 'Active')
}
}
then use computedDevices in the v-for instead of devices
<tr v-for="device in computedDevices" :key="device.id" >
...
</tr>
you can use v-if and v-for just don't do it in the same tag.
use v-if before or inside v-for
e.g.
<div v-for="i in 10">
<div v-if="i>
...
</div>
</div>
Vue does not support using v-if inline with v-for. You can create a parent v-for and child is v-if
Something like this
<template v-for="..." :key="...">
<div|tr|anytag v-if="...">
</div|tr|anytag>
</template>
Related
here's my problem :
I'm using the VueDraggable library in order to drag and drop elements between a DragBoard.vue and a DropBoard.vue, and a specific type of element should allow to be nested when it is in the DropBoard.
I'm going to take this element as an example :
"Grouped Items"
To do that I've followed this example : https://github.com/SortableJS/vue.draggable.next/blob/master/example/components/nested-example.vue
And this is what I get when I drop "Grouped Items" into the DropBoard.vue :
IMG
As you can see, the DropBoard appears a second time inside Grouped items for whatever reason. I've supposed that the nested-draggable tag also loop what is out of the draggable tag and I've no idea how to resolve that...
📄 dragItems.JSON (used in DragBoard.vue) :
1st object is a common element
2nd object is a nestable element
[
{
"type": "Simple list",
"title": "Simple list",
"id": 1,
"properties": "this is an item property"
},
...
{
"type": "Grouped items",
"title": "Grouped items",
"id": 10,
"properties": "this is an item property",
"tasks": []
},
...
]
🚩 DropBoard.vue template:
<template>
<div class="board">
<div class="head">Mock</div>
<div class="dd-container">
<draggable
:list="tasks"
v-model="dropItems"
item-key="title"
:group="{ name: 'items', put: true }"
#change="log"
>
<template #item="{ element }">
<div
class="item"
:key="element"
>
<div>
{{ element.title }}
</div>
<nested-draggable
v-if="element.tasks"
:tasks="element.tasks"
class="group-container"
/>
<div class="trashico" :key="index">
<i class="fas fa-trash" #click="deleteItem(index)"></i>
</div>
</div>
</template>
</draggable>
</div>
</div>
</template>
🚩 DropBoard.vue script
<script>
import draggable from "vuedraggable";
export default {
name: "nested-draggable",
components: {
draggable,
},
props: {
dropItems: {
type: Array,
required: true,
},
tasks: {
required: true,
type: Array,
},
},
data() {
return {
dropItems: [],
};
},
methods: {
deleteItem(id) {
this.dropItems.splice(id, 1);
},
},
};
</script>
Here is what I found while using the Vue DevTools, it's quit explicit about the problem.
See image: IMG
We are trying to select another item from drop down options but it doesn't changing the value:
<b-dropdown aria-role="list">
<b-button
icon-right="caret-down"
class="dropdown"
size="is-small"
slot="trigger"
>
<span>{{selectedProduct}}</span>
</b-button>
<b-dropdown-item
v-for="product in products"
:key="product.id"
aria-role="listitem"
#click="setItem(selectedProduct)"
v-model="selectedProductTag"
>
{{ product.name }}
</b-dropdown-item>
</b-dropdown>
**js part**
editProduct(){
...
...
this.selectedProductTag = {
id: selectedProductId,
name: product.name,
tag: product.tag,
};
}
setItem(selectedProduct) {
this.selectedProduct = selectedProduct
},
How to make it change both the selected option labels as well as v-model object data?
I see you're using a bootstrap-vue component, but, for what you're doing, I would suggest you to use the select component instead. But anyways, using the dropdown component:
In your HTML
<b-dropdown aria-role="list" v-bind:text="(selectedProduct.id == 0? 'Select here' : selectedProduct.name)">
<b-dropdown-item
v-for="(product, index) in products"
:key="index"
aria-role="listitem"
#click="setItem(product)"
>{{ product.name }}</b-dropdown-item>
</b-dropdown>
In your data section
data() {
products: [
{
id: 1,
name: "test1",
value: 1,
etc: "..."
},
{
id: 2,
name: "test2",
value: 2,
etc: "..."
}
//....
],
selectedProduct: {
id: 0,
name: "",
value: 0,
etc: "..."
}
//Make sure not to set selectedProduct to `null`, as it could result on an error
}
In your methods section
setItem: function(product) {
this.selectedProduct = product;
},
I've been following VueJS official documentation on passing data to child components with props; though I'm not working with a string template. I'm aware about what happens when your prop is camel case; you should write it as kebab case.
Nevertheless, this is not the case since it's all lowercase and won't work.
I'm using nuxt and I've separated my work into files, which are:
<template>
<div class="row">
<input type="text" name="" id="" placeholder="Write your question" v-model="text">
<select v-model="selectedField">
<option v-for="option in options" :key="option.id" :value="option.value">
{{ option.text }}
</option>
</select>
<button class="btn btn-sm btn-primary" #click="$emit('add-field')"
v-bind:class="{ disabled: ($parent.count <= 1 && $parent.count == identifier) }">
>{{identifier}}</button>
<button class="btn btn-sm btn-danger" #click="$emit('delete-field')">-</button>
</div>
Now for its JS file:
export default {
data () {
return {
options: [
{
id: 1,
value: 1,
text: "Radio"
},
{
id: 2,
value: 2,
text: "Rate"
},
{
id: 3,
value: 3,
text: "Text"
}
],
props: ['identifier'],
selectedField: 1,
text: "",
}
},
}
Now, for my parent component:
<template>
<div class="offset-md-3" id="qz">
<form-maker
v-for="item in questions" :key="item._id"
v-on:add-field="addField()"
v-on:delete-field="deleteField(item._id)"
v-bind:identifier="item._id" <<--What I want to set
ref="child"
></form-maker>
<button #click="saveForm" class="btn btn-large btn-success">SAVE</button>
</div>
</template>
Finally:
var vm = null;
export default {
layout: 'admin',
components: {
formMaker
},
data() {
return {
count: 1,
questions: [{
_id: 1//static
}]
}
},
}
What I'm trying to do is, to use the prop for some validations, nevertheless it throws the next error:
Property or method "identifier" is not defined on the instance but
referenced during render. Make sure that this property is reactive,
either in the data option, or for class-based components, by
initializing the property.
Thank you.
Here is where you go wrong. Props should not be in data(). See the code snippet below
<script>
export default {
props: ['identifier'],
data() {
return {
options: [
{
id: 1,
value: 1,
text: "Radio"
},
{
id: 2,
value: 2,
text: "Rate"
},
{
id: 3,
value: 3,
text: "Text"
}
],
selectedField: 1,
text: "",
}
}
}
</script>
I have a dataset that looks like this:
[
{id: 1, name: 'Foo', is_primary: false},
{id: 2, name: 'Bar', is_primary: true},
{id: 3, name: 'Baz', is_primary: false},
]
Only one of the entries is allowed to have is_primary = true. I'm displaying these items in a list, and I'm trying to display a radio button for each that the user can select to indicate that this is the primary one.
<tr v-for="item in items">
<td><input name="primary" type="radio" v-model="item.is_primary"></td>
</tr>
However, I don't think I'm understanding how this is supposed to work, because it's not working for me. Is this possible or am I supposed to handle this situation another way?
A set of radio inputs should v-model the same variable: a scalar that takes on the value associated with the selected radio.
To translate that back and forth into your item list, you can use a settable computed.
new Vue({
el: '#app',
data: {
items: [{
id: 1,
name: 'Foo',
is_primary: false
},
{
id: 2,
name: 'Bar',
is_primary: true
},
{
id: 3,
name: 'Baz',
is_primary: false
},
]
},
computed: {
primaryItem: {
get() {
return this.items.find((i) => i.is_primary);
},
set(pi) {
this.items.forEach((i) => i.is_primary = i === pi);
}
}
}
});
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
<div v-for="item in items">
<input name="primary" type="radio" :value="item" v-model="primaryItem">
</div>
<pre>{{JSON.stringify(items, null, 2)}}</pre>
</div>
Depending on the array 'r.meta.fields' a specific sort icon of each column needs to be shown. When the template is rendering, it is working correctly. But when the array change, the template isn't changing anymore.
<th v-for="field in r.meta.fields">
{{field.label}}
<a href="#" #click.prevent="sortField(field)">
<div class="fa fa-sort-up" v-if="field.sort_direction === 'desc'"></div>
<div class="fa fa-sort-down" v-else-if="field.sort_direction === 'asc'"></div>
<div class="fa fa-sort" v-else-if="field.sortable"></div>
</a>
What could be the problem?
You could create a mapping for the sort icons and handle the changes on click:
const vm = new Vue({
el: '#app',
data() {
const iconMap = {
sort: {
'asc': 'fa-sort-up',
'desc': 'fa-sort-down'
}
};
return {
r: {
meta: {
fields: [
{
label: 'field #1',
sortable: false,
sort_direction: 'asc',
icon: ''
},
{
label: 'field #2',
sortable: true,
sort_direction: 'desc',
icon: iconMap.sort['desc']// Initially sortable in descending order
}
]
}
},
iconMap
}
},
methods: {
sortField(field) {
let direction = (field.sort_direction === 'asc') ? 'desc' : 'asc';
let icon = this.iconMap.sort[direction] || '';
field.sort_direction = direction;
field.icon = icon;
}
}
})
Template or HTML
<div id="app">
<table>
<tr>
<th v-for="field in r.meta.fields" :key="field.label">
{{field.label}}
<a href="#"
:class="field.icon"
#click.prevent="sortField(field)"></a>
</th>
</tr>
</table>
</div>
if you are using something like
r.meta.fields = newValue
then this won't work.
you should use
Vue.set(r.meta.fields, indexOfItem, newValue)
document here: vue update array