I'm trying to get my table to load on intervals. I am now receiving the below error:
TypeError: g is null
The user will enter form parameters and then hit the submit button which has a click event. As follows:
$('.searchSubmit').on('click', function()
{
var data = {
searchCriteria: {
bill: $('#import_bill').val(),
ramp: $('#import_ramp').val(),
// few other parameters
}
};
$.ajax({
url: 'api/railmbs.php',
type: 'POST',
data: data,
dataType: 'html',
success: function(data, textStatus, jqXHR)
{
var jsonObject = $.parseJSON(data);
var table = $('#example1').DataTable({
"data": jsonObject,
"columns": [
{ "data": "BILL" },
{ "data": "RAMP" },
// few more columns
],
"iDisplayLength": 25,
"paging": true,
"bDestroy": true,
"stateSave": true,
"autoWidth": true
});
var idle = 0;
var idelInterval = setInterval(timer, 10000);
$(this).mousemove(function(e){idle = 0;});
$(this).keypress(function(e){idle = 0;});
function timer()
{
idle = idle + 1;
if(idle > 2)
{
$('#example1').DataTable().ajax.reload(); // <--error occurs here
console.log('table reloaded');
}
}
},
error: function(jqHHR, textStatus, errorThrown)
{
console.log('fail');
}
});
});
Here's the funny part...above, where I pointed to where the error was occurring, I originally had it looking like this:
$('#example').DataTable().ajax.reload();
Notice the table name was 'example' instead of 'example1'. The table ID is indeed example1, as I indicated up near where the success function begins. When I saw the reload interval was looking at a different table ID, I changed it, which now is causing the error at the top.
I don't think I should keep the ID as 'example' because that is not the correct ID.
With that said, why am I getting the error?
I've worked out a solution that seems to do the trick. I tried to keep this as simple as I could, while still incorporating jQuery and (I think) solving the issue you were having.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ajax Reloader</title>
</head>
<body>
<header>
<h1>AJAX Reloader</h1>
</header>
<section>
<form id="theForm">
<input id="theButton" type="button" value="Click me to load data">
</form>
</section>
<section>
<p>
<h3>Data Reload in: <span id="reloadCounter">5</span></h3>
</section>
<section>
<table id="theTable"></table>
</section>
<template id="theTemplate">
<tr>
<td>Name:</td>
<td data-js></td>
</tr>
</template>
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous">
</script>
<script>
(function() {
const $btn = $('#theButton');
const $tbl = $('#theTable');
const $tmpl = $('#theTemplate');
const $span = $('#reloadCounter');
let isLoading = false;
let counter = 5;
// Load data on Button click.
$btn.click(() => {
loadData(true);
});
// Auto-reload table data every 5 seconds.
// Add a countdown, just for gits and shiggles
window.setInterval(() => {
if (counter === 0) {
loadData(false);
counter = 5;
} else {
counter--;
}
$span[0].textContent = counter.toString();
}, 1000);
function loadData(isBtnClick) {
if (!isLoading) {
isLoading = true;
let file = (isBtnClick) ? 'data1' : 'data2';
$.ajax({
url: `./${file}.json`,
type: 'GET',
dataType: 'json',
success: (data, status) => {
console.log('loadData::success - Got data!', data);
$tbl[0].innerHTML = '';
let td = $tmpl[0].content.querySelector('td[data-js]');
data.forEach((item, idx, arr) => {
td.textContent = item.name;
let tr = document.importNode($tmpl[0].content, true);
$tbl[0].appendChild(tr);
});
isLoading = false;
}
});
if (isBtnClick) {
console.log('loadData - Button clicked');
} else {
console.log('loadData - Interval triggered');
}
}
}
})();
</script>
</body>
</html>
data1.json
[
{"name": "Rincewind"},
{"name": "Mustrum Ridcully"},
{"name": "Ponder Stibbons"},
{"name": "Gaspode The Wonder Dog"},
{"name": "CMOT Dibbler"},
{"name": "Nanny Ogg"}
]
data2.json
[
{"name": "Jason Ogg"},
{"name": "Tiffany Aching"},
{"name": "Rob Anybody"},
{"name": "Mrs. Cake"},
{"name": "Nobby Nobbs"},
{"name": "Fred Colon"}
]
My style of coding is a little different from yours, but the same basic concepts should be in play here.
Hope it helps. :-)
How do you expect ajax.reload() to work? There is no AJAX in use and therefore no previous AJAX to reload. Do this instead (schematic) :
var table = $('#example1').DataTable({
ajax: {
url: 'api/railmbs.php',
data: function() {
return {
searchCriteria: {
bill: $('#import_bill').val(),
ramp: $('#import_ramp').val(),
// few other parameters
}
}
}
},
"columns": [
{ "data": "BILL" },
{ "data": "RAMP" },
// few more columns
],
"iDisplayLength": 25,
"paging": true,
"bDestroy": true,
"stateSave": true,
"autoWidth": true
});
Now you should be able to table.ajax.reload() from anywhere where table is available.
Related
could someone help me with one problem? I want to add a process bar when you waiting for a response from the server (Django 3.x).
Step to reproduce:
On the page 'A' we have the form.
Enter data to form.
Submit POST request by clicking to button on the page 'A'.
Waiting for getting the result on the page 'A'.
Get the result on the page 'A'.
So, I want to add process bar after 4th and before 5th points on the page 'A'. When you will get the result on the page 'A' it should disappear.
Python 3.7
Django 3.x
You can use nprogress, it's a library used for progress bars. Use this inside the interceptor where you can config it for displaying only when request is in progress until finished.
There are lots of ways to do this. I think using jquery would be easier. Basically you just need to prevent submitting the page and do an Ajax request to server. something like
<script type='text/javascript'>
$(document).ready(function () {
$("form").submit(function (e) {
// prevent page loading
e.preventDefault(e);
$('#loadinAnimation').show();
// preapre formdata
$.ajax({
type: "yourRequestType",
url: "yourUrlEndpoint",
data: formdata,
success: function (data) {
$('#loadinAnimation').hide();
// do rest of the work with data
}
});
});
});
</script>
and show appropriate loading animation in your html part
<div id='loadinAnimation' style='display:none'>
<div>loading gif</div>
</div>
You can also do it using UiKit Library in Javascript on your Django Template Page.
Below code is when a file is Uploaded
In your template file (template.html)
<body>
..
<form>
<progress id="js-progressbar" class="uk-progress" value="0" max="100" hidden></progress>
...
<div class="uk-alert-danger uk-margin-top uk-hidden" id="upload_error" uk-alert></div>
...
</form>
</head>
<script type="text/javascript">
$(document).ready(function(){
var bar = document.getElementById('js-progressbar');
UIkit.upload('.js-upload-list', {
url: '',
name : "customer-docs",
params :{
"csrfmiddlewaretoken":"{{csrf_token}}"
},
method : "POST",
concurrent:1,
allow:'*.(csv|xlsx)',
beforeSend: function (environment) {
console.log('beforeSend', arguments);
// The environment object can still be modified here.
// var {data, method, headers, xhr, responseType} = environment;
},
beforeAll: function (args,files) {
console.log('beforeAll', arguments);
},
load: function () {
console.log('load', arguments);
},
error: function (files) {
console.log("---------------")
},
complete: function () {
console.log('complete', arguments);
},
loadStart: function (e) {
console.log('loadStart', arguments);
bar.removeAttribute('hidden');
bar.max = e.total;
bar.value = e.loaded;
},
progress: function (e) {
console.log('progress', arguments);
bar.max = e.total;
bar.value = e.loaded;
},
loadEnd: function (e) {
console.log('loadEnd', arguments);
bar.max = e.total;
bar.value = e.loaded;
},
completeAll: function (data) {
console.log('completeAll', arguments);
console.log('completeAll', data);
let redirect_loc = ""
setTimeout(function () {
bar.setAttribute('hidden', 'hidden');
}, 1000);
// This is the response from your POST method of views.py
data.responseText = JSON.parse(data.responseText)
if(data.responseText.status == 201){
// swal is another library to show sweet alert pop ups
swal({
icon: data.responseText.status_icon,
closeOnClickOutside: true,
text: data.responseText.message,
buttons: {
Done: true
},
}).then((value) => {
switch (value) {
case "Done":
window.location.href = ""
break;
}
});
}
else if(data.responseText.status == 500){
swal({
icon: data.responseText.status_icon,
closeOnClickOutside: true,
text: data.responseText.message,
buttons: {
Ok: true
},
}).then((value) => {
switch (value) {
case "Ok":
window.location.href = ""
break;
}
});
}
}
});
// This block of code is to restrict user to upload only specific FILE formats (below example is for CSV & XLSX files)
(function() {
var _old_alert = window.alert;
window.alert = function(e) {
console.log(e)
if(e.includes("csv|xlsx") || e.includes("Invalid file type")) {
$("#upload_error").html("Invalid file format. Valid formats are CSV, XLSX").removeClass('uk-hidden')
}else if(e.includes("Internal Server Error")) {
$("#upload_error").html("Internal Server Error Kindly upload Documents again").removeClass('uk-hidden')
}
else {
_old_alert.apply(window,arguments);
$("#upload_error").addClass('uk-hidden').html("")
}
};
})();
});
</script>
On your views.py you can do your computation and once done, you can return a response like below
resp_json = {
"status" : 201,
"status_icon" : "success",
"url" : "/",
"message": message
}
return HttpResponse(json.dumps(resp_json))
For more info on SWAL (Sweet Alerts), visit https://sweetalert.js.org/guides/
I am trying to populate datatables with a complex json scheme, however I don't know exactly how to do it.
First, some parts of json are nested and it needs iteration.
Second I need to create some markup, basically a href link.
Here is what I have:
$(document).ready(function(){
$('#empTable').DataTable({
'processing': true,
'serverSide': true,
'serverMethod': 'post',
'ajax': {
'url':'/dashboard/ajaxgetrequests',
dataSrc: "json_list"
},
'columns': [
{ data: 'firstname' },
{ data: 'funding_project_name' } // this must be a link like <a href='/<relation_id>'><funding_project_name></a>
]
});
});
{
"json_list":{
"125":{
"firstname":"John",
"funding_project_name":"A",
"relation_id": "7"
},
"133":{
"firstname":"Cesar",
"funding_project_name":[
"A",
"B"
],
"relation_id":[
"7",
"9"
]
}
}
}
1) For nested JSON you can use something like this:
// JSON structure for each row:
// {
// "engine": {value},
// "browser": {value},
// "platform": {
// "inner": {value}
// },
// "details": [
// {value}, {value}
// ]
// }
$('#example').dataTable( {
"ajaxSource": "sources/deep.txt",
"columns": [
{ "data": "engine" },
{ "data": "browser" },
{ "data": "platform.inner" },
{ "data": "details.0" },
{ "data": "details.1" }
]
} );
2) To edit and insert a link you can use columns.render (documentation)
$('#example').dataTable( {
"columnDefs": [ {
"targets": 0,
"data": "download_link",
"render": function ( data, type, row, meta ) {
return 'Download';
}
} ]
} );
Honestly, there may be a better built in way of handling this but when I experience things that do not fit the exact mold of using the base datatable functionality, I prefer to take manual control of the generation. This will give you an overview on how to do it with your structure:
Just basic html for your table (nothing really to see here):
<table id="empTable">
<thead>
<tr><th>First Name</th><th>ProjectName</th></tr>
</thead>
<tbody></tbody>
</table>
In JS we declare a variable we can use throughout our script then on ready event we instatiate our datatable:
var dt;
$(document).ready(function () {
dt = $('#empTable').DataTable();
loadDT();
});
We will also use a function call 'loadDT()' and what this will do is trigger a ajax call to the backend to get your json, in this example, I'm just gonna mock it but in your world so this on the ajax success:
Iterate your list and determine the types then use the api call row.add to dynamically add new rows to your table. (notice we are reusing the stored variable dt that we initially declared.) This is where you can do whatever custom logic fun you need to do.
function loadDT(){
var mockJSON = { "json_list":{ "125":{ "firstname":"John","funding_project_name":"A","relation_id": "7"},"133":{ "firstname":"Cesar","funding_project_name":["A","B"],"relation_id":["7","9"]}}};
$.each(mockJSON.json_list, function (i, n){
if(Array.isArray(n.funding_project_name)) {
$.each(n.funding_project_name, function (i2, p){
dt.row.add([n.firstname,'' + p + '']);
dt.draw(false);
});
} else {
dt.row.add([n.firstname, '' + n.funding_project_name + '']);
dt.draw(false);
}
});
}
Like previously stated, there may be some built in functions to handle this that I am unaware but when things get complicated, just know you can take manual control of it.
Full Example:
var dt;
$(document).ready(function () {
dt = $('#empTable').DataTable();
loadDT();
});
function loadDT(){
var mockJSON = { "json_list":{ "125":{ "firstname":"John","funding_project_name":"A","relation_id": "7"},"133":{ "firstname":"Cesar","funding_project_name":["A","B"],"relation_id":["7","9"]}}};
$.each(mockJSON.json_list, function (i, n){
var projLinks = "";
if(Array.isArray(n.funding_project_name)) {
$.each(n.funding_project_name, function (i2, p){
projLinks += '' + p + ' ';
});
} else {
projLinks = '' + n.funding_project_name + '';
}
dt.row.add([n.firstname, projLinks]);
dt.draw(false);
});
}
<link href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<table id="empTable">
<thead>
<tr><th>First Name</th><th>ProjectName</th></tr>
</thead>
<tbody></tbody>
</table>
I'm using get_bottom_selected to get all the checked/selected nodes in JSTree. When I setup a button in my form that calls the following method it works. When I try to call the same function from check box click event it does not find any selected nodes, even if there are some.
function testit() {
var data = $('#my_tree').jstree(true).get_bottom_selected(true);
for(var count = 0; count < data.length; count++){
// Do Stuff
}
}
When the following event fires I want to call the function and get all the selected child nodes, but it does not work. Is there something specific to do on this event that works different than calling from a button click event?
.on("check_node.jstree uncheck_node.jstree", function(e, data) {
testit(); // first line of this function does not get any selected data, even if several are selected. When called from a button click event in my form it does work.
});
Here's how I currently have my jstree setup.
$('#my_tree')
.on("changed.jstree", function (e, data) {
// Do Stuff
})
.jstree({
checkbox: {
"keep_selected_style": false,
"visible" : true,
"three_state": true,
"whole_node" : true,
},
plugins: ['checkbox'],
'core' : {
'multiple' : true,
'data' : {
"url" : "/static/content_data.json",
"dataType" : "json"
}
}
})
.on("check_node.jstree uncheck_node.jstree", function(e, data) {
testit();
});
Because of the strict mode you will get the exception that if you try to use get_bottom_checked
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them.
at Function.invokeGetter (<anonymous>:2:14)
You can use data.selected from your check or uncheck event handler if you just want the ids of selected nodes but if you need more than that you can use 'data.instance._model.data'. As you can see in my example I am trying to alert if there is only one item selected and that's state is open. In the code example, you can see the Alert if you open the `Europe1 and select the checkbox.
var data1 = [{
"id": "W",
"text": "World",
"state": {
"opened": true
},
"children": [{
"text": "Asia"
},
{
"text": "Africa"
},
{
"text": "Europe",
"state": {
"opened": false
},
"children": ["France", "Germany", "UK"]
}
]
}];
function testit(data) {
alert(data.length + ' and ids are ' +data );
for (var count = 0; count < data.length; count++) {
}
}
$('#Tree').jstree({
core: {
data: data1,
check_callback: false
},
checkbox: {
three_state: false, // to avoid that fact that checking a node also check others
whole_node: false, // to avoid checking the box just clicking the node
tie_selection: false // for checking without selecting and selecting without checking
},
plugins: ['checkbox']
})
$('#Tree').on("check_node.jstree uncheck_node.jstree", function(e, data) {
if (data.selected.length === 1) { alert(data.instance._model.data[data.selected].state['opened']); }
testit(data.selected);
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" type="text/css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script>
<div id="Tree"></div>
According to this
you can get all selected nodes on change event like this:
$('#jstree').on('changed.jstree', function (e, data) {
var i, j, r = [];
for(i = 0, j = data.selected.length; i < j; i++) {
r.push(data.instance.get_node(data.selected[i]).text);
}
$('#event_result').html('Selected: ' + r.join(', '));
}).jstree();
I'm trying to insert a chart dynamically with javascript. I found an example of how to do such a thing and it almost works. The chart loads but then underneath the chart, part of the Javascript used to display the chart actually shows as text on the page. It otherwise works fine.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<div id="tvTest"></div>
<script>
/* helpers
*/
// runs an array of async functions in sequential order
function seq (arr, callback, index) {
// first call, without an index
if (typeof index === 'undefined') {
index = 0
}
arr[index](function () {
index++
if (index === arr.length) {
callback()
} else {
seq(arr, callback, index)
}
})
}
// trigger DOMContentLoaded
function scriptsDone () {
var DOMContentLoadedEvent = document.createEvent('Event')
DOMContentLoadedEvent.initEvent('DOMContentLoaded', true, true)
document.dispatchEvent(DOMContentLoadedEvent)
}
/* script runner
*/
function insertScript ($script, callback) {
var s = document.createElement('script')
s.type = 'text/javascript'
if ($script.src) {
s.onload = callback
s.onerror = callback
s.src = $script.src
} else {
s.textContent = $script.innerText
}
// re-insert the script tag so it executes.
document.head.appendChild(s)
// clean-up
$script.parentNode.removeChild($script)
// run the callback immediately for inline scripts
if (!$script.src) {
callback()
}
}
// https://html.spec.whatwg.org/multipage/scripting.html
var runScriptTypes = [
'application/javascript',
'application/ecmascript',
'application/x-ecmascript',
'application/x-javascript',
'text/ecmascript',
'text/javascript',
'text/javascript1.0',
'text/javascript1.1',
'text/javascript1.2',
'text/javascript1.3',
'text/javascript1.4',
'text/javascript1.5',
'text/jscript',
'text/livescript',
'text/x-ecmascript',
'text/x-javascript'
]
function runScripts ($container) {
// get scripts tags from a node
var $scripts = $container.querySelectorAll('script')
var runList = []
var typeAttr
[].forEach.call($scripts, function ($script) {
typeAttr = $script.getAttribute('type')
// only run script tags without the type attribute
// or with a javascript mime attribute value
if (!typeAttr || runScriptTypes.indexOf(typeAttr) !== -1) {
runList.push(function (callback) {
insertScript($script, callback)
})
}
})
// insert the script tags sequentially
// to preserve execution order
seq(runList, scriptsDone)
}
$(document).ready(function()
{
var htmlContent = `<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
<script type="text/javascript">
new TradingView.widget({
"width": 500,
"height": 400,
"symbol": "BINANCE:AMBETH",
"interval": "60",
"timezone": "Etc/UTC",
"theme": "Dark",
"style": "1",
"locale": "en",
"toolbar_bg": "#f1f3f6",
"enable_publishing": false,
"allow_symbol_change": true,
"hideideas": true
});
</script>`;
var $container = document.querySelector('#tvTest');
$container.innerHTML = htmlContent;
runScripts($container);
});
</script>
</body>
</html>
If I run that, the chart displays and just underneath it, I see the code var $container = document.querySelector('#tvTest'); $container.innerHTML = htmlContent; runScripts($container); }); as text in the DOM. How can I get it to render the chart without printing any code to the DOM?
By default this trading view library appends to the body. You can override that by passing "container_id" property. Here is a simplified example of your code:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://s3.tradingview.com/tv.js"></script>
</head>
<body>
<div id="tvTest"></div>
<script type="text/javascript">
new TradingView.widget({
"container_id": "tvTest", // THIS IS THE LINE I ADDED
"width": 500,
"height": 400,
"symbol": "BINANCE:AMBETH",
"interval": "60",
"timezone": "Etc/UTC",
"theme": "Dark",
"style": "1",
"locale": "en",
"toolbar_bg": "#f1f3f6",
"enable_publishing": false,
"allow_symbol_change": true,
"hideideas": true
});
</script>
</body>
</html>
You need to escape the <\/script> inside of the string template
I have a function that creates an instance of DataTables and for some reason it initializes n-times after destruction. I only noticed this because I add custom fields on init and they were multiplying. I could prevent that but that only deals with the symptom.
To clarify, after I "destroy: the instance and reinitialize it, to change the data source, if it's the second time it initializes twice. Three times if it's the 3rd time, etc.
I speculate that the table variable is part of the closure formed by the function because even if I set table = null the same thing happens.
How can I prevent this?
DataTables Function
/*Create a DataTable on tableElementID using pageUrl as the source*/
function ajaxLoadTable ( pageUrl, tableElementID ) {
window.table = $(tableElementID)
.on( 'init.dt', function () {
//The success function is used internally so it should NOT be overwritten, have to listen for this event instead
//Add our custom fields _length refers to an element generated datatables
if ( additionalElements.saveButton ) {
$(tableElementID + '_length').after('<div class="dataTables_filter"><button>Save All Edits</button></div>');
}
if ( additionalElements.selectState ) {
$(tableElementID + '_length').after('<div class="dataTables_filter"><label>Project State: <select name="projectState" style="width:auto;"><option>Select ...</option><option value="Active">Active</option><option value="Historical">Historical</option></select></label></div>');
}
if ( additionalElements.searchBox ) {
$(tableElementID + '_length').after('<div class="dataTables_filter"><label>Search:<input type="search" id="customSearch" style="width:auto;"></label></div>');
}
})
.DataTable({
"processing": true,
"serverSide": true,
"ajax":{
type: "POST",
url: pageUrl,
data: function ( additionalData ) {
$('.serverData').each( function( index, element ){
if( element.nodeName === "SELECT"){
additionalData[element.name.toUpperCase()] = element.options[element.selectedIndex].value;
return true; //return true is equivalent to continue for $.each
}
additionalData[element.name.toUpperCase()] = element.value;
});
},
dataType: "json"
},
"pageLength": 4,
"lengthMenu": [ 4, 8, 12, 16, 24 ],
"searchDelay": 1500,
"columnDefs":
{ "targets": 0,
"orderable": false,
"data": {
"_": "display"
}
}
});
}
Destruction Function
/*Load the selected project state*/
$('html').on( 'change' , '[name=projectState]' ,function(){
var currentState = $('option:selected', this).val();
$('#projectState').val(currentState);
//Remove the old table records and the datatables. Order matters, otherwise there is unsual behavior.
if( $.fn.DataTable.isDataTable( '#searchTable' ) ) {
window.table.destroy();
window.table = null;
}
$('.projectStateText').html( currentState );
//Get the new table records
ajaxLoadTable( *some undisclosed URL*, '#searchTable');
});