I have a Django application and a page where data is written to a table that I am styling using DataTables. I have a very simple problem that has proven remarkably complicated to figure out. I have a dropdown filter where users can select an option, click filter, and then an ajax request updates the html of the table without reloading the page. Problem is, this does not update the DataTable.
My html:
<table class="table" id="results-table">
<thead>
<tr>
<th scope="col">COL 1</th>
<th scope="col">COL 2</th>
<th scope="col">COL 3</th>
<th scope="col">COL 4</th>
<th scope="col">COL 5</th>
</tr>
</thead>
<tbody class="table_body">
{% include 'results/results_table.html' %}
</tbody>
</table>
results_table.html:
{% for result in result_set %}
<tr class="result-row">
<td>{{ result.col1 }}</td>
<td>{{ result.col2 }}</td>
<td>{{ result.col3 }}</td>
<td>{{ result.col4 }}</td>
<td>{{ result.col5 }}</td>
</tr>
{% endfor %}
javascript:
function filter_results() {
var IDs = [];
var IDSet = $('.id-select');
for (var i = 0; i < IDSet.length; i++) {
var ID = getID($(IDSet[i]));
IDs.push(ID);
}
// var data = [];
// data = $.ajax({
// url:"filter_results/" + IDs + "/",
// dataType: "json",
// async: false,
// cache: false,
// data: {},
// success: function(response) {
// $('#results-table').html(response);
// // console.log(response.length);
// // console.log(typeof response);
// //
// }
// }).responseJSON;
var dataTable = $('#results-table').DataTable();
dataTable.clear();
$('.table_body').html('').load("filter_results/" + IDs + "/", function() {
alert("Done");
});
dataTable.draw();
}
And views:
def filter_results(request, ids):
ids = [int(id) for id in ids.split(',')]
account_set = Account.objects.filter(id__in=ids)
form = ResultsFilterForm()
result_set = Result.objects.filter(account__in=account_set)
context = {
'form': form,
'result_set': result_set
}
return render(request, 'results/results_table.html', context)
What is happening is that the Ajax is correctly updating what I see on the HTML page, but it is not updating the actual data table. So I can filter for a particular ID, for instance, which has 2 results and this will work and show me the two results on the HTML page without reloading it. However, the DataTable still contains the rest of the results so there is still like a "next" page which makes no sense when there are only 2 results.
I also tried changing the view to return a JSON response with the code that is commented out of the JS and when I did that I got "Warning: DataTable encountered unexpected parameter '0' at row 0 column 0" even though the data coming from Django was the correct data in JSON form.
Really stuck here, appreciate the help.
I figured out a way to make it work. Though maybe not the "best" way to do this, it is simple so I hope this helps someone else. Change JavaScript as follows:
function filter_results() {
$('#results-table').DataTable().destroy();
var IDs = [];
var IDSet = $('.id-select');
for (var i = 0; i < IDSet.length; i++) {
var ID = getID($(IDSet[i]));
IDs.push(ID);
}
$('.table_body').html('').load("filter_results/" + IDs + "/", function() {
$('#results-table').DataTable();
});
}
That was it. All I needed to do was destroy the old DataTable at the beginning of the filter_results function and then re-create it. Note however that the recreation is the function to execute after the call to .load(). If you don't write it like this you will have an issue with JS recreating the DataTable before the the html is finished loading, which is a problem I encountered. Nesting that function inside the .load() call is an easy workaround though.
I know there is probably a better way to do this, one that involves updating rather than destroying and recreating the DataTable. If somebody knows of it feel free to post it as an answer but for now this workaround is good for me!
Related
I have a HTML table where one of the columns value is dynamically added. I have an update button, upon clicking it I want this data to get updated in my sql database. For this, I am planning to first fetch the table data and put into view , then send data to controller and then updating sql.
I am stuck at the first step,
Descibing table below
<thead>
<tr>
<th>ID</th>
<th >Name</th>
<th>Active</th>
<th>Order By</th>
</tr>
</thead>
<tbody>
#if (ViewBag.data != null)
{
foreach (var item in ViewBag.data)
{
<tr>
<td >#item.AutoID</td>
<td #item.Text</td>
<td >#item.Active</td>
<td>#item.OrderBy</td>
</tr>
}
}
</tbody>
</table>
</div>
</div>
<input type="submit" value="Update Preference" class="BtnUpdOrderId" />
</div>
I tried this below js function to fetch the data
$(".BtnUpdOrderId").click(function () {
var tr = $(this).closest('tr');
var id = tr.find('input[name="autoid"]').val();
var text = tr.find('input[name="text"]').val();
var active = tr.find('input[name="active"]').val();
var orderby = tr.find('input[name="orderby"]').val();
alert('type1 : ' + id + ' ' + text + ' ' + active + ' ' + active);
});
but not sure why nothing came in alert
var TableData = new Array();
$('#tblLookup1 tr').each(function (row, tr) {
TableData = TableData + $(tr).find('td:eq(0)').text();
alert(TableData);
});
then tried the above block of code to get data in a variable but still not able to get anything.
Once I get the data I can try sending from view->controller.
So need the following help:
what mistake am I making?
once this is fixed, how to send data to sql? (this is a ado.net based mvc project)
you might want to consider creating a json object:
Creating json object in mvc and returning from controller
then build your table Convert JSON array to an HTML table in jQuery
finally, the update need only post back the json object
https://dontpaniclabs.com/blog/post/2013/02/27/posting-json-data-to-an-mvc-controller-via-ajax/
if you going to use this jason object make sure you use serialization
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-6-0
your have to patch these concepts together but there are a lots of tutorials and examples online so it be a good learning experience
I hope this helps
helpful links:
https://www.sqlshack.com/modifying-json-data-using-json_modify-in-sql-server/
Updating a JSON object using Javascript
https://www.geeksforgeeks.org/how-to-convert-json-data-to-a-html-table-using-javascript-jquery/
create json object from html table using selected colums using jquery
I have a table that sets out a list of rules. When the checkboxes are clicked, I need them to take this "true" value and post to an API endpoint. This has been set up, but what I am getting back is that "associated_rule" is undefined.
I have tried setting $scope.associated_rule.selected = true; in my controller, but this still doesn't define the variable and throws up the same error in the console.
Here is my HTML form:
<form name="rules_form" method="post" ng-submit="attach()">
<table class="table table-striped table-hover" ng-model="associated_rules">
<thead>
<th>Rule Types:</th>
<th>Description:</th>
<th>Start Time:</th>
<th>End Time:</th>
<th>Apply Rule to Vehicle:</th>
</thead>
<tr ng-repeat="associated_rule in associated_rules">
<td>#{{ associated_rule.resource_ids.accounts }}</td>
<td>#{{ associated_rule.description }}</td>
<td>#{{ associated_rule.start_time }}</td>
<td>#{{ associated_rule.end_time }}</td>
<td><input type="checkbox" ng-model="associated_rule.selected" aria-label="rule"></td>
</tr>
</table>
<button class="btn btn-primary" ng-click="attach()">Attach</button>
</form>
My Controller event:
$scope.attach = function () {
$scope.associated_rule.selected = true;
var i;
for (i = 0; i < $scope.associated_rule.selected.length; i++) {
//need to create a loop where the true value is picked up and then I can send the data using a POST method. But I'm stuck on this.
}
console.log(the result of the event);
};
For now, I just want the results to console.log, so I can see that the event is creating the loop and displaying the results. After that, I should be OK.
Any help would be much appreciated.
I have fixed this by defining the $scope.rule as an empty array and setting the $scope.rule.selected to "false" by default.
Great! step one! But the checkboxes are ALL selecting when you click A checkbox - think this may be the ng-repeat causing me a pain in the backside.
$scope.rule = [];
$scope.rule.selected = false;
So, how do I ensure that only the checkboxes set that I select and not all at once?
Fixed this too; as the above was just making the entire array selected; as i wasn't drilling down into the array. This did it:
ng-model="rules.selected[associated_rule.id]"
by modelling the a rule within that defined array, it shows up when testing. Brill. :)
By mistake you are changing the value of your check box on clicking the button:
$scope.associated_rule.selected = true;
This will give the current value, selected or not selected
$log.log($scope.associated_rule.selected);
I need to update an html table cell, which is created using a Django template for loop, once the selection of the pickerselect has changed. My HTML for the table:
<tbody>
{% for task in tasks %}
<tr>
<td>{{ task.taskNumber }}</td>
<td>{{ task.taskDescription }}</td>
<td><div name="dataOutput">{{task.thisPeriod}}</div></td>
</tr>
{% endfor %}
</tbody>
My HTML for the pickerselect:
<select name="selValue" class="selectpicker">
<option value="1">Current Period ({{currentPeriod}})</option>
<option value="2">Previous Period ({{previousPeriod}})</option>
</select>
My javascript:
<script language="JavaScript">
$(function() {
$('.selectpicker').on('change', function(){
var selected = $(this).find("option:selected").val();
var updateArray = document.getElementsByName("dataOutput");
for (var i = 0; 1 < updateArray.length; i++) {
if (selected == 1) {
updateArray[i].innerHTML = "{{task.thisPeriod}}";
} else if (selected == 2) {
updateArray[i].innerHTML = "temp";
}
}
});
});
</script>
At this point, the table row values update with the placeholder "temp" when selection 2 is made but when I change back to selection 1, no values appear in the table. I assume that this is because the Django template for loop is not being refreshed so it doesn't know how to render for the specific task in the loop {{task.thisPeriod}}. I have looked through a number of questions about refreshing div's using javascript but nothing has been effective... I always get the same result of blank values in the table.
It's kind of obvious, but you can't do it just like that, because javascript doesn't record the tasks django context that you passed to the template. You should use ajax to pass the selected value to a separate method in views.py, in there you find out the data and return it to the javascript, then render the div with the new data.
I have followed the approach "DataTables: Custom Response Handling" by Fabrício Matté. However, my requirement is to avoid rendering of table's rows and columns via callback. Instead, would like to traverse the current ajax request returned json data and render explicit html (tr/td) to have more control. Due to this, currently i see data shown twice on my table. At the same time, i understand that callback is rendering the page related buttons: prev, 1,2 next etc and click events which i would like to reuse and wan't to avoid custom implementation.
JS:
function notificationsCtrl($scope,$http,$resource, DTOptionsBuilder, DTColumnBuilder) {
var vm = this;
vm.notifications = [];
$scope.dtOptions = DTOptionsBuilder.newOptions()
.withOption('serverSide', true)
.withOption('ajax', function(data, callback, settings) {
// make an ajax request using data.start and data.length
$http.get('notifications/list?page=' + (((data.start)/10)+1)).success(function(res) {
// map your server's response to the DataTables format and pass it to
// DataTables' callback
callback({
recordsTotal: 120,
recordsFiltered: 120,
data: res
});
vm.notifications = res;
});
})
.withDataProp('data') // IMPORTANT¹
.withOption('processing', true)
.withPaginationType('full_numbers');
$scope.dtColumns = [
DTColumnBuilder.newColumn('notificationId').withTitle('notificationId'),
DTColumnBuilder.newColumn('createUserId').withTitle('createUserId'),
DTColumnBuilder.newColumn('Language').withTitle('language')
];
}
HTML: sample but actual will require extra processing for some of the td tags
<table datatable="" dt-options="dtOptions" dt-columns="dtColumns" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Language</th>
<th>Last Updated</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="notification in wynkCMSToolApp.notifications">
<td>{{ notification.notificationId }}</td>
<td>{{ notification.title }}</td>
<td>{{ notification.Language }}</td>
</tr>
</tbody>
</table>
If you want to render directly in the HTML, consider using the Angular renderer. However, such renderer does not support the server side processing.
So I recommend you use the server side processing along with the columns.render function.
Here an example of using the render function.
I'm using jQuery and django
I have a dynamic table generated by jQuery, which holds the email field and password field and the delete button that could remove the row:
$("button#genBtn").click(function() {
var t = $("input#inputEmail").val();
var p = $("input#inputPassword").val();
var delBtn = $('<button class=\"btn btn-danger\">delete</button>');
var row = $("<tr><td>" + t + "</td><td>" + p + "</td><td></td></tr>");
$('td:last', $(row)).append(delBtn);
$(delBtn).bind("click", deleteNode);
$("table tbody").append(row);
});
This is the table:
<table class="table table-striped">
<thead>
<tr>
<th>email</th>
<th>password</th>
<th>action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Let say I'd like to get the data of all rows from this table, and post it to sever side. How could I do that?
Without any additional libraries, something like this:
(1) get the data from the table
function getTableData()
{
// Array of data we'll return
var data = [];
// Counter
var i = 0;
// Cycle through each of the table body's rows
$('tbody tr').each(function(index, tr) {
var tds = $(tr).find('td');
// Check we've got two <td>s
if (tds.length > 1) {
// If we do, get their text content and add it to the data array
data[i++] = {
email: tds[0].textContent,
password: tds[1].textContent
}
}
});
return data;
}
(2) post it to the server
$.ajax({
method: 'post',
url: '', // Set the URL of whatever in Django will handle your post
data: getTableData()
});
However, if I were attempting this task, I would achieve (1) by using Knockout, which would allow for a much better separation between view and viewmodel (or between template and view, you might think of them, as a Django user). Great that you're using a decent server-side framework, would be a pity to end up with spaghetti code on the client!