search bar filter using Vue js - vue.js

I am trying to filter a table that get data from api and I tried this solution but it doesnt work.
I couldnt find where the problem is and if I pass the search input event listener
and here is my table component :
<template>
<table class="mt-12 border-2 border-gray-600">
<thead>
<tr>
<th v-for="header in data.headers" :key="header" class="text-left border-l-2 border-gray-600 border-b-2 border-gray-600 bg-red-400 ">{{ header }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(rows, index) in data.rows" :key="index">
<td v-for="row in rows" :key="row" class="border-l-2 border-gray-600" >{{ row }}</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
props: {
data: Object,
default: {}
}
}
</script>
<style src="../assets/tailwind.css"/>
My question:
If anyone can help me to define my problem and solve this ?
Thanks for help.

You can use your data as computed where you pass it as a prop.
<BaseTable :data='data' />
Here instead of using like this create a computed which can be filteredData.
<BaseTable :data='filteredData' />
and in your props you can simply filter it or just send the data as it is.
computed: {
filteredData() {
if(this.search) {
// filter your data as you want and return
}
else // return your main data
}
}
Here is a working simple example:
https://codesandbox.io/s/filterlist-example-vdwhg?file=/src/App.vue
And change your include to includes.

what error you are getting ? I think you are using this.search inside filteredRows function which is not a vue instance property. it should be this.data.search. The search is used with V-model as well so you should declare it outside data (JSON object).

Related

Vue2: Can I pass an optional (global) filter into a reusable component?

I am quite new to Vue.
I am working on a table as component, which is supposed to be a lot of times. So far, so good, but now I want to use a filter, which can be optional passed into it.
That is how I "call" the table:
<table
:headers="headers"
:items="some.data"
></table>
data () {
return {
headers: [
{ title: 'date', value: ['date'], filter: 'truncate(0, 10, '...')' },
]
}
}
Here is my table component
<template>
<div>
<table class="table">
<thead>
<tr>
<!-- <th v-for="header in headers" :key="header.id" scope="col">{{ header.title }}</th> -->
<th v-for="header in headers" :key="header.id" scope="col">
{{ header.title }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="item in items" :key="item.id" scope="col">
<td v-for="header in headers" :key="header.id" scope="row">
<!-- {{item}} -->
<span v-for="val in header.value" :key="val.id">
{{item[val] | truncate(0, 10, '...') }}
</span>
<!-- {{header.filter}} -->
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script >
export default {
name: 'Table',
props: {
headers: Array,
items: Array
}
}
</script>
My global filter:
Vue.filter('truncate', function (text, start, truncLength, clamp = '') {
return text.slice(start, truncLength) + clamp
// return text.slice(0, stop) + (stop < text.length ? clamp || ' ...' : '')
})
I was hopping, to add by that an optional filter (via v-if I would chech for it). So far I can render the filter as string ... but not execute it.
Even if I put the filter in the span, it does not work (it says then, "text.slice is not a function" in the console.
I was not successful with googling it, because with filters/filter it is mostly about how to use .filter(...) on data as JS method, but not as Vue filter.
Any advise?
A filter is a function that runs inside JSX template in html. Example of how to create custom Vue.js filter
Vue.filter('ssn', function (ssn) { // ssn filter
return ssn.replace(/(\d{3})(\d{2})(\d{4})/, '$1-$2-$3');
});
Using it
{{stringToTrans | ssn}}
To use a filter outside this you can use a computed property or standard function like so
convertedDateString() { // computed
return this.$moment(this.dateString).format('MM/DD/YYYY')
}
convertedDateString(dateString) { // method
return this.$moment(dateString).format('MM/DD/YYYY')
}

inject render method into template

Must the render method of a Vue component be used exclusively or can it be combined with a template? Most of my component can be rendered using a template but just need a small part of it to be rendered using code. If this is possible how can I combine the render method output with the template?
Example in component:
<template>
<table>
<tr>
// use render method here
</tr>
<tr v-for="row in rows">
// use render method here
</tr>
</table>
</template>
Need render method in spots above to loop through the array $scopedSlots.column and render each <th> and <td> based on multiple <templates v-slot:column={row}> provided by parent.
As far as I know you can either use the render function or a template - but not both. They can not be combined.
What you could do to make your example work is to use the v-html-directive, which updates the innerHTML of an element https://012.vuejs.org/api/directives.html#v-html.
new Vue({
el: '#el',
data: {
rows: ['row1', 'row2', 'row3']
},
methods: {
renderRow(row) {
return `<td>${row}</td>`;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="el">
<table>
<tr v-for="row in rows" v-html="renderRow(row)">
</tr>
</table>
</div>
I'm having this same challenge as well. Which is how I came to your question.
As others have said, it seems you can either use a template or render function but not both.
My suggestion would be to have a component handle only the render function, while the parent component can use the template method
So, Per your example
<template>
<table>
<tr>
<component-using-only-render-method />
</tr>
<tr v-for="row in rows">
<component-using-only-render-method />
</tr>
</table>
</template>
I think its the best of both worlds with minimal compromise

dynamically add input as well as dropdown in vuejs

I m trying to add a dynamical input along with dropdown for that i m using vue-serach-select let me show my code then issue in detail
<template>
<button #click="addRow1"></button>
<table class="table table-striped">
<thead>
<tr style="font-size: 12px; text-align: center;">
<th class="span2" >Product Name</th>
<th class="span2" >Unit</th>
</tr>
</thead>
<tbody id="product_table_body">
<tr v-for="(row, index) in rows">
<td><model-list-select :list="productname"
option-value="product_name"
option-text="product_name"
v-model="row.selectproduct"
placeholder="Search Product Name"
#searchchange="searchprd3">
</model-list-select>
</td>
<td>
<input class="form-control-sm" type="text" v-model="row.new1" disabled="disabled" style="width: 100px;">
</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
data () {
return {
productname3:[],
rows:[],
}
},
methods:{
addRow1(){
this.rows.push({})
},
searchprd3(search,index){
},
}
}
</script>
now if i click on button one more row is added with dropdown now my problem is that how i can differentiate productname according to dynamic create row i had tried like :list="productname{index},:list="productname.index but none of them worked and last thing i want to pass index parameter in searchprd3 function so i tried like this searchprd3(search,index) in function I m getting index but it throw error for search and if only pass index then also throw error however it work none of parameter is passed from select like this #searchchange="searchprd3" but in function i m not getting index value or is there anyother way to achieve this thing
updated
<tr v-for="(row, index) in rows">
<td><model-list-select :list="row.productname"
option-value="product_name"
option-text="product_name"
v-model="row.selectproduct"
placeholder="Search Product Name"
#searchchange="searchprd3">
</model-list-select>
</td>
</tr>
<script>
searchprd3(searchText,index){
var _this=this
this.$http.get('/api/companyproducts?filter[where][product_name][ilike]=%'+searchText+'%').then(function (response) {
_this.rows.push({productname:response.data})
}).catch(function (error) {
console.log("error.response");
});
},
</script>
i want select option list seprate for every row but my code throw error like "Cannot read property 'map' of undefined"
that is why row push not working for dynamic row is I m missing Something
update2
found my mistake
addRow1(){
this.rows.push({
productnamex:[],
unit:'',
qty:'',
amt:'',
cgst:'',
sgst:'',
igst:''})
},
problem is that i haven't initialize my dropdown at rows click now another problem is that why my drop not get populated
try like this
<template>
<model-list-select :list="row.productnamex"
option-value="product_name"
option-text="product_name"
v-model="row.selectproduct"
placeholder="Search Product Name"
#searchchange="searchprd3($event,index,row)" >
</model-list-select>
</template>
<script>
addRow1(){
console.log(this.rows)
this.rows.push({ productnamex:[],unit:'', qty:'',amt:'', cgst:'', sgst:'', igst:''})
},
searchprd3(searchText,index,row){
console.log(index)
var _this=this
console.log(row)
this.$http.get('/api/companyproducts?filter[where][product_name][ilike]=%'+searchText+'%').then(function (response) {
console.log(response.data)
row.productnamex=response.data
}).catch(function (error) {
console.log("error.response");
});
},
</script>
hope this will help you as well as other enjoy :D
you should be able to do:
:list="productname+index"
You need to manually pass the index into the callback together with the searchtext in order for your searchprd3 method to receive both values.
This is where the inline $event variable in your event listener comes into the action. The $event variable contains the value that is supposedly passed to your callback by the event.
So if you want to pass additional data into the callback aside from the data that is passed by the event, you can manually call the callback function and pass the $event together with the data that you want to pass which in this case, the index variable.
Your event listener should look like this:
#searchchange="searchprd3($event, index)"

vuejs: Component for <tr>, how to implement?

After reading docs & forums for hours... still have no answer.
Have the following JS/HTML:
Vue.component("my-component", {
props: ["id"],
render: function(h) {
return h("tr", this.$slots.default);
}
});
var vapp = new Vue();
<script src="https://unpkg.com/vue#2.4.2/dist/vue.js"></script>
<table>
<tr is="my-component" :name="yo">
<td>
<span v-text="name"></span>
</td>
</tr>
</table>
Use tr + "is" attribute to specify component within the table otherwise browser will hoist it out as invalid content. Done
Use render + h("tr"...) because vuejs doesn't render tr element, but instead table > td and again browser hoist it out.Done
Now I have table > tr > td rendered well but how can I add children bound to the props/data, so I will see "yo" on the screen.
Thanks a lot!
If the elements in the slot need access to data inside the component, then you need to use a scoped slot.
Since you are using a render function, the scoped slot is available as a function through this.$scopedSlots.default() and you pass it an object with the data you want to be made available to the scoped slot.
You also need to define the scoped slot in the template. Here is an example.
Vue.component("my-component", {
props: ["id", "name"],
render: function(h) {
return h("tr", this.$scopedSlots.default({name: this.name}));
}
});
var vapp = new Vue({ el:"#app"});
<script src="https://unpkg.com/vue#2.4.2/dist/vue.js"></script>
<div id="app">
<table>
<tr is="my-component" :name="'yo'">
<template scope="{name}">
<td>
<span v-text="name"></span>
</td>
</template>
</tr>
</table>
</div>
If you are using .vue files you can have the table row component defined like this:
<template>
<tr>{{ name }}</tr>
</template>
<script>
export default {
name: 'table-row',
props: ['name'],
};
</script>
Then use it like this:
<table>
<tr is="TableRow" name="Some name here"></tr>
</table>
<table>
<thead>
<!-- ...some th -->
</thead>
<tbody>
<tr>
<td>..</rd>
</tr>
<template> <row-component my-param="blabla"></row-component> <!-- component return full row <tr>...</tr> -->
<some-other-recomponent my-par="blabla"></some-other-recomponent> <!-- component return full row <tr>...</tr> -->
</template>
</tbody>
</table>
If your're interested in returning multiple rows from your component, you can do like this.
In your main component.
<table width="90%">
<thead>
<tr>
<th width="1%">#</th>
<th>header description</th>
</tr>
</thead>
<template>
<your-child-component
:prop="prop-data"
v-for="(lancamento, index) in lancamentos"
:key="lancamento.id">
</your-child-component
</template>
</table>
And inside your child component
<template>
<tbody> <!-- dont forget this tag -->
<tr>
<td rowspan="2" v-html="prop.id"></td>
<td><td>
</tr>
<tr>
<td colspan="2" class="text-center"></td>
</tr>
</tbody>
</template>

Vue.js doesn't render table

My template is:
<tbody id="deliveries-table">
<tr v-for="item in deliveries">
<td class="table-view-item__col"></td>
<td class="table-view-item__col" v-bind:class="{ table-view-item__col--extra-status: item.exclamation }"></td>
<td class="table-view-item__col">{{item.number}}</td>
<td class="table-view-item__col">{{item.sender_full_name}}</td>
<td class="table-view-item__col" v-if="item.courier_profile_url">{{item.courier_full_name}}</td>
<td class="table-view-item__col" v-if="item.delivery_provider_url">{{item.delivery_provider_name}}</td>
<td class="table-view-item__col">
<span style="font-weight: 900">{{item.get_status_display}}</span><br>
<span>{{item.date_state_updated}}</span>
</td>
</tr>
</tbody>
My javascript code for render a lot of prepared data is:
var monitorActiveDeliveries = new ActiveDeliveries();
monitorActiveDeliveries.fillTable(allDeliveries);
class ActiveDeliveries {
constructor() {
this.table = new Vue({
el: '#deliveries-table',
data: {
deliveries: []
}
});
}
fillTable (d) {
this.table.deliveries = d;
}
}
But after script starts any render into tbody, i have just empty place in HTML.
Where i got some wrong?
First, although you can instantiate your Vue app on the <table> tag, usually you want just a single Vue instance on the whole page, so it might be better to make the Vue instance on one main div/body tag.
Second, I think your code could work (I don't know what your deliveries objects should look like...), but your fillTable() method is probably not getting called, i.e. deliveries are empty.
I made this working example based on your code: http://jsfiddle.net/wmh29mds/
Life is easier than it seems.
I got a mistake into this directive:
v-bind:class="{ table-view-item__col--extra-status: item.exclamation }"
I just forgot single quotas into class name, next variant is working:
v-bind:class="{ 'table-view-item__col--extra-status': item.exclamation }"