problems with having a vue component in for loop - vue.js

Please first take a look at following code:
<tr v-for="item in items">
<td> ... </td>
<td> ... </td>
<td> ... </td>
<td> <actions-manager :item="item" /> </td>
</tr>
As you can see I am including actions-manager component inside a for loop for each item.
In actions-manager I have following code :
<ul>
<li v-for="action in item.actions">
<a #click="initAction(action.name, item.id)" href="javascript:;">
{{action.name}}
</a>
<a #click="someOtherMethod()" href="javascript:;">
...
</a>
</li>
</ul>
my data:
data : function() {
return {
currentActionName : null,
currentItemId : null
}
}
my methods :
methods : {
initAction : function (actionName, itemId) {
this.currentActionName = actionName;
this.currentItemId = itemId;
},
someOtherMethod : function () {
console.log(this.currentActionName, this.currentItemId);
}
}
and now initAction method works completely fine for the first table row and sets currentActionName and currentItemId properly, but not for other rows.
for example :
when I call someOtherMethod after initAction in first table row I can access currentActionName and currentItemId and it works fine for first row, but for other rows initAction is not setting values for currentActionName and currentItemId.
any ideas why?

According to the structure of your code I guess these two components are similar to what you want:
parent component that has tr tags:
<template>
<section>
<tr v-for="item in items">
<td> <actions-manager :item="item" />
item1
</td>
<td> <actions-manager :item="item" />
item2
</td>
<td> <actions-manager :item="item" />
item3
</td>
<td> <actions-manager :item="item" />
item4
</td>
<td> <actions-manager :item="item" />
item5
</td>
</tr>
</section>
</template>
<script>
import actionsManager from "#/components/actionsManager.vue";
export default {
data() {
return {
items: [
{
actions: {
action1: {
name: "first item",
},
action2: {
name: "second item",
},
action3: {
name: "third item",
}
},
id: 1
},
{
actions: {
action4: {
name: "fourth item",
},
action5: {
name: "five item",
},
action6: {
name: "six item",
}
},
id: 2
},
]
}
},
components: {
actionsManager
}
}
</script>
actionsManager component:
<template>
<ul>
<li v-for="action in item.actions">
<a #click="initAction(action.name, item.id)" href="javascript:;">
{{action.name}}
</a>
<a #click="someOtherMethod()" href="javascript:;">
...
</a>
</li>
</ul>
</template>
<script>
export default {
props: ["item"],
data : function() {
return {
currentActionName : null,
currentItemId : null
}
},
methods : {
initAction : function (actionName, itemId) {
this.currentActionName = actionName;
this.currentItemId = itemId;
},
someOtherMethod : function () {
console.log(this.currentActionName, this.currentItemId);
}
}
}
</script>
And that works fine in my vue-cli development environment! I could not understand what is the problem. You can test those two component and see that console logs data correctly.

Related

Vue 3 problems with value input

I have two components, the first one is a row insertion table and the second one contains the rows
I have a problem with the inputs, everything works fine except that when I delete a line in the middle of the table the values of the other inputs are reset,
the parent component :
<script>
import editortable from "./editortable.vue";
export default {
data: () => {
return {
elements: [{ id: 0, ref: "1", equation: "2" }],
nextid: "1",
};
},
methods: {
removrow(index) {
this.elements.splice(index, 1);
},
add() {
this.elements.push({
id: this.nextid++,
ref: "",
});
},
},
components: {
editortable,
},
};
</script>
<tbody>
<tr
is="vue:editortable"
v-for="(element, index) in elements"
:iden="element.id"
:referen="element.ref"
:eq="element.equation"
:key="index"
#delete="removrow(index)"
></tr>
</tbody>
<button type=" button" class="btn btn-primary" #click="add">
Ajouter une ligne
</button>
** component :**
<script>
export default {
props: ["iden", "referen", "eq"],
emits: ["delete"],
methods: {
deleterow() {
this.$emit("delete");
},
},
};
</script>
<template>
<tr>
<td>
<input class="form-control" type="ipunt" :value="iden" />
</td>
<td><input class="form-control" type="ipunt" :value="referen" /></td>
<td><input class="form-control" type="ipunt" :value="eq" disabled /></td>
<td>
<button type="button" class="btn btn-danger" #click="deleterow">
delete
</button>
</td>
</tr>
</template>
There is 1 obvious issue is you shouldn't use index as the key in your case.
Change it to use the id.
:key="element.id"
And in removrow(id), using id find the index first and then remove it should be ok.
removrow(id) {
const index = this.elements.findIndex(ele => ele.id === id);
this.elements.splice(index, 1);
}

how to display 2 lists filtered by another field from one model

Model: Article.
id.
name.
type: ['code', 'design']
API gets all articles
How can I display two lists:
all articles with type ='Code',
all articles with type = 'Design'
In other words, is it possible to filter the API query
Or is it better to do it on the API side?
Extra: same as above but in a nested environment (ie Articles belong to Category. How to do it on the category detail page.
You can use computed properties. I built a sample component:
EDIT: Took some time to DRY it up.
Parent.vue
<template>
<div class="parent">
<div class="row">
<div class="col-md-6">
<article-list title="Code Articles" :articles="codeArticles" />
</div>
<div class="col-md-6">
<article-list title="Design Articles" :articles="designArticles" />
</div>
</div>
</div>
</template>
<script>
import ArticleList from './ArticleList.vue'
export default {
components: {
ArticleList
},
data() {
return {
articles: [
{
id: 1,
name: 'Article1',
type: 'Code'
},
{
id: 2,
name: 'Article2',
type: 'Design'
},
{
id: 3,
name: 'Article3',
type: 'Code'
},
{
id: 4,
name: 'Article4',
type: 'Design'
},
]
}
},
computed: {
codeArticles() {
return this.articles.filter(article => article.type === 'Code');
},
designArticles() {
return this.articles.filter(article => article.type === 'Design');
}
}
}
</script>
ArticleList.vue
<template>
<div class="two-filtered-lists">
<h5>{{ title }}</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>TYPE</th>
</tr>
</thead>
<tbody>
<tr v-for="article in articles" :key="article.id">
<td>{{ article.id }}</td>
<td>{{ article.name }}</td>
<td>{{ article.type }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true
},
articles: {
type: Array,
required: true
}
}
}
</script>

How to close a dropdown list when clicking anywhere

How do I close the dropdown that opens when I click anywhere?
This code:
<tr class="inputs-table">
<td>Type object: </td>
<td>
<div class="select">
<div class="select-header form-control" v-on:click="AddForm(1)">
<span class="select__current">Please select one option</span>
</div>
<addForm v-if="addedForm === 1" />
</div>
</td>
</tr>
<tr class="inputs-table">
<td>Type business-model: </td>
<td>
<div class="select">
<div class="select-header form-control" v-on:click="AddForm(2)">
<span class="select__current">Please select one option</span>
</div>
<addForm v-if="addedForm === 2" />
</div>
</td>
</tr>
export default {
name: 'Index',
data() {
return {
addedForm: 0,
}
},
methods: {
AddForm(number) {
this.addedForm = number;
},
closeForm() {
this.addedForm = false;
}
},
components: {
addForm,
}
}
Drop-list:
What can I try next?
You can make use if window.onClick and see whether or not it matches you'r element. If it doesn't then close it; call the function that checks clicks at mounted and beforeDestroy or breforeRouteLeave.
E.g:
mounted(){
this.hideNav()
},
methods: {
hideNav() {
window.onclick = function(event) {
if (!event.target.matches('.select__body')) {
this.closeForm()
}
}
}
},
beforeRouteLeave() {
this.hideNav()
}
<template>
<div
#click="dropdownIsActive = !dropdownIsActive"
ref="dropdown"
>
</div>
</template>
<script>
export default {
data: () => ({
dropdownIsActive: false
}),
created () {
window.addEventListener('click', this.closeDropdown)
},
methods: {
closeDropdown (e) {
if (!this.$refs.dropdown.contains(e.target)) {
this.dropdownIsActive = false
}
}
},
beforeDestroy () {
window.removeEventListener('click', this.closeDropdown)
}
}
</script>

dynamically call an object property in a v-for loop

so i ran into a problem again,
i want to make a table component where you can send a array to the component, and it will render a table for you
we set it up like this
<template>
<section class="container">
<Apptable :search="true" :loader="true" title="User data" :data="users"/>
</section>
</template>
<script>
import Apptable from "~/components/table.vue";
export default {
components: {
Apptable
},
data() {
return {
users: [
{
id: 1,
name: "Lars",
Adres: "hondenstraat 21",
phone: "06555965"
},
{
id: 1,
name: "John",
Adres: "verwelstraat 35",
phone: "06555965"
}
]
};
}
};
</script>
i send data to the component and loop it from there like this
<template>
<section class="container">
<h2 v-if="title">{{title}}</h2>
<input v-if="search" class="search" placeholder="Search">
<button v-if="loader" class="update" #click="dialog = true">Update</button>
<table class="table">
<thead>
<tr class="tableheader">
<th v-for="(item, index) in Object.keys(data[0])" :key="index">{{item}}</th>
</tr>
</thead>
<tbody>
<tr class="userdata" v-for="(item, index) in data" :key="index">
<td v-for="(name, index) in Object.keys(data[index])" :key="index">{{//TODO: I WANT TO SELECT THE ITEM.DYNAMIC PROPERTY}}</td>
</tr>
</tbody>
</table>
<loader v-if="loader" :trigger="dialog"/>
</section>
</template>
<script>
import loader from "~/components/loader.vue";
export default {
components: {
loader
},
data() {
return {
dialog: false
};
},
watch: {
dialog(val) {
if (!val) return;
setTimeout(() => (this.dialog = false), 1500);
}
},
props: {
data: {
type: Array,
required: true
},
title: {
type: String,
required: false,
default: false
},
loader: {
type: Boolean,
required: false,
default: false
},
search: {
required: false,
type: Boolean,
default: true
}
}
};
</script>
so if you look at the table. were i left the todo, if i put in the {{item}} in the todo place. i will get this in my column
enter image description here
but i want to select the key of the object dynamically. but if i put {{item.name}} in the todo place it will not select the key dynamically.
so the point is that i want to dynamically call a property from the object in the v-for so the columns will get the data in the cells.
You should use item[name] instead of item.name
<tbody>
<tr class="userdata" v-for="(item, index) in data" :key="index">
<td v-for="(name, nIndex) in Object.keys(data[index])" :key="nIndex">
{{ item[name] }}
</td>
</tr>
</tbody>

vue.js how to v-model values as separate arrays

from the backend I'm getting an array like this.
then I render this array to a table like this
My code
<tr v-for="item in items">
<td>
{{item[1]}}
</td>
<td>
{{item[2]}}
</td>
<td>
<input type="text" v-model="grnItems[items[1]]"/>
</td>
</tr>
This is a purchase return component
what I want is v-model this each an every input element as a separate array along with the item name.
like this
[
["chicken","12"]
["chille","19"]
]
How do I achieve this using vue.js?
Use an auxiliar array with the data populated the way you want, some example using computed properties
new Vue({
el: '#app',
data: {
items: [['1', 'some text', '66'], ['2', 'another text', '12'], ['5', 'random text', '89']],
result: []
},
computed: {
procesedItems() {
return this.items.map(i => ({
id: i[0],
name: i[1],
amount: i[2]
}))
}
},
methods: {
doSomething() {
this.result = this.procesedItems.map(i => {
let aux = [];
aux.push(i.name, i.amount)
return aux
})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<ul>
<li v-for="item in procesedItems"> {{item.id }} {{item.name }} <input v-model="item.amount"/></li>
</ul>
<button #click="doSomething">Calculate</button>
{{ result }}
</div>