Vue : How to implement Table search - javascript

I have a simple search functionality on table.
But somehow its not working,
I should be be getting filtered rows on the table while searching.
Following is the code:
// Search Input
<div class="dv-header-search">
<input type="text" class="dv-header-input"
placeholder="Search"
v-model="query.search_input">
</div>
//Table row
<tr v-for="row in filteredRow">
<td v-for="(value, key) in row">{{value}}</td>
</tr>
data() {
return {
model: { data: [] },
columns: {},
query: {
search_input: ''
},
}
},
// Setting model after API call
.then(function(response) {
Vue.set(vm.$data, 'model', response.data.model)
})
computed: {
filteredRow: function(){
return this.model.data.filter((row) => {
return row;
});
}
}
Now the filteredRow calls on page load, What am i missing here.

```
filteredRow: function(){
return this.model.data.filter((row) => {
return row;
});
}
```
should be
```
filteredRow: function(){
return this.model.data.filter((row) => {
// containOrNot should return bool
return containOrNot(row, this.query.search_input)
});
}
```

filteredRow: function(){
return this.model.data.filter((row) => {
//i don't know you value key.. so just picking first property
for(var key in row){
return String(row[key]).indexOf(this.query.search_input);
}
});
}

Related

how to prevent a re-rendering of a variable that is not being used in the HTML of my vue.js component?

I am trying to recreate a real example of my code.
In my real code, this line is actually a component that will fetch an endpoint every few seconds, and fetch a random array of "n" length, myData it will contain these fetch.
<div v-for="item in addingData(myData)"> <!-- in My real code, "myData" should be the answer of an endpoint, is an setInterval, returns data like [{id:1},{id:2}] -->
{{ item.id }}
</div>
I am simulating that the response changes in myData with the help of setTimeOut
mounted() {
setTimeout(() => {
console.log('First data');
this.myData = [{ id: 3 }, { id: 2 }, { id: 1 }];
setTimeout(() => {
console.log('second data');
this.myData = [{ id: 4 }, { id: 4 }];
setTimeout(() => {
console.log('Third data');
this.myData = [];
}, 3000);
}, 3000);
}, 2000);
},
I am trying to make that every time I receive data in myData, the list of the concatenation of the received data is shown without having repeated data. That's why every time I receive data, that calls the function addingData(myData) that will do this data concatenation.
I'm using the function v-for="item in addingData(myData) and auxData is the variable that will do this concatenation.
why when there is new data, the addingData function is called 2 times and how can I prevent it?
in terms of performance this should be the output in the console.log:
what causes this re-rendering and how can I avoid it?
this is my live code:
https://stackblitz.com/edit/vue-l7gdpj?file=src%2FApp.vue
<template>
<div id="app">
<div v-for="item in addingData(myData)">
{{ item.id }}
</div>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
name: 'App',
data() {
return {
myData: [],
auxData: [],
};
},
mounted() {
setTimeout(() => {
console.log('First data');
this.myData = [{ id: 3 }, { id: 2 }, { id: 1 }];
setTimeout(() => {
console.log('second data');
this.myData = [{ id: 4 }, { id: 4 }];
setTimeout(() => {
console.log('Third data');
this.myData = [];
}, 3000);
}, 3000);
}, 2000);
},
methods: {
addingData(getDataFetch) {
console.log('Entering AddingData', getDataFetch);
if (getDataFetch.length !== 0) {
if (this.auxData.length === 0) {
//Adding initial data
this.auxData = getDataFetch;
} else {
//prevent duplicated values
getDataFetch.forEach((item) => {
const isNewItem = this.auxData.find((itemAux) => {
return item.id === itemAux.id;
});
if (!isNewItem) {
//adding new data
this.auxData.unshift(item);
}
});
}
} else {
//if there is not data, return []
return this.auxData;
}
},
},
};
</script>
As per my understanding, You want to combined the unique objects in to an array getting from multiple API calls and show them into the template using v-for. If Yes, You can achieve that by using computed property.
As you are updating the myData every time you are getting response, You can push the unique objects into a separate array and then return that array using a computed property.
Live Demo :
new Vue({
el: '#app',
data: {
combinedData: [],
myData: []
},
mounted() {
setTimeout(() => {
console.log('First data');
this.myData = [{ id: 3 }, { id: 2 }, { id: 1 }];
this.pushData(this.myData)
setTimeout(() => {
console.log('second data');
this.myData = [{ id: 4 }, { id: 4 }];
this.pushData(this.myData)
setTimeout(() => {
console.log('Third data');
this.myData = [];
this.pushData(this.myData)
}, 3000);
}, 3000);
}, 2000);
},
methods: {
pushData(data) {
data.forEach(obj => {
if (!JSON.stringify(this.combinedData).includes(JSON.stringify(obj))) {
this.combinedData.push(obj)
}
});
}
},
computed: {
finalData() {
return this.combinedData
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="item in finalData">
{{ item.id }}
</div>
</div>
in terms of performance this should be the output in the console.log
In terms of performance, you should use as few reactive data as possible, especially if your object has many properties. I would modify auxData directly.
this.addingData([{ id: 3 }, { id: 2 }, { id: 1 }]);
Simplified addingData
addingData(getDataFetch) {
// It's faster to get the id-s first
let itemDict = new Set(this.auxData.map((m) => m.id));
getDataFetch.forEach((item) => {
if (!itemDict.has(item.id)) {
this.auxData.unshift(item);
itemDict.add(item.id);
}
});
},
And iterate over it
<div v-for="item in auxData">
{{ item.id }}
</div>
Also watching object list can also cause performance issues. It should be used on primitive values.
Example on StackBlitz
Looks like you should be using v-for with auxData as that's what you're updating using the result of your API call (myData). As your API sends you new results, use a watcher to run a function whenever a new update is made to then also update auxData
updated stackblitz
watch: {
myData(newData, oldData) {
console.log('Entering AddingData', newData);
if (newData.length !== 0) {
if (this.auxData.length === 0) {
this.auxData = newData;
} else {
newData.forEach((item) => {
const isNewItem = this.auxData.find((itemAux) => {
return item.id === itemAux.id;
});
if (!isNewItem) {
this.auxData.unshift(item);
}
});
}
}
},
},
<div v-for="item in auxData">
{{ item.id }}
</div>

Vue.js. Data property undefined

I'm trying to access my data property in my Vue.js component. Looks like I'm missing something obvious.
Here is a short version of my code. StoreFilter.vue is a wrapper for matfish2/vue-tables-2.
<template>
<store-filter :selected.sync="storeIds"></store-filter>
</template>
<script>
import StoreFilter from './Filters/StoreFilter';
export default {
components: {
StoreFilter
},
data() {
return {
options : {
requestFunction(data) {
console.log(this.storeIds); //undefined
return axios.get('/api/orders', {
params: data
}).catch(function (e) {
this.dispatch('error', e);
}.bind(this));
},
},
storeIds: [],
}
},
watch : {
storeIds(storeIds) {
this.refreshTable();
}
},
methods : {
refreshTable() {
this.$refs.table.refresh();
}
}
}
</script>
How to get storeIds from requestFunction?
Use a closure, see rewrite below.
data() {
let dataHolder = {};
dataHolder.storeIds = [];
dataHolder.options = {
requestFunction(data) {
// closure
console.log(dataHolder.storeIds); // not undefined
return axios.get('/api/orders', {
params: data
}).catch(function (e) {
this.dispatch('error', e);
}.bind(this));
}
}
return dataHolder;
}
I recommend using the created() way to handle this.
export default {
// whatever you got here
data () {
return {
options: {}
}
},
created () {
axios.get('/api/orders', { some: params }).then(response => this.options = response.data)
}
}

vue 2 filter multiple array

Excuse me, my json data have two array.
I want to filter this json data(banner_img:['img']) with Vue.js.
But Analysis Json data is some problems..
Json data
[{"id":10,"banner":"AIR","banner_img":[{"id":1,"img":"air_1.png","banner_id":10},{"id":2,"img":"air_2.png","banner_id":10}]},
{"id":11,"banner":"HOT","banner_img":[{"id":3,"img":"hot_1.png","banner_id":11},{"id":4,"img":"hot_2.png","banner_id":11}]},
{"id":12,"banner":"NEW","banner_img":[{"id":5,"img":"new_1.png","banner_id":12},{"id":6,"img":"new_2.png","banner_id":12}]}]
Vue.js
var app = new Vue({
el: '#app',
data: {
banner:[],
search:'',
},
methods: {
getBannerData: function() {
axios.get('/case/ajax/33').then(response => {
this.banner = response.data.banner;
});
},
},
mounted: function() {
this.getBannerData();
},
computed: {
filteredList() {
return this.banner(value => {
return value.banner_img.filter(bannerImg => {
return bannerImg.img.toLowerCase().includes(this.search.toLowerCase());
});
})
}
}
});
HTML
<input type="text" name="ImgFilter" v-model="search">
<div v-for="value in filteredList">
<img v-for="imgs in value.banner_img" :src="imgs.img" height="100">
</div>
Then I try this filteredList
return value.banner_img.filter(bannerImg => {
return bannerImg.img.toLowerCase().includes(this.search.toLowerCase());
});
but is not work..
Please give me some advices~!
Try this one:
filterList:function()(
var that = this;
return this.banner.filter(function(item) {
return item.banner_img && item.banner_img.some(function(img) {
return img.img && img.img.toLowerCase() === that.search.toLowerCase();
});
});
)

Why index-of not working correctly in vuejs?

I make a custom component in Vue.js .In My component, I have a list which has a delete button.On click of a button, it deletes the row.If I click any row it deletes the last row because the index is always -1 why?
here is my code
https://plnkr.co/edit/hVQKk3Wl9DF3aNx0hs88?p=preview
methods: {
deleteTodo:function (item) {
console.log(item)
var index = this.items.indexOf(item);
this.items.splice(index, 1);
}
}
below Whole code
var MyComponent = Vue.extend({
template:'#todo-template',
props:['items'],
computed: {
upperCase: function () {
return this.items.map(function (item) {
return {name: item.name.toUpperCase(),complete:item.complete};
})
}
},
methods: {
deleteTodo:function (item) {
console.log(item)
var index = this.items.indexOf(item);
this.items.splice(index, 1);
}
}
})
Vue.component('my-component', MyComponent)
var app = new Vue({
el: '#App',
data: {
message: '',
items: [{
name: "test1",
complete:true
}, {
name: "test2",
complete:true
}, {
name: "test3",
complete:true
}]
},
methods: {
addTodo: function () {
this.items.push({
name:this.message,
complete:true
});
this.message ='';
},
},
computed: {
totalCount:function () {
return this.items.length;
}
}
});
Instead of passing the whole object you should pass the index of the item.
Change the for loop to
<li v-for="(item, index) in upperCase" v-bind:class="{'completed': item.complete}">
{{item.name}}
<button #click="deleteTodo(index)">X</button>
<button #click="deleteTodo(index)">Toggle</button>
</li>
and the delete function to
deleteTodo:function (itemIndex) {
this.items.splice(itemIndex, 1);
}
Updated Code: Link
Your code is assuming that indexOf will return a valid index
deleteTodo:function (item) {
console.log(item)
var index = this.items.indexOf(item);
this.items.splice(index, 1);
}
If it's returning -1, it means that it's not finding the item in the list. Quite likely this.items is not what you think it is.
A bit of defensive code will help you solve this:
deleteTodo:function (item) {
console.log(item)
var index = this.items.indexOf(item);
if (index === -1)
console.error("Could not find item "+item in list: ",this.items);
else
this.items.splice(index, 1);
}
This will show you what this.items is in your console output

How to structure vue.js app so I can access ajax request data from computed property on page load

I have the following code (below) that lets a user search data in an array. I want to replace the data property with data from an api and I don't know the proper way to structure a vue.js app so the methods have access to ajax data that is called on page load.
I know I use the axios library to call the data.
Vue.axios.get('https://jsonplaceholder.typicode.com/posts/1').then((response) => {
console.log(response.data)
})
MY CODE
https://jsfiddle.net/bny191f7/1/
Vue.js code
new Vue({
el: '#app',
data: {
searchString: "",
users: [{ //____________I want to replace this data with api data
"name": "Bob"
},
{
"name": "Angel"
},
{
"name": "Whatever"
}
]
},
computed: {
filterUsers: function() { //___________And insure this has access to it
//___________so the app continues to work
var users_array = this.users,
searchString = this.searchString;
if (!searchString) {
return users_array;
}
searchString = searchString.trim().toLowerCase();
users_array = users_array.filter(function(item) {
if (item.name.toLowerCase().indexOf(searchString) !== -1) {
return item;
}
})
return users_array;;
}
}
});
HTML
<form id="app" v-cloak>
<input type="text" v-model="searchString" placeholder="Enter your search terms" />
<ul>
<li v-for="user in filterUsers">
<p>{{user.name}}</p>
</li>
</ul>
I figured it out.
VUE
new Vue({
el: '#app',
data: {
searchString: "",
users: undefined
},
mounted: function () {
Vue.axios.get('https://jsonplaceholder.typicode.com/posts')
.then(response => {
console.log(response);
this.users = response.data;
console.log(this.users);
})
.catch(function (error) {
console.log(error);
});
},
computed: {
filterUsers: function () {
var users_array = this.users,
searchString = this.searchString;
if(!searchString){
return users_array;
}
searchString = searchString.trim().toLowerCase();
users_array = users_array.filter(function(item){
if(item.title.toLowerCase().indexOf(searchString) !== -1){
return item;
}
})
return users_array;;
}
}
});
HTML
<form id="app" v-cloak>
<input type="text" v-model="searchString" placeholder="Enter your search terms" />
<ul>
<li v-for="user in filterUsers">
<p>{{user.title}}</p>
</li>
</ul>
</form>

Categories