datatables sorting by selected checkboxes on top - datatables

I am trying to order checkboxes in a datatable at the top when they are clicked. This all works fine, but I also need to order them at the top when the page first loads. This I am having a struggle with. Any suggestions are appreciated!
$(document).ready(function() {
table = $('#schedule-list').DataTable( {
"dom": '<"toolbar">frtip',
"iDisplayLength": 500,
columnDefs: [
{ targets: 0, visible: false }
],
orderFixed: [0, 'desc'],
} );
$("#schedule-list_wrapper div.toolbar").html('<input class="btn btn-primary btn-sm md-btn-flat ml-3 checkall2" type="button" name="CheckAll2" id="checkall2" value="Select All" onClick="this.value=checkScheds(this.form)" style="float:left;">');
} );
$('#schedule-list').on('change', 'input[type="checkbox"]', function() {
var row = table.row($(this).closest('tr'));
table.cell({ row: row.index(), column: 0 } ).data( this.checked ? 1 : 0 )
row.invalidate().draw()
})
var checkflag2 = false;
function checkScheds() {
var inputs = document.getElementsByClassName( 'schedule' );
checkflag2 = checkflag2 === false ? true : false;
for (var i = 0; i < inputs.length; i++) {
inputs[i].checked = checkflag2;
}
return checkflag2 === true ? "Deselect All" : "Select All";
}
Here is some table data:
<table id="schedule-list" class="table table-striped" style="border-style: none; ">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<cfoutput>
<cfloop query="qryUserDetail.schedulelist">
<tr>
<td></td>
<td>
<label class="custom-control custom-checkbox">
<cfif structKeyExists(form, 'schedule') and #ArrayContains(form.schedule,#id#)# or (structKeyExists(url, 'schedule') and #url.schedule# eq #id#)>
<input type="checkbox" class="custom-control-input schedule" value="#id#" name="schedule" id="schedule" checked>
<cfelse>
<input type="checkbox" class="custom-control-input schedule" value="#id#" name="schedule" id="schedule">
</cfif>
<span class="custom-control-label">#schedname#</span>
</label>
</td>
</tr>
</cfloop>
</cfoutput>
</tbody>
</table>

Related

Reusing a form with Vue.js

I have a page with a list of things. I want the user to be able to click on an item in the list and open a modal dialog with a relatively complicated form with data for each item. I've implemented the form using Vue.js, and so far, it works, but I can't figure out how to get the Vue to switch from the data for one item to the data for another item, or do something equivalent to that.
The examples make it look like components are for small things and not entire forms. Here's the JavaScript from a JS fiddle with a hastily-coded example that's kind of similar to what I want to do.
HTML:
<table>
<thead>
<tr>
<th scope="col">Item</th>
<th scope="col">Data</th>
</tr>
</thead>
<tbody>
<tr data-identifier="1">
<td><a>Item 1</a></td>
<td class="json">{ "id": "1", "formData": { "whichOption": "1", "optionOneSetting": "ONE AYE", "optionTwoSetting": null, "optionThreeSetting": null } }</td>
</tr>
<tr data-identifier="2">
<td><a>Item 2</a></td>
<td class="json">{ "id": "2", "formData": { "whichOption": "2", "optionOneSetting": null, optionTwoSetting": "two-b", "optionThreeSetting": null } }</td>
</tr>
<tr data-identifier="3">
<td><a>Item 3</a></td>
<td class="json">{ "id": "3", "formData": { "whichOption": "2", "optionOneSetting": null, "optionTwoSetting": "two-c", "optionThreeSetting": null } }</td>
</tr>
<tr data-identifier="4">
<td><a>Item 4</a></td>
<td class="json">{ "id": "4", "formData": { "whichOption": "3", "optionOneSetting": null, "optionTwoSetting": null, "optionThreeSetting": "true" } }</td>
</tr>
<tr data-identifier="5">
<td><a>Item 5</a></td>
<td class="json">{ "id": "5", "formData": { "whichOption": "1", "optionOneSetting": "ONE AYE AGAIN", "optionThreeSetting": null } }</td>
</tr>
</tbody>
</table>
<div id="app" class="hidden">
<select v-model="formData.whichOption">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<label v-if="formData.whichOption==1">1-A: <input type="text" v-model="formData.optionOneSetting" /></label>
<div v-if="formData.whichOption==2">
<label><input type="radio" name="optionTwoSetting" value="two-a" v-model="formData.optionTwoSetting" /> 2-A</label>
<label><input type="radio" name="optionTwoSetting" value="two-b" v-model="formData.optionTwoSetting" /> 2-B</label>
<label><input type="radio" name="optionTwoSetting" value="two-c" v-model="formData.optionTwoSetting" /> 2-C</label>
</div>
<div v-if="formData.whichOption==3">
<label><input type="checkbox" name="optionThreeSetting" value="three" v-model="formData.optionThreeSetting" /> 3-A</label>
</div>
<button v-on:click="doMagic">Do a Thing</button>
</div>
JavaScript:
$(document).ready(function() {
$("a").click(function() {
var data = $(this).closest("tr").find("td.json").text();
data = JSON.parse(data);
if (!document.vue) {
document.vue = new Vue({
el: "#app"
, data: data
, methods: {
doMagic: function(event) {
var $tr = $("tr[data-identifier=" + this.id + "]");
console.log($.extend({}, this));
$tr.find(".json").text(JSON.stringify({
"id": this.id
, "formData": this.formData
}));
}
}
})
$("#app").removeClass("hidden");
}
else {
// This isn't expected to work, but it's effectively what I'd like to happen.
document.vue.data = data;
}
})
})
https://jsfiddle.net/don01001100/eywraw8t/370396/

Vuejs2- Avoid repetition of select field options using vee-validate

Iam using vee-validate plugin for validation. In my form, there is a select field in the table. Rows will be added dynamically in the table. I don't want to select the same select(Description column) option again and again Image. Hence I want to throw an error like "Selected description already exists in a table" this using vee-validate. Kindly help me to solve this.
Here is my code:
<template>
<div>
<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 class="form-control" v-model="row.billChgDesc" v-validate="'required|check'" :name="'billChgDesc' + index" data-vv-as="Description" #change="checkRepetation">
<option v-for="option in billChgDescOpt" v-bind:value="option.value"
:key="option.value"> {{ option.text }}
</option>
</select>
<span v-show=" errors.has('billChgDesc' + index)" class="is-danger">{{ errors.first('billChgDesc' + index) }}</span>
</td>
<td>
<input class="form-control text-right" type="text" v-model="row.charges" data-type="currency" v-validate="'required'" :name="'charges' + index" data-vv-as="Charges" >
<span v-show=" errors.has('charges' + index)" class="is-danger">{{ errors.first('charges' + index) }}</span>
<td>
<input class="form-control text-right" :value="row.qty * row.charges" 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">TOTAL</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>
</div>
</template>
<script>
import Vue from 'vue'
import accounting from 'accounting'
export default {
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,
selectArr:[]
}
},
methods: {
addRow: function (index) {
try {
this.rows.splice(index + 1, 0, {});
} catch(e)
{
console.log(e);
}
},
removeRow: function (index) {
this.rows.splice(index, 1);
},
checkRepetation:function(){
this.$validator.extend('check', {
getMessage: field => '* Slected ' + field + ' already exists',
validate: function(value){
selectArr.push(value);
}
})
}
}
}
</script>
<style lang="scss" scoped>
.is-danger{
color: RED;
}
</style>
Thanks in advance.
You're on the right track, but a couple changes need to be made. When you call this.$validator.extend, that only needs to be done once - when your component is created. It attaches the check method to the validator, so then every time you have the attribute v-validate="'required|check'" in your HTML, it will run that check method.
In your check validator, you need to answer the question "is this value already selected". The answer is to go through the this.rows and see if any of them have the same billChgDesc property. Because this is in Vue, by the time the validator gets run, the row in question already does have that value, so you want to check if MORE than one row have that value. So, something like this:
mounted() {
var self = this;
this.$validator.extend('check', {
getMessage: field => '* Selected ' + field + ' already exists',
validate: function(value){
return (self.rows.filter(function(v){
return v.billChgDesc == value;
}).length <= 1);
}
});
}
This validator returns true if only one item has the given value. I'm using the built-in filter method of Array (see docs).
You can see an example of this all working here: https://jsfiddle.net/ryleyb/f9q50wx4/1/

Bootstrap Select and dynamic rows issue in vue.js

I have the following dynamic table/rows in vue.js and I use bootstrap-select to have a nicer dropdown select. The form has a add/remove line to be dynamic. I cannot load the options of a select using bootstrap select. The select appears on each row but no dropdown list appears.
What am I doing wrong?
here goes my jsfiddle
HTML:
<div id="app">
<table class="table">
<thead>
<tr>
<td><strong>Date</strong></td>
<td><strong>Account</strong></td>
<td><strong>Debit</strong></td>
<td><strong>Credit</strong></td>
<td></td>
</tr>
</thead>
<tbody>
<tr v-for="row in rows">
<td>
<input type="date" v-date="row.myDate">
</td>
<td>
<select class="selectpicker" ref="select" v-model="row.select">
<option value="Acc1">Account1</option>
<option value="Acc2">Account2</option>
<option value="Acc3">Account3</option>
<option value="Acc4" selected>Account4</option>
</select>
</td>
<td>
<input type="text" v-model="row.debit" v-on:keypress="isNumber(event)">
</td>
<td>
<input type="text" v-model="row.credit" v-on:keypress="isNumber(event)">
</td>
<td><a #click="removeRow(row)">Remove</a></td>
</tr>
</tbody>
<tfooter>
<td class="al-g"> <button class="button btn-primary" #click="addRow">Add Line</button></td>
<td></td>
<td class="al-r">tot D.: {{ totaldebit | roundme }}</td>
<td class="al-r">tot Cr.:{{ totalcredit | roundme}}</td>
<td class="al-r">Dif: {{ totaldebit-totalcredit | roundme}}</td>
</tfooter>
</table>
</div>
JS:
Vue.filter('roundme', function (value) {
return value.toFixed(3);
})
var app = new Vue({
el: "#app",
data: {
rows: [{debit:0, credit:0},
]
},
computed: {
totaldebit() {
return this.rows.reduce((total, row) => {
return total + Number(row.debit);
}, 0);
},
totalcredit() {
return this.rows.reduce((total, row) => {
return total + Number(row.credit);
}, 0);
}
},
methods: {
addRow: function() {
this.rows.push({myDate:"",
account:"",
debit: "",
credit: ""
});
},
removeRow: function(row) {
//console.log(row);
this.rows.$remove(row);
},
isNumber: function(evt) {
evt = (evt) ? evt : window.event;
var charCode = (evt.which) ? evt.which : evt.keyCode;
if ((charCode > 31 && (charCode < 48 || charCode > 57)) && charCode !== 46) {
evt.preventDefault();;
} else {
return true;
}
}
}
});
You are going to need a wrapper component like this.
The way bootstrap-select normally activates is to scan the HTML at startup and apply itself to .selectpicker elements. That won't work if the DOM is dynamic, as it is with Vue. You have to activate the elements using the $(element).selectpicker() method as they are created or updated.
See also Make VueJS and jQuery play nice.

Add data to a dynamic row from database

So for example i have a form where the data is added dynamically. When I click on a "+" there comes one more row, where i can add more data. And so on...
Like in this example.
function deleteRow(row)
{
var i=row.parentNode.parentNode.rowIndex;
document.getElementById('POITable').deleteRow(i);
}
function insRow()
{
console.log( 'hi');
var x=document.getElementById('POITable');
var new_row = x.rows[1].cloneNode(true);
var len = x.rows.length;
new_row.cells[0].innerHTML = len;
var inp1 = new_row.cells[1].getElementsByTagName('input')[0];
inp1.id += len;
inp1.value = '';
var inp2 = new_row.cells[2].getElementsByTagName('input')[0];
inp2.id += len;
inp2.value = '';
x.appendChild( new_row );
}
<div id="POItablediv">
<input type="button" id="addPOIbutton" value="Add POIs"/><br/><br/>
<table id="POITable" border="1">
<tr>
<td>POI</td>
<td>Latitude</td>
<td>Longitude</td>
<td>Delete?</td>
<td>Add Rows?</td>
</tr>
<tr>
<td>1</td>
<td><input size=25 type="text" id="latbox"/></td>
<td><input size=25 type="text" id="lngbox" readonly=true/></td>
<td><input type="button" id="delPOIbutton" value="Delete" onclick="deleteRow(this)"/></td>
<td><input type="button" id="addmorePOIbutton" value="Add More POIs" onclick="insRow()"/></td>
</tr>
</table>
So how can i save this data on a database, and then read this. I also need to save the count of the row´s. Is this even possible with JS?

Can datatables sort a column with an input field?

I am trying to make datatables sort my columns. The first column works okay as it's a simple number. However the next column is an input field. When I try to make that sort then nothing happens.
<table width="100%" cellspacing="0" class="table sortable no-margin">
<thead>
<tr>
<th scope="col" class="sorting" style="width: 57px;">
<span class="column-sort">
</span>
ID
</th>
<th scope="col" class="sorting_desc" style="width: 94px;">
<span class="column-sort">
</span>
Order
</th>
</tr>
</thead>
<tbody>
<tr id="row_20" class="odd">
<td id="refKey_20" style="text-align:center;" class="">
1Y
</td>
<td class=" sorting_1">
<input type="text" value="160" size="3" name="item.Order"
maxlength="3" id="Order_20" >
</td>
</tr>
<tr id="row_19" class="even">
<td id="refKey_19" style="text-align:center;" class="">
1X
</td>
<td class=" sorting_1">
<input type="text" value="150" size="3" name="item.Order"
maxlength="3" id="Order_19" >
</td>
</tr>
</tbody>
</table>
Is there some way that I can get datatables to sort input fields?
The easiest way is to add a hidden span inside columns <span style="visibility:hidden">value of the input</span>
You should look at this example that explains how to do sorting on input fields. Basically you declare a sorting function
/* Create an array with the values of all the input boxes in a column */
$.fn.dataTableExt.afnSortData['dom-text'] = function ( oSettings, iColumn )
{
var aData = [];
$( 'td:eq('+iColumn+') input', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
aData.push( this.value );
} );
return aData;
}
And then tell to your table to use that
$('#example').dataTable( {
"aoColumns": [
null,
{ "sSortDataType": "dom-text" }
]
} );
or wit aoColumnDefs
$('#example').dataTable( {
"aoColumnDefs": [{ "sSortDataType": "dom-text" , aTarget: "yourclass"}]
} );
For versions of Datatables 1.10+ the names of some option variables have been changed and a new API introduced. Documentation here: http://datatables.net/examples/plug-ins/dom_sort.html.
Here is a working version of the above accepted answer in 1.10+:
$(document).ready(function () {
var table = $('#example').DataTable({
"columnDefs": [
{
"orderDataType": "dom-input",
"targets": 0, // Just the first column
},
],
});
});
The custom sort function:
$.fn.dataTable.ext.order['dom-input'] = function (settings, col) {
return this.api().column(col, { order: 'index' }).nodes().map(function (td, i) {
return $('input', td).val();
});
}
jQuery.fn.dataTableExt.oSort['textbox-asc'] = function (a, b) {
var vala = $('#' + $(a).filter('input').attr('id')).val().toLowerCase();
var valb = $('#' + $(b).filter('input').attr('id')).val().toLowerCase();
if (vala === '')
return 1;
if (valb === '')
return -1;
return vala < valb ? -1 : vala > valb ? 1 : 0;
};
jQuery.fn.dataTableExt.oSort['textbox-desc'] = function (a, b) {
var vala = $('#' + $(a).filter('input').attr('id')).val().toLowerCase();
var valb = $('#' + $(b).filter('input').attr('id')).val().toLowerCase();
if (vala === '')
return 1;
if (valb === '')
return -1;
return vala < valb ? 1 : vala > valb ? -1 : 0;
};
then use it like this
$(datatable).dataTable({
"iDisplayLength": 50,
"bLengthChange": false,
"bPaginate": false,
"columns": [
null, { "sType": "textbox" }
],
});
If you decide to use the columns option where you are rending information from a JSON file you can easily add a hidden span on your render property. It appears as though DataTables looks for text to order and if it cannot find any, it will break. The example at https://datatables.net/examples/plug-ins/dom_sort.html uses a table that has already been rendered. Here is an example using an API:
...columns([{
"data": "receivedDate",
"render": function (data, type, row, meta)
{
if (data == "null")
{
return "<input type='text' id='datepicker_" + meta.row + "' class='datepicker form-control' /><span class='hidden'><span>";
}
else
{
return "<input type='text' id='datepicker_" + meta.row + "' class='datepicker form-control' value='" + moment(data).format("MM/DD/YYYY") + "'/><span class='hidden'>" + moment(data).format('MM/ DD / YYYY') + "</span>";
}
}
}]);
Set an invisible div with the value before the input field.
<tbody>
<tr id="row_20" class="odd">
<td id="refKey_20" style="text-align:center;" class="">
1Y
</td>
<td class=" sorting_1">
<div style="display:none;">160</div>
<input type="text" value="160" size="3" name="item.Order"
maxlength="3" id="Order_20" >
</td>
</tr>
<tr id="row_19" class="even">
<td id="refKey_19" style="text-align:center;" class="">
1X
</td>
<td class=" sorting_1">
<div style="display:none;">150</div>
<input type="text" value="150" size="3" name="item.Order"
maxlength="3" id="Order_19" >
</td>
</tr>
</tbody>