I pull data in from an Axios request. I have approx 500 records each with several results. I have simplified it for ease.
I display the data on the main component as a drop-down menu and it displays one entry for 'John' and one for 'Jane'. It splits the JSON data and removes duplicates and this works fine.
What Im stuck with is how to display the same data in the child component. At the moment it just displays 'Jane,Jane,Jane,John,John,John,Jane,'.
Do I have to do another methods in the child component for this? I can pass in a prop with uniquenames but it just displays them all in each result.
Any help would be most appreciated.
JSON
"results": [
"result": {
"name": "Jane,Jane,John,John,John,John,Jane,Jane,"
}
"result": {
"name": "Jane,Jane,Jane,John,John,John,Jane,"
}
"result": {
"name": "John,Jane,"
}
"result": {
"name": "Jane"
}
]
Main component
<Result
v-for="result in Results"
:result="result"
:key="result.docNum"
/>
<ul v-for="name in uniquenames" :key="name">
<li>
<label class="pr-2" for="name">{{ name }}</label>
<input
type="checkbox"
v-model="names"
:value="name"
/>
</li>
</ul>
methods: {
const metanames = this.Results
.filter((result) => results.result && results.result.name)
.map((item) => item.name)
.filter((names, i, arr) => arr.indexOf(names) === i)
let names = []
metanames.forEach((item) => {
const splitArr = item.split(', ')
names = names.concat(splitArr)
})
this.uniquenames = names.filter(
(names, i, arr) => arr.indexOf(names) === i,
)
}
Child component
<template>
<div>
{{ name }}
</div>
</template>
<script>
export default {
name: "result",
props: {
result: Object,
},
data: function () {
return {
name: this.results.result.name
}
}
Your <Result> component in the Parent template is passing in the items of Result in a loop, but you have not modified Result, hence why the original non-unique array of names is passed in. Create and pass down a computed property instead which should be a modified version of Result in the format you want. A computed property can update/reformat automatically whenever its dependencies (the original Result) changes. Whenever the computed property updates so will the prop
<Result
v-for="result in modifiedResults"
:result="result"
:key="result.docNum"
/>
computed: {
modifiedResults() {
// unfinished code for returning modified Results containing array of unique names
return [...this.Results].map(r => {
let uniquenames = getUniqueNames(r.names)
return { ...r, names: uniquenames }
})
},
},
as the comment says you'll need to finish/change the computed property to return exactly what you want. I can't do it as I'm not completely sure how all the different arrays and object properties in your code fit together, but computed property is definitely the way to go!
Related
This is what i have:
Template
<div
v-for="(filter, index) in filtersList"
:key="index"
class="option-block"
>
<label
v-for="value in filter.values"
:key="value.id"
class="option-block__container"
>
{{ value.title }}
<input
type="checkbox"
v-model="filtersValues[filter.name]"
:value="value.value"
>
<span class="option-block__checkmark"></span>
</label>
</div>
And the part of my vue code:
data() {
return {
filtersList: {},
filtersValues: {}
}
},
beforeMount() {
this.loadInitData();
this.initFilters();
},
methods: {
loadInitData() {
const data = JSON.parse(this.$el.getAttribute('data-data'));
this.filtersList = data.filters;
},
initFilters() {
for (let i in this.filtersList) {
if (!this.filtersList.hasOwnProperty(i)) {
continue;
}
this.filtersValues[this.filtersList[i].name] = [];
}
}
}
It works, but when i call initFilters() method again (for reseting) checkboxes are still selected, and i don't know why.
The way you are assigning new, empty arrays to filterValues is not reactive.
If you change your initFilters to assign an entire new value to filterValues, you don't need to worry about using Vue.set(). For example
initFilters() {
this.filtersValues = this.filtersList.reduce((vals, { name }) => ({
...vals,
[ name ]: []
}), {})
}
Demo ~ https://jsfiddle.net/cjx09zwt/
Where did filter.values come from in line 2 of template?
Anyways vue would not be able to track the changes you are making (judging from the visible code)
There are some caveats to vue 2's reactivity. Check here for more info.
TLDR; you will need to declare anything you want to be made reactive in the component's data option upfront.
HTH
Why name 2 not changed and not reactive? What wrong?
How can I make it reactive so that when the properties of the object change, the DOM also changes?
When I delete Name 2 nothing happens
<template>
<div>
<div v-for="(item, index) in items" v-bind:key="item.id">
<p>{{item.name}}</p>
</div>
<button v-on:click="deleteItem">
Delete Name 2
</button>
</div>
</template>
<script>
export default {
data:function(){
return {
items:[
{
id: 1,
name: "Name 1"
},
{
id: 2,
name: "Name 2"
}
]
}
},
methods: {
deleteItem: function(){
this.items[1] = [];
console.log(this.items);
alert("DELETED");
}
},
created: function(){
let self = this;
setTimeout(function(){
self.items[1] = [];
}, 2000);
}
};
</script>
the reactivity in vue (2) is a little bit tricky, this link explain you how to solve this issue
https://v2.vuejs.org/v2/guide/reactivity.html#For-Arrays
Modify your delete item function. Don't set it to an empty array. Filter the array like this:
Your HTML Markup :
<button #click="deleteItem(2)">
Delete Name 2
</button>
Send the id of the item that you want to delete to the deleteItem() as an argument.
deleteItem: function(itemId){
let filtered = this.items.filter((each) => {
return each.id !== itemId;
})
this.items = filtered; //Finally mutate the items[] in data
}
You are actually assigning an empty array to item with index 1, instead of removing it.
If you want to remove the element with index 1 simply use splice() and Vue will automatically react to it:
this.items.splice(1, 1); // The first 1 is the index
Or, alternatively use Vue.delete(), which is originally to remove properties from object, but can also remove items from arrays:
Vue.delete(this.items, 1); // 1 is the index
More info: https://v2.vuejs.org/v2/api/#Vue-delete
The class binding is not working as expected using a template, as the image below shows:
I have a array with many categories and when the user click it has to filter, this i already did, for whose category he wants. My problem is that once i click in other category, the ones before stills on.
The template receives a array such as:
categories: ["todos", "beer", "eco-bag", "paper-bag", "suplementos", "chas", "doces", "chocolates", "dieteticos"]
Here is the template:
<template id="category-box">
<span :class="{active: currentFilter == category}" #click="setFilter(category)">
{{category}}
</span>
</template>
The call inside the #app element:
<div id="category">
<category v-for="category in categories" :category="category"></category>
</div>
The code that handles it:
const Category = Vue.component("category", {
template: "#category-box",
props: {
"category": String,
},
data: function() {
return {
currentFilter: "todos"
}
},
methods: {
setFilter: function(filter) {
this.currentFilter = filter;
this.$parent.$emit('filteredCategory', filter);
}
}
});
It looks like currentFilter is scoped to the individual Category component. Each time you set it you are setting it for that component. Move it to the parent scope so that there is only one currentFilter for all of your categories.
In my data object, I need to push objects into an array called editions.
data() {
return {
editions: []
}
}
To do this, I am dynamically creating a form based on some predetermined field names. Here's where the problem comes in. I can't get v-model to cooperate. I was expecting to do something like this:
<div v-for="n in parseInt(total_number_of_editions)">
<div v-for="field in edition_fields">
<input :type="field.type" v-model="editions[n][field.name]" />
</div>
</div>
But that isn't working. I get a TypeError: _vm.editions[n] is undefined. The strange thing is that if I try this: v-model="editions[n]"... it works, but I don't have the property name. So I don't understand how editions[n] could be undefined. This is what I'm trying to end up with in the data object:
editions: [
{
name: "sample name",
status: "good"
},
...
]
Can anyone advise on how to achieve this?
But that isn't working. I get a TypeError: _vm.editions[n] is undefined.
editions is initially an empty array, so editions[n] is undefined for all n. Vue is essentially doing this:
const editions = []
const n = 1
console.log(editions[n]) // => undefined
The strange thing is that if I try this: v-model="editions[n]"... it works
When you use editions[n] in v-model, you're essentially creating the array item at index n with a new value. Vue is doing something similar to this:
const editions = []
const n = 2
editions[n] = 'foo'
console.log(editions) // => [ undefined, undefined, "foo" ]
To fix the root problem, initialize editions with an object array, whose length is equal to total_number_of_editions:
const newObjArray = n => Array(n) // create empty array of `n` items
.fill({}) // fill the empty holes
.map(x => ({...x})) // map the holes into new objects
this.editions = newObjArray(this.total_number_of_editions)
If total_number_of_editions could change dynamically, use a watcher on the variable, and update editions according to the new count.
const newObjArray = n => Array(n).fill({}).map(x => ({...x}))
new Vue({
el: '#app',
data() {
const edition_fields = [
{ type: 'number', name: 'status' },
{ type: 'text', name: 'name' },
];
return {
total_number_of_editions: 5,
editions: [],
edition_fields
}
},
watch: {
total_number_of_editions: {
handler(total_number_of_editions) {
const count = parseInt(total_number_of_editions)
if (count === this.editions.length) {
// ignore
} else if (count < this.editions.length) {
this.editions.splice(count)
} else {
const newCount = count - this.editions.length
this.editions.push(...newObjArray(newCount))
}
},
immediate: true,
}
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.min.js"></script>
<div id="app">
<label>Number of editions
<input type="number" min=0 v-model="total_number_of_editions">
</label>
<div><pre>total_number_of_editions={{total_number_of_editions}}
editions={{editions}}</pre></div>
<fieldset v-for="n in parseInt(total_number_of_editions)" :key="n">
<div v-for="field in edition_fields" :key="field.name+n">
<label>{{field.name}}{{n-1}}
<input :type="field.type" v-if="editions[n-1]" v-model="editions[n-1][field.name]" />
</label>
</div>
</fieldset>
</div>
I have a table and a select box for each row. I want the check box to model a value in the data that doesn't actually exist, yet.
<tr v-for="item in someData">
<input type="checkbox" v-model="item.selected"></td>
<input type="checkbox" v-model="item.name"></td>
<tr>
My data when loaded from the DB looks like this:
someData: [
{'name': 'john'},
{'name': 'kate'},
{'name': 'aaron'},
]
When the user presses a Select All button it should update the selected key even if it doesn't exist (well thats the idea)
toggleSelect: function () {
this.someData.forEach(element => {
element.selected = !element.selected;
});
}
However the checkboxes don't react even though the values have been updated. To make this work I need to get the data and add the key/value manually prior to loading it into view and rendering
getDatabaseData: function () {
// some code omitted
response['data'].forEach(element => {
element["selected"] = false;
});
app.someData = response['data']
}
Am I doing it correctly? Am I right in thinking Vue won't be reactive to values that didn't exist prior to rendering?
Try this idea,
in vue component.
<input type="checkbox" v-model="selectAll"> Select All
<tr v-for="item in someData" :key="item.name">
<td>
<input type="checkbox" v-model="selected" :value="item.name">
</td>
{{ item.name }}
</tr>
script:
data() {
return {
selectAll: false,
selected: [],
someData: [{ name: "john" }, { name: "kate" }, { name: "aaron" }]
};
},
watch: {
selectAll(value) {
// validate if value is true
if (value) {
this.someData.forEach(item => {
// push unique value
if(this.items.indexOf(item.name) === -1) {
this.selected.push(item.name);
}
});
} else {
// Unselect all
this.selected = [];
}
}
}
You have a selected variable where the selected Items are located. selectAll variable to select all items and push to selected variable.
You should be using Vue.set to update the value of the selected property on your objects in order to be reactive, like this:
import Vue from 'vue';
...
toggleSelect: function () {
this.someData.forEach(element => {
Vue.set(element, 'selected', !element.selected);
});
}