Remove datatables search field without disabling searchable - datatables

I made an datatable in Angular5 using angular-datatables. I then created the table footer and implemented input fields to search each column individually, and added style="display: table-header-group;" to the footer so it goes above the header.
Now I want to remove the standard datatables search input field.
If i just disable it in datatable configuration, then the custom search fields won't work.
I also tried to change value of class dataTables_filter to display: none, but it won't work, since it get's it's styling from node_modules folder.
I could change my node_modules/datatables/style.css but the problem is, that's the only folder that is on the gitignore list, and doesn't go to our repository.
Here is my code:
HTML:
<tfoot style="display: table-header-group;">
<tr>
<th style="width: 25%">
</th>
<th style="width: 25%">
</th>
<th style="width: 25%">
<input type="text" placeholder="Search actions" name="search-actions" class="form-control" />
</th>
<th style="width: 25%">
<input type="text" placeholder="Search messages" name="search-messages" class="form-control" />
</th>
</tr>
</tfoot>
TypeScript:
ngOnInit(): void {
this.dtOptions = {
ajax: 'http://www.mocky.io/v2/5b0eb2083200006300c19bd8',
columns: [{
title: 'Date',
data: 'date'
}, {
title: 'Time',
data: 'time'
}, {
title: 'Action',
data: 'action'
}, {
title: 'Message',
data: 'message'
}]
};
}
ngAfterViewInit(): void {
this.datatableElement.dtInstance.then((dtInstance: DataTables.Api) => {
dtInstance.columns().every(function () {
const that = this;
$('input', this.footer()).on('keyup change', function () {
if (that.search() !== this['value']) {
that
.search(this['value'])
.draw();
}
});
});
});
}

Use dom option and exclude f from default value (lfrtip for default style) to hide filtering control.
For example:
this.dtOptions = {
// ... skipped ...
dom: 'lrtip'
}

Related

Jest: Quasar table is not rendering rows

I'm using Jest for testing a quasar table in VueJs 3. The table I have is like this:
<template>
<q-table id="myTable"
:rows="rows"
:columns="columns"
virtual-scroll
:rows-per-page-options="[0]"
hide-bottom
/>
</template>
<script lang="ts">
import {defineComponent, PropType} from 'vue'
export default defineComponent({
name: 'TableDemo',
props: {
rows: {
type: [] as PropType<[{ name: string, surname: string }]>,
default: []
},
},
setup() {
const columns = [
{
name: 'name',
required: true,
align: 'left',
label: 'name',
field: 'name',
sortable: true
},
{
name: 'surname',
label: 'surname',
align: 'left',
field: 'surname',
sortable: true
},
]
return {columns}
}
})
</script>
I'm trying to write a very simple test to understand how this works:
it('Should contain the word "John"', async () => {
)
const wrapper = mount(TableDemo, {
props: {rows: [{name: 'John', surname: 'Marston'}]},
}
)
expect(wrapper.find('#myTable').text()).toContain('John')
})
My problem is that it doesn't render the rows, but only the columns.
Here what Jest renders:
<div class="q-table__container q-table--horizontal-separator column no-wrap q-table__card q-table--no-wrap"
id="myTable"><!---->
<div class="q-table__middle q-virtual-scroll q-virtual-scroll--vertical scroll">
<table class="q-table">
<thead>
<tr>
<th class="text-left sortable">name<i aria-hidden="true"
class="notranslate material-icons q-icon q-table__sort-icon q-table__sort-icon--left"
role="presentation">arrow_upward</i></th>
<th class="text-left sortable">surname<i aria-hidden="true"
class="notranslate material-icons q-icon q-table__sort-icon q-table__sort-icon--left"
role="presentation">arrow_upward</i></th>
</tr>
</thead>
<tbody class="q-virtual-scroll__padding">
<tr>
<td colspan="2" style="height: 0px; --q-virtual-scroll-item-height: 48px;"/>
</tr>
</tbody>
<tbody class="q-virtual-scroll__content" id="qvs_1" tabindex="-1"/>
<tbody class="q-virtual-scroll__padding">
<tr>
<td colspan="2" style="height: 48px; --q-virtual-scroll-item-height: 48px;"/>
</tr>
</tbody>
</table>
</div><!----></div>
Where is my mistake? Why doesn't the table render the row?
UPDATE
I actually realized that it works without "virtual-scroll" option, so the problem is bound to that. Does anyone have experience with it?
It seems they have used some timer function inside virtual scroll implementation, this is my fix:
call jest.useFakeTimers(); method before describe block, then inside test call jest.runAllTimers(); and await wrapper.vm.$nextTick(); before assertion
it('renders correctly', async () => {
const wrapper: any = wrapperFactory();
jest.runAllTimers();
await wrapper.vm.$nextTick();
expect(wrapper.html()).toMatchSnapshot();
});

Vuejs2- How to call a filter function from a method

I am using "moneyFormat" filter for formatting the currency value. It's formatting the values which is defined already. I want to format the dynamic values. Hence I have called the filter function through a method called "displayValue", but I am getting error
and the given input field also not updated.
Here is my code :
<template>
<b-card>
<div class="panel-body" id="app">
<table class="table table-hover">
<thead>
<tr>
<th style="width: 20px;">No.</th>
<th style="width: 330px;">Description</th>
<th style="width: 130px;" class="text-right">Charges</th>
<th style="width: 130px;">Total</th>
<th style="width: 130px;"></th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows" :key="row.qty">
<td>
{{ index +1 }}
</td>
<td>
<select name="" id="" class="form-control" v-model="row.billChgDesc">
<option v-for="option in billChgDescOpt" v-bind:value="option.value"
:key="option.value"> {{ option.text }}
</option>
</select>
</td>
<td>
<input #input="displayValue" class="form-control text-right" type="text" v-model="row.charges" data-type="currency" v-validate="'required'" :name="'charges' + index">
<span v-show="vErrors.has('charges' + index)" class="is-danger">{{ vErrors.first('charges' + index) }}</span>
<td>
<input class="form-control text-right" :value="row.qty * row.charges | moneyFormat" number readonly />
<input type="hidden" :value="row.qty * row.charges * row.tax / 100" number/>
</td>
<td>
<button class="btn btn-primary btn-sm" #click="addRow(index)"><i class="fa fa-plus"></i></button>
<button class="btn btn-danger btn-sm" #click="removeRow(index)"><i class="fa fa-minus"></i></button>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3" class="text-right">DELIVERY</td>
<td colspan="1" class="text-right"><input class="form-control text-right" v-model="delivery" number/></td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</b-card>
</template>
<script>
import Vue from 'vue'
import accounting from 'accounting'
export default {
filters:{
moneyFormat: function (val){
if (val > 0) {
return accounting.formatMoney(val, " ₹ ", 2, ",", ".");
}
}
},
data: function () {
return {
billChgDescOpt: [
{ value: '', text: 'Select' },
{ value: 'M', text: 'Maintenance Fee'},
{ value: 'W', text: 'Water Charges'},
{ value: 'P', text: 'Penalty Fee'},
],
rows: [
{qty: 5, billChgDesc: '', charges: 55.20, tax: 10},
{qty: 19, billChgDesc: '', charges: 1255.20, tax: 20},
],
grandtotal: 0,
delivery: 40
}
},
computed: {
total: function () {
var t = 0;
$.each(this.rows, function (i, e) {
t += accounting.unformat(e.total, ",");
});
return t;
},
taxtotal: function () {
var tt = 0;
$.each(this.rows, function (i, e) {
tt += accounting.unformat(e.tax_amount, ",");
});
return tt;
}
},
methods: {
addRow: function (index) {
try {
this.rows.splice(index + 1, 0, {});
} catch(e)
{
console.log(e);
}
},
removeRow: function (index) {
this.rows.splice(index, 1);
},
displayValue:function (e) {
var value = e.target.value
var a = this.filters.moneyFormat(value);
return a;
}
}
}
</script>
<style lang="scss" scoped>
.is-danger{
color: RED;
}
</style>
You could use:
this.$options.filters.moneyFormat(value)
Check: https://v2.vuejs.org/v2/api/#vm-options
For global filters, first set:
Vue.prototype.$filters = Vue.options.filters
And then:
this.$filters.foo
Edit:
Looking closer your code, you are not using the filter as a Vue filter and only calling from one point (a method) instead of calling inline from HTML, maybe it's better that the method itself returns the value of the input, like:
displayValue: function (e) {
var val = e.target.value
if (val > 0) {
return accounting.formatMoney(val, " ₹ ", 2, ",", ".");
}
}
Did it work? Or the same error is shown? If yes, can you paste the error?
Hope it helps!
As it's been said, if you want to use the filter, you need to do this.$options.filters.moneyFormat(value)
What you're trying to achieve it's rendered the moneyFormat inside an input and the value displayed is the v-model. It's this one you have to format.
So you can initialize a new data property filled with each row.charges formatted on mounted:
data: function () {
return {
rows: [
//...
],
currentCharges: []
}
},
mounted() {
this.rows.forEach(row => {
let formattedCharges = this.$options.filters.moneyFormat(row.charges)
this.currentCharges.push(formattedCharges)
})
},
and use this data to fulfill your inputs:
<tr v-for="(row, index) in rows">
<td>
<input v-model="currentCharges[index]" #input="displayValue($event, index)">
<td>
To keep the current row.charges updated, reassign it when the v-model updates:
methods: {
displayValue:function (e, index) {
// the target value is a string like this " ₹ 55.20"
// split it and convert the last element to Float type
let arrValue = e.target.value.split(" ")
let parseValue = parseFloat(arrValue[arrValue.length -1])
// reassign the row.charges with the new float value
this.rows[index].charges = parseValue
}
},

How to reload dataTables from VueJS after adding or editing?

I'd like to know how I can use this same approach when loading data from database through an api. In the first time the dataTables loads fine. However, when I add a new record, then I need to load my dataTables again with the new record. Here's my html code:
<table class="table table-striped table-bordered table-hover" id="dataTables-eventos">
<thead>
<tr class="tbheader">
<th class="text-center">#</th>
<th>Nome do Evento</th>
<th class="text-center">Editar</th>
<th class="text-center">Deletar</th>
</tr>
</thead>
<tbody>
<tr v-for="ev in eventos" :key="ev.id" track-by="id">
<td class="text-center">{{ ev.id }}</td>
<td>{{ ev.name }}</td>
<td class="text-center"><a class="cursorpointer" v-on:click="showMessage()"><span class="glyphicon glyphicon-edit"></span></a></td>
<td class="text-center"><span class="glyphicon glyphicon-trash"></span></td>
</tr>
</tbody>
</table>
And here's my VueJS code:
mounted() {
this.getEventos();
},
methods: {
getEventos() {
axios.get('/api/eventos')
.then((response) => {
this.eventos = response.data})
.then((response) => {
$('#dataTables-eventos').DataTable({
responsive: true,
"aaSorting": [[ 1, "asc" ]],
"aoColumnDefs": [
{ "bSortable" : false, "aTargets": [0,2,3] },
{ "searchable": false, "aTargets": [2,3] }
],
language: {
url: '/js/dataTables/localization/pt_BR.json'
}
});
});
},
addNewRecord() {
axios.post('/api/eventos', { nomeEvento: this.nomeEvento });
}
So after adding (or editing) a new record on my DB how can I reload my dataTables so I can see the changes?
I just ran into a situation where I needed to use vue.js with datatables.net and had to use customized table html. I created a component that allowed me to format the table as I needed using template/v-for and refresh the datatable while preserving datatables.net functionality. I hope this helps someone down the road...
export default {
data() {
return {
dataTable: null
}
},
props: {
data: Array
},
watch: {
data() {
if (this.dataTable) {
this.dataTable.destroy();
}
this.$nextTick(() => {
this.dataTable = $("#table").DataTable({
language: {
emptyTable: "No Results Found"
}
});
});
}
}
}
You can do it without reloading all the data.
Just add this.nomeEvento to array eventos after posting to server.

Vue.js binds inline style expressions when rendering an Array

I have the following components, I think if the width of the column when the value of a circle in the rendering of a style =" width: 200px ", if the column width no value is not rendering, how do I do?
<template>
<div class="table-scrollable">
<table class="table table-bordered table-hover">
<thead>
<tr v-if="tableOption.columns.length">
<th v-for="(column, index) in tableOption.columns">
{{ column.name }}
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</template>
<script>
export default {
data() {
return {
columns: [{
name: app.localize('Actions'),
width: 200
}, {
name: app.localize('TenancyCodeName'),
field: 'tenancyName'
}, {
name: app.localize('Name'),
field: 'name'
}, {
name: app.localize('Edition'),
field: 'editionDisplayName'
}, {
name: app.localize('Active'),
field: 'isActive',
width: 100
}, {
name: app.localize('CreationTime'),
field: 'creationTime',
}]
}
}
}
</script>
Seeking a solution,thanks!
If you're sure to always have the width, yes, your solution is good enough.
Otherwsise, you can use a method:
methods: {
// a more explicit name would be better
style (width) {
return {
width: width || 200 + 'px'
}
}
}
Then, in the HTML, you could do
<th :style="style(column.width)">...</th>
Or use a computed property that makes sure the style attribute is present
Imo the easiest way would be if you would define styles in object, like this:
{
name: app.localize('Actions'),
style: {
width: '200px', // px suffix required
}
}
so then you can simply do:
<th v-for="(column, index) in columns" :style="column.style">
Is there a better solution?

How can we change width of search fields in DataTables

Can I change the width of search text fields in dataTables ?
I am writing following code right now but it is not working.
$('#example').dataTable()
.columnFilter({ sPlaceHolder: "head:before",
aoColumns: [ { type: "text",width:"10px" },
{ type: "date-range" },
{ type: "date-range" }
]
});
And if my dataTables is dynamically generated like as gven below:
$('#example').dataTable({
"aaData": aDataSet,
"aoColumns": [
{ "sTitle": "#","sWidth": "10px" },
{ "sTitle": "ID" },
{ "sTitle": "Staff Name" },
{ "sTitle": "Rig Days" },
{ "sTitle": "Manager"},
{ "sTitle": "Grade"},
{ "sTitle": "Tools"},
{ "sTitle": "Vacations"},
{ "sTitle": "Presently On"},
]
});
}
How to add Search field in this dynamically created DataTable to search by each column?
None of the above solution worked for me; then I got this:
$(document).ready(function () {
$('.dataTables_filter input[type="search"]').css(
{'width':'350px','display':'inline-block'}
);
});
And it worked perfectly!
If you want to place a placeholder too inside the search box use this ..
$('.dataTables_filter input[type="search"]').
attr('placeholder','Search in this blog ....').
css({'width':'350px','display':'inline-block'});
And this will work for sure.
To change the search box width, all I had to do was go into my .css file and enter the following:
.dataTables_filter input { width: 300px }
it's worked for me
<script>
var datatable = $('#table').DataTable({
...<datatables properties>,
initComplete: function () {
$('.dataTables_filter input[type="search"]').css({ 'width': '350px', 'display': 'inline-block' });
}
</script>
The only thing worked for me is this css.
$(document).ready(function(){
$('#datatable-buttons_filter').css({"position":"relative","left":"-100px"});
});
try to use css to change the width
example
.text_filter{
width:45%;
min-width:200px;
}
I applied this solution in my environment (Laravel 5.2, yajra/Laravel-DataTables 6.0, Bootstrap 3.x):
My table:
<table id="my-table" class="table table-striped table-hover" style="font-size: 80%">
<thead>
<tr>
<th class="input-filter" style="text-align:center;width: 5%">ID</th>
...
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr>
<th style="text-align:center;width: 5%">ID</th>
...
</tr>
</tfoot>
My script:
<script type="text/javascript">
var dt = $('#my-table').DataTable({
stateSave: true,
responsive: true,
processing: true,
serverSide: true,
order: [[ 0, "desc" ]],
lengthMenu: [[5, 10, -1], [5, 10, "All"]],
ajax: {
url: '...',
},
columns: [
{data: 'id', name: 'id',orderable:true,searchable:true},
...
],
language: {
url: '....'
},
initComplete: function () {
this.api().columns('.input-filter').every(function () {
var column = this;
var input = document.createElement("input");
// start - this is the code inserted by me
$(input).attr( 'style', 'text-align: center;width: 100%');
// end - this is the code inserted by me
$(input).appendTo($(column.footer()).empty())
.on('keyup', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? val : '', true, true).draw();
});
});
}
});
</script>
here is the repeater
<asp:Repeater ID="rptClients" runat="server">
<HeaderTemplate>
<table id="example" class="display">
<thead>
<tr style="color:#fff;">
<th>Edit</th>
<th>Client Name</th>
<th>Address</th>
<th>City</th>
<th>State</th>
<th>Zip</th>
<th>Phone</th>
<th>Fax</th>
<th>Client Type</th>
<th>Active</th>
</tr>
<tr id="filterRow">
<td>Edit</td>
<td>Client Name</td>
<td>Address</td>
<td>City</td>
<td>State</td>
<td>Zip</td>
<td>Phone</td>
<td>Fax</td>
<td>Client Type</td>
<td>Active</td>
</tr>
</thead>
<tfoot style="display:none;">
<tr>
<td> </td>
</tr>
</tfoot>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td><div class="padBtm-05 padTop-10"><asp:Button runat="server" ID="btnEdit" Text="Edit" /></div></td>
<td>
<%# Container.DataItem("ClientName")%>
</td>
<td>
<%# Container.DataItem("ClientAddress")%>
</td>
<td>
<%# Container.DataItem("ClientCity")%>
</td>
<td>
<%# Container.DataItem("State")%>
</td>
<td>
<%# Container.DataItem("ClientZip")%>
</td>
<td>
<%# Container.DataItem("ClientPhone")%>
</td>
<td>
<%# Container.DataItem("ClientFax")%>
</td>
<td>
<%# Container.DataItem("ClientType")%>
</td>
<td>
<%# Container.DataItem("Active")%>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
There are filters on all columns. Here I actually hide the edit column filter since it doesn't need one. If i don't render a filter for this column my filtering is off 1 column. In the javascript i target my table row which will end up being my title placeholder. Based on the placeholder name you can run an if statement that allows you to target the input element and set the width of the item. I have found setting the width of the filter sets the column width on the table.
// apply the input styling
$('#example thead td').each(function (i) {
var title = $('#example thead th').eq($(this).index()).text();
if (title == "Edit") {
var serach = '<input type="text" style="display:none;" placeholder="' + title + '" />';
} else if (title == "Client Name") {
var serach = '<input type="text" style="width:370px;" placeholder="' + title + '" />';
} else if (title == "Zip") {
var serach = '<input type="text" style="width:50px;" placeholder="' + title + '" />';
} else {
var serach = '<input type="text" placeholder="' + title + '" />';
}
$(this).html('');
$(serach).appendTo(this).keyup(function () { table.fnFilter($(this).val(), i) })
});
// Apply the search
table.columns().every(function () {
var that = this;
$('input', this.footer()).on('keyup change', function () {
if (that.search() !== this.value) {
that
.search(this.value)
.draw();
}
});
});
I have used the below code while using dataTables
JS
$('#du_ft_table').dataTable( { <br/>
"bProcessing": true,<br/>
"bServerSide": true,<br/>
scrollX: true,<br/>
"sAjaxSource": "../server_processing_man.php?sourceC=du_ft&rights=1&mandatory=1&retailer_id=2725",
"aoColumns": [
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
{ "mData": function(obj) {
return '<a class="btn btn-xs btn-primary" href="EBSNL_NW='+obj[0]+'" alt="Edit" title="Edit"><i class="fa fa-pencil-square-o"></i></a>';
}}
]
} );
$('.dataTables_scrollFootInner tfoot th').each( function () {<br/>
var title = $(this).text();<br/>
if(title != '') $(this).html( '<input type="text" placeholder="Search '+title+'" />' );<br/>
});
var duft_table = $('#du_ft_table').DataTable();
// Apply the search
duft_table.columns().every( function () {<br/>
var that = this;<br/>
$( 'input', this.footer() ).on( 'keyup change', function () {<br/>
if ( that.search() !== this.value ) {<br/>
that.search(this.value).draw();<br/>
}<br/>
} );
});
Nativ javascript solution.
$(document).ready(function() {
$('#yourTableId').DataTable();
var addressTableFilter = document.getElementById('addressTable_filter');
addressTableFilter.firstChild.getElementsByTagName('input')[0].style.width = "400px";
addressTableFilter.firstChild.getElementsByTagName('input')[0].setAttribute('placeholder', 'Search');
addressTableFilter.firstChild.removeChild(document.getElementById('addressTable_filter').firstChild.firstChild);
});
Dont forget to wrap everything inside document ready(if u using it)otherwise other lines may kick in before datatable is initiated and you will get error.
This will also remove search label and add it as placeholder inside input box.