I'm using Tabulator 3.5 and have a mutator:
Tabulator.extendExtension("mutator", "mutators", {
percentageMutator:function(value, data, type, mutatorParams){
console.log(mutatorParams); // sanity check - we'll come back to this!
console.log(data); // sanity check - we'll come back to this!
// code omitted here to perform calculations
return percentage
}
});
My constructor object, stripped of all niceties, is as below. Notice that the columns are nested twice. Notice also that I'm using the Django template language to dynamically build the table.
$("#markbook-table").tabulator({
columns: [
{title: "First Name", field: "firstname", validator: ["maxLength:50", "string"]},
{title: "Last Name", field: "lastname", validator: ["maxLength:50", "string"]},
{ title:"Some grouping",
columns:[
{% for assessment in assessments %}
{
title: "{{ assessment.name }} (out of {{ assessment.max_mark }})",
columns:[
{title: "Result", field: "{{ assessment.name }}", editor: "input",},
{title: "Mark out of", field: "{{ assessment.name }}_outof"},
{title: "weighting", field: "{{ assessment.name }}_weighting"},
{title: "%", field: "{{ assessment.name }}_percentage", mutator: "percentageMutator", mutatorParams:{assessmentName: "{{ assessment.name }}"}},
},
]
},
{% endfor %}
],
},
{% endfor %}
],
data: tableData,
cellEdited: function(cell) {
row.update({'Statistics_percentage': false}); // this is the line that fails
$.ajax({
// Do ajax magic here to save to database
});
},
})
The crux of the matter is that my row.update() doesn't recalculate the % after a change in the result field. The field name 'Statistics_percentage' has been chosen because it fits my current data but I can easily generalise it later.
The console.log(mutatorParams) in the percentageMutator function is the same upon initial construction of the table, and after editing. However, the console.log(data) shows that the row data passed after editing to the percentageMutator function only contains the cell in question and does not contain any data for other cells in the row - hence the calculations fail after editing.
I'm hoping you'd have an idea about how I can force all the row data to be sent to the mutator upon editing. Thanks so much for even reading this far. Any help would be appreciated.
You need to use the mutatorEdit and mutatorEditParams options if you only want the mutator called on edit.
Mutators are only called on a cell if that cells value has been changed, updating the value of other fields in that rows data will not trigger the mutator to be called again as this could lead to unpredictable behaviour (imagine a mutator that multiplied the incoming value by 1000 as a unit change, if that got called any time any row data got updated, it would quickly corrupt the data).
If all you are looking to do is display the value to a user and you don't need to actually store the data, can i suggest that you use a formatter to do this instead, that way you can call the row.reformat() function after your update, which will re-run the formatter and recalculate the display value.
Related
Wha is the best way to hide empty or null comumns from Yajra Datatables:
var table = $('.data-table').DataTable({
processing: true,
serverSide: true,
ajax: "{{ route('any.route') }}",
columns: [
// How to Hide any of these columns if its data is empty or null?
// for exampe I want to hide mname column ?
// visible: what condition for example !!!!
{data: 'fname', name: 'fname'},
{data: 'mname', name: 'mname'},
{data: 'lname', name: 'lname'},
....
]
});
How do I hide th in table whenever any data is empty or null. for instance, using visible:, what condition should I use to test if the data: is empty or null
Can I see your controller? I have same issue but I can fix it
Try this in your controller!
public function example(){
if ($request->ajax()) { // if request ajax
$data = User::all(); // take all user table
return Datatables::of($data)
->editColumn('fname', function ($row) { //this example for edit your columns if colums is empty
$fname = !empty($row->name) ? $row->name : 'empty';
return $fname;
})
->make(true);
return view('example', compact('data'));
}}
This is the best way to hide NULL values from the data tables.
$('#leads').DataTable({
"columnDefs": [{
"defaultContent": "-",
"targets": "_all"
}]
I am using Tabulator for client-side entry and editing of tabular data. In my application I have need to copy the data from a single [Crew Leader] table to one or more [Crew Member] tables. After data for the [Crew Leader] is entered I am making use of a button to trigger the copying process to the [Crew Member] tables. This is accomplished using the Tabulator setData() function which works as expected.
After data has been copied to the [Crew Member] tables it is necessary to edit each row with information pertinent to the individual [Crew Member]. The on-screen editing process works as expected.
My problem arises when I go to export the data. Note, the data in my JSON string:
is not the same as what appears on-screen; and
is the same for all [Crew Member] tables.
It appears the changes being applied to one [Crew Member] table are being applied (in the virtual DOM) to all [Crew Member] tables. Stated differently, with two "cloned" tables the changes applied to table one are applied to table two in the virtual DOM but not on-screen (and vice versa).
Client-side script which does the copying from the [Crew Leader] table to the [Crew Member] table(s):
function CloneTable() {
var tableCrewLeader = Tabulator.prototype.findTable('#CrewLeaderTable')[0];
var dataCrewLeader = tableCrewLeader.getData();
if (tableCrewLeader.getDataCount() > 0) {
// Verify a Tabulator table is present for each selected [Crew Member] by
// looping through each <div> element with the class "crew-member-card".
$(".crew-member-card").each(function () {
if ($(this).attr('id').length > 0) {
const divId = "#" + $(this).attr('id').replace('Card', 'Table');
const tableMember = Tabulator.prototype.findTable(divId);
if (tableMember.length > 0) {
const tableCrewMember = Tabulator.prototype.findTable(divId)[0];
tableCrewMember.setData(dataCrewLeader);
}
else {
console.log("The Tabulator table " + divId+ " was not found.");
}
}
});
}
}
It is also worth noting, these anomalies do not occur when the data for the [Crew Member] tables is entered directly (no setData() method is used). On-screen edits/changes are not reflected in other tables when data was not copied to the table originally.
For what it's worth, the following is the loop I use to verify the contents of each [Crew Member] table row (using Firefox Web Console to view the log):
var dataCrewMember = tableCrewMember.getData();
$(dataCrewMember).each(function () {
console.log(this);
});
EDIT
I have eliminated the disparity between the on-screen data and the exported data by setting the reactiveData attribute in my Tabulator constructor, as follows:
var table = new Tabulator(divid, {
height: "100%",
layout: "fitDataFill",
reactiveData: true, //enable reactive data
movableRows: true,
tabEndNewRow: true,
rowContextMenu: myActionContextMenu,
keybindings: {
"navUp": true,
"navDown": true,
},
columns: [
{ title: "Phase Code", field: "Phasecode", width: 144, editor: "select", editorParams: { values: function (cell) { return window.laborPhaseCodes; } } },
{ title: "Date Worked", field: "DateComp", hozAlign: "center", sorter: "date", editor: dateEditor },
{ title: "Start Time", field: "TimeStart", hozAlign: "center", sorter: "time", editor: timeEditor },
{ title: "Finish Time", field: "TimeFinish", hozAlign: "center", sorter: "time", editor: timeEditor },
{ title: "Memo", field: "Memo", width: 144, hozAlign: "left", editor: "input" },
{ title: cloneString, headerSort: false, headerClick: CloneTable, rowHandle: true, formatter: "handle" }
],
});
Note, however, I am still experiencing the issue wherein a change made in one [Crew Member] table is automatically replicated within the other [Crew Member] tables. This only happens when the data in the [Crew Member] tables has been populated using the setData() method.
Any assistance is greatly appreciated.
Inside of your cloneTables function, you set dataCrewLeader = tableCrewLeader.getData(). Then you use dataCrewLeader as the value in each of the newly created tables. I can only assume that these are being passed as a reference because they are objects. So, changing one changes them all. (I don't know if this is a bug or if it is expected that tabulator create copies when calling setData().)
To fix this, instead of setting a variable to the value. You want to call .getData() multiple times. So, you could do tableCrewMember.setData(tableCrewMember.getData()) and it will work as expected.
You can comment/uncomment the lines inside of the copyData function from the below example to see the issue.
Edited the example, so that it works without changing anything.
https://jsfiddle.net/nrayburn/85ecbvys/36/
I am trying to create a table for my website and for some reason it is only showing the first row of data.
This is how I am formatting the columns of the data:
const { items } = this.props.item;
// console.log({ items });
// react - bootstrap - table - next
const columns = [{
dataField: 'team',
text: 'Team',
sort: true,
formatter: (cellContent, row, rowIndex) => (
Object.values(row.team)[rowIndex]
)
}, {
dataField: 'current_Rank',
text: 'Current Rank',
sort: true,
formatter: (cellContent, row, rowIndex) => (
Object.values(row.current_Rank)[rowIndex]
)
}, {
dataField: 'new_Rank',
text: '321 Rank',
sort: true,
formatter: (cellContent, row, rowIndex) => (
Object.values(row.new_Rank)[rowIndex]
)
}];
This is how I am returning the table so that it renders the table:
return (
<BootstrapTable
keyField="team"
data={items}
columns={columns}
striped
hover />
)
}
}
The data:
Picture from the console
Live site: https://nhl-321-pointsystem.herokuapp.com/
I looked up your network response for /api/items API call, and found out that the data contains only one item. This being one of the reason you're seeing a single row when the table is rendered.
Please note the, another reason for the issue is, react-bootstrap-table-next key
data accepts a single Array object. And not array of single object.
You should re-arrange your data so that key 'team' will be present for all items in the array. And rest of the column header values (e.g. current_Rank) are available for each like.
Something like a reformat function I created in the sandbox available here.
Plus point - After you apply the reformat function, you won't need formatter for each column unlike your previous solution.
Alternate but recommended solution would be to send the formatted response from the API endpoint only, instead of re-parsing and creating new object to fit the needs of UI.
Sandbox link - https://codesandbox.io/embed/32vl4x4oj6
(FYI, I'm using jqGrid 5.)
My question is about a column filter in jqGrid. I have a field that comes back as a boolean from the server, either "true" or "false". I may have the luxury of changing this to a 0/1, but I really don't want to if I can avoid it. So, it looks like this:
{"rows":[{"rowID":47568,"field1":"some text here","isOpen":true} ...]}
In my jqGrid, I am using the "beforeProcessing" function to show that boolean as something besides "true" or "false":
beforeProcessing: function(data) {
for(var i=0, len=data.rows.length; i< len; i++) {
data.rows[i].isOpenModified = openFormatter(data.rows[i].isOpen);
}
...
function openFormatter(isOpen) {
return isOpen ? '' : 'CLOSED';
}
This has the effect of leaving the cell blank if isOpen is true, and showing 'CLOSED' if it's false. Everything is good so far.
The problem I'm having is the select field for the toolbar filter field. Here is the jqGrid column model:
colModel: [
{ name: 'field1', label: 'Stuff', width: 100},
{ name: 'isOpenModified', index:'isOpen', label: 'CLOSED',
searchoptions: { sopt:['eq'], value: ':All;" ":Open;"CLOSED":CLOSED'}, stype: 'select'},
],
This correctly shows the select box for the "CLOSED" field, but selecting anything besides "All" shows nothing.
I did try to change the isOpenModified to a 0 and 1, and then the filter select box worked, but of course I can't deliver that. My requirement is to display either empty (isOpen==true) or "CLOSED" (isOpen==false).
I feel like the solution might involve the name and index fields of the column model, but I can't pinpoint what I'm doing wrong. I appreciate your time....
I discovered the solution to the original problem, however it presents another problem.
The searchoptions needed to be modified in order to make it work:
{ name: 'isOpenModified', label: 'CLOSED',
searchoptions: { sopt:['eq'], value: ':All; :Open;CLOSED:CLOSED'}, stype: 'select'}
Notice the quotes are removed from around the space and from around CLOSED.
I also changed the format method to return a space instead of an empty string:
return isOpen ? ' ' : 'CLOSED';
This causes another issue: Now the "Open" option is selected by default (but it wasn't filtered by Open items). I attempted to solve that problem by reversing the order:
{ name: 'isOpenModified', label: 'CLOSED',
searchoptions: { sopt:['eq'], value: ' :Open;:All;CLOSED:CLOSED'}, stype: 'select'}
This worked insomuch as "All" was selected by default, but it was 2nd in the list. I'm waiting for the "style" guys to throw a fit. Any advice?
I would like to search datagrid in Kendo UI during typing into input field above the grid.
How can I do it?
Thanks for any advice.
Here is example of columns:
$("#grid").kendoGrid({
dataSource: dataPacket,
filterable: true,
pageSize: 10,
pageable: true,
sortable: true,
reorderable: true,
resizable: true,
columnMenu: true,
height: 550,
toolbar: ["create", "save", "cancel"],
columns: ["id",
"username",
"name",
"surname",
"email",
{
field :"created",
title : "Created at",
format: "{0:M/d/yyyy}",
parseFormats: ["dd-MM-yyyy"],
type: "date"
},
Kendo make this thing really easy for you, what is needed is to create a filter and pass it to the DataSource.
http://docs.telerik.com/kendo-ui/api/framework/datasource#methods-filter
However, this problem must be divided into two different tasks:
a) Capture the key events in the search box, throttle it and start the search "operation".
b) Build a filter and pass it to the DataSource.
So for throttling the keyboard events, we need a timeout. Or use the throttle function from underscorejs. Why? We don't wanna trigger a search operation on each key press. Only 250 milliseconds (this number is up to you) after the last keystroke.
Here is your sample HTML
<input type="text" id="search" />
Here is your sample script. I wrap everything as a self calling function as you don't wanna create a mess declaring global variables.
(function($, kendo){
// ID of the timeout "timer" created in the last key-press
var timeout = 0;
// Our search function
var performSearch = function(){
// Our filter, an empty array mean "no filter"
var filter = [];
// Get the DataSource
var dataSource = $('#grid').data('kendoGrid').dataSource;
// Get and clean the search text.
var searchText = $.trim($('#search').val());
// Build the filter in case the user actually enter some text in the search field
if(searchText){
// In this case I wanna make a multiple column search so the filter that I want to apply will be an array of filters, with an OR logic.
filter.push({
logic: 'or',
filters:[
{ field: 'username', operator: 'contains', value: searchText },
{ field: 'name', operator: 'contains', value: searchText },
{ field: 'surname', operator: 'contains', value: searchText },
{ field: 'email', operator: 'contains', value: searchText }
]
});
}
// Apply the filter.
dataSource.filter(filter);
};
// Bind all the keyboard events that we wanna listen to the search field.
$('#search').on('keyup, keypress, change, blur', function(){
clearTimeout(timeout);
timeout = setTimeout(performSearch, 250);
});
})(window.jQuery, window.kendo);
Bottom-line: Make sure you are using the right DataSource configuration.
If you configured serverFiltering = true, this filtering logic will be part of your Ajax request, so your server will have to interpret and perform the filtering on server-side.
In case you configured serverFiltering = false all this filtering logic will be evaluated on client side using JavaScript (damn fast!). And in this case, the schema (what data-type is expected on each column) must be also well-configured.