Im trying to remove all the rows in jquery data tables when new data arrives in order to fill with new rows.
But the problem is despite calling clear function it simply add the result rows to previous rows.
How to clear the rows in jquery data tables . Following is the code
var table = $("#editable");
var tp = table.DataTable({
"paging": true,
"createdRow": function (row, data, index) {
$compile(row)($scope);
}
});
//more code here ajax call only from angular post
if (result != null) {
$scope.reviews = result;
$window.toastr["success"]("Loaded Successfully !", "Recent Reviews");
tp.clear().draw();
for (var i = 0; i < $scope.reviews.length; i++) {
var id = $scope.reviews[i].ID;
var checked = $scope.reviews[i].Enabled == "1" ? true : false;
tp.row.add([
$scope.reviews[i].ID,
$scope.reviews[i].AddedOn,
$scope.reviews[i].Company.Name,
$scope.reviews[i].Rate,
$scope.reviews[i].User.Name,
$scope.reviews[i].Description,
"<div class='switch'><div class='onoffswitch'><input ng-model='$scope.reviews[" + i + "].Enabled' ng-click='ChangeReviewPublishStatus(" + $scope.reviews[i].ID + ")' ng-checked='" + checked + "' class='onoffswitch-checkbox' id= 'stat" + id + "' type= 'checkbox'><label class='onoffswitch-label' for='stat" + id + "'><span class='onoffswitch-inner'></span><span class='onoffswitch-switch'></span></label></div></div>"
]).draw();
}
}
I would place that piece of code inside a $timeout. Both dataTables and angular wants to manipulate the DOM - angular wins the battle and by that dataTables never get a chance to finish its business. A $timeout will force the dataTables clear() and data population into the next digest, and ensure it is actually executed.
$timeout(function() {
tp.clear().draw();
for (var i = 0; i < $scope.reviews.length; i++) {
var id = $scope.reviews[i].ID;
var checked = $scope.reviews[i].Enabled == "1" ? true : false;
tp.row.add([
$scope.reviews[i].ID,
$scope.reviews[i].AddedOn,
//etc
]).draw();
}
});
Well, that is at least my theory - cannot replicate your scenario in full - let me know if it makes any difference.
Related
I have a small web application setup on google sheets which have almost 10k rows and 9 columns.
currently, I took all the data from Google sheets and putting it on an HTML Table and Then I have few inputs through which I filter the table using event listener.
As you could have guessed already it is taking too much of memory since it is on the client side and loading and filtering are slow.
Earlier I was having an interactive filter with an event listener on each key press I have changed it to "Enter" key since it was taking too much time for first two or three characters.
Script on index.HTML
<script>
//global variables
var rows = []; //rows
var currentOrder = 'ascending'; //sorting order
var inputFilter = document.getElementById('partNum'); //input field for ItemName
var inputFilterDes = document.getElementById('partDes'); //input field for description
var nameTable = document.getElementById('table'); //html table
//load function being used for pulling data from google sheet
function load() {
//calling get data function with array and filter array inside
google.script.run
.withSuccessHandler(function(response) {
//response function will be separted into column values
rows = response.map(function(element) {
//all the elements converted into columns
return {
itemCode: element[0],
itemName: element[1],
itemDescription: element[2],
inStock: element[3],
committed: element[4],
onOrder: element[5],
available: element[6],
warehouse: element[7]
};
});
//rows mapping finished
renderTableRows(rows);
//initial load finished here
//filter section starts
//Item name filter
inputFilter.addEventListener('keyup', function(evt) {
if (evt.keyCode === 13) {
// Cancel the default action, if needed
evt.preventDefault();
var filter = evt.target.value.toString().toLowerCase();
}
var filteredArray = rows.filter(function(row) {
return row.itemName.toString().toLowerCase().includes(filter);
});
renderTableRows(filteredArray);
});
//description filter
inputFilterDes.addEventListener('keyup', function(evt) {
if (evt.keyCode === 13) {
// Cancel the default action, if needed
evt.preventDefault();
var filterDes = evt.target.value.toString().toLowerCase();
}
var filteredArrayDes = rows.filter(function(row) {
return row.itemDescription.toString().toLowerCase().includes(filterDes);
});
renderTableRows(filteredArrayDes);
});
})
.getData("SAP"); //pull data from defined sheet
}
//retruing array values in HTML table and placing them in page
function renderTableRows(arr) {
nameTable.innerHTML = arr.map(function(row) {
return '<tr>' +
'<td>' + row.itemCode + '</td>' + '<td>' + row.itemName + '</td>' +
'<td>' + row.itemDescription + '</td>' + '<td>' + row.inStock + '</td>' +
'<td>' + row.committed + '</td>' + '<td>' + row.onOrder + '</td>' + '<td>' +
row.available + '</td>' + '<td>' + row.warehouse + '</td>' + '</tr>';
}).join('');
};
load();
</script>
My code.gs
function doGet(e) {
if (!e.parameter.page) {
// When no specific page requested, return "home page"
return HtmlService.createTemplateFromFile('index').evaluate().setTitle("My Web App");
}
// else, use page parameter to pick an html file from the script
return HtmlService.createTemplateFromFile(e.parameter['page']).evaluate();
}
function getData(sheetName) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
return sheet.getSheetValues(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn());
}
function getScriptUrl() {
var url = ScriptApp.getService().getUrl();
return url;
}
I tried to move it on the server side using the following but failed
EDIT : Removed my Server side atempt code as i think it will create confusions.
I'm not a coder so please excuse me if it sounds silly or unorganized.
SO I am trying to increase the speed and for this, I want to Move scripts server-side however I am not fully confident it will help me or not so I am open to any other methods to improve the speed of application.
Apart from moving map() to async server call, you can optimize the client-side code by creating an ordering function that works over DOM. Currently, each time a keyup event is fired, you rerender the whole table (10K iterations each time if I understand the Spreadsheet size correctly).
First, access your table's children (assuming it is constructed with both <thead> and <tbody> elements: var collection = nameTable.children.item(1).children (returns HtmlCollection of all the rows).
Second, iterate over rows and hide ones that do not satisfy the filtering criteria with hidden property (or create and toggle a CSS class instead):
for(var i=0; i<collection.length; i++) {
var row = collection.item(i);
var cells = row.children;
var itemName = cells.item(1).textContent; //access item name (0-based);
var itemDesc = cells.item(2).textContent; //access item description (0-based);
var complies = itemName==='' && itemDesc===''; //any criteria here;
if( complies ) {
row.hidden = false;
}else {
row.hidden = true;
}
}
Third, move the renderTableRows() function to server async call as well, since you render your table rows with string concatenation (instead of createElement() on document) with htmlString.
Useful links
Document Object Model (DOM) reference;
Server-client communication in GAS reference;
Best practices for working with HtmlService;
I have a website with a list of json objects arranged something like this:
[
{
"a": true or false,
"b": "information",
"c": "information",
"d": "information",
"e": "information"
},
...
]
The idea of this code is to print out all the objects on a table and have a checkbox which filters out the false objects out when needed. The site is supposed to just have the the table with unfiltered object on there, but after I added the checkbox event listener the full table list disappeared. When I check the checkbox I get the filtered objects and it keeps adding more and more of the same filtered content on the bottom of the table if I keep re-clicking it.
What am I doing wrong here? Here is the code I have:
var stuff = document.getElementById("stuff-info");
var ourRequest = new XMLHttpRequest();
ourRequest.open('GET', 'url');
ourRequest.onload = function() {
var ourData = JSON.parse(ourRequest.responseText);
renderHTML(ourData);
};
ourRequest.send();
function renderHTML(data) {
var htmlString = "";
var filteredData = data.filter(function(element) {
return element.a
});
var checkbox = document.querySelector("input[name=hide]");
checkbox.addEventListener('change', function() {
if (this.checked) {
for (i = 0; i < filteredData.length; i++) {
htmlString += "<table><tr><td>" + filteredData[i].b + "</td><td>" + filteredData[i].c + "</td><td>" + filteredData[i].d + "</td><td>" + filteredData[i].e + "</td></tr>"
}
} else {
for (i = 0; i < data.length; i++) {
htmlString += "<table><tr><td>" + data[i].b + "</td><td>" + data[i].c + "</td><td>" + data[i].d + "</td><td>" + data[i].e + "</td></tr>"
}
}
stuff.insertAdjacentHTML('beforeend', htmlString);
});
}
Might be easier to filter with CSS selector:
#filter:checked ~ table .filter { display: none }
<input type=checkbox id=filter> Filter
<table border=1>
<tr class=filter><td>1</td><td>a</td></tr>
<tr><td>2</td><td>b</td></tr>
<tr class=filter><td>3</td><td>c</td></tr>
<tr><td>4</td><td>d</td></tr>
</table>
after I added the checkbox event listener the full table list disappeared.
All of your logic for deciding what to render is trapped inside your onchange event, so nothing will be drawn until a checkbox is changed.
When I check the checkbox I get the filtered objects and it keeps adding more and more of the same filtered.
All of your html strings are generated with += against the original htmlString variable trapped in the closure. So yeah, it will just keep adding more and more rows. You are also inserting the udated strings into the dom without removing the old table(s), so this will be exponential growth.
I think there is a great case here for higher order functions instead of for loops, you can use the map array method to transform each item in the array into a string, instead of manually iterating. This is cleaner and more maintainable.
Notice that now that the rendering logic is not mixed together with the event logic, it would be much easier to reuse the render function with some different data or different events. It's also somewhat trivial to add more transformations or filters.
const ourRequest = new XMLHttpRequest();
ourRequest.onload = function() {
const ourData = JSON.parse(ourRequest.responseText);
initialRender(ourData);
};
ourRequest.open('GET', 'url');
ourRequest.send();
function filterAll() { return true; }
function filterA() { return element.a; }
function toRowString(item) {
return `
<tr>
<td>${item.a}</td>
<td>${item.b}</td>
<td>${item.c}</td>
<td>${item.d}</td>
<td>${item.e}</td>
</tr>`;
}
function renderTable(predicate, parentElement, data){
const rows = data
.filter(predicate)
.map(toRowString);
parentElement.innerHTML = `<table>${rows}</table>`;
}
function initialRender(data) {
const stuff = document.getElementById("stuff-info");
const checkbox = document.querySelector("input[name=hide]");
renderTable(filterAll, stuff, data);
checkbox.addEventListener('change', function(event) {
renderTable(
event.target.checked ? filterA : filterAll,
stuff,
data
);
}
}
I got myself stuck in a situation. I was coding a Wikipedia search tool for a personal practice project, but I've ran into a small error. When a user enters a word into the search bar, the input will be store into the data parameter of $.getJSON, then the response will return a array of title and description objects based on the word entered in the search bar. The $.getJSON function will display 5 sets of a title and it's description in a list format in the designated HTML. Simple enough, but the issue is the $.getJSON function will display the wording "undefined", then continue to display the required set of titles and descriptions. Below I have listed my JS coding for your viewing. Also, the full code can be viewed at my codepen.
Can anyone give me a heads up of what might be the issue. As $.getJSON is asynchronous, that might be the issue, but I can't quite put my finger on it. Thanks in advance!
$("#search-word").on("keydown", function(event) {
if(event.keyCode == 13) {
event.preventDefault();
var input = {search: $(this).val()};
getWikiInfo(input);
}
});//search end
function getWikiInfo(input) {
var wikipApi = "https://en.wikipedia.org/w/api.php?format=json&action=opensearch&callback=?";
var getWikipHtml = function(response) {
console.log(response);
var wikipHtml;
for(var i = 1; i < 6; i++) {
wikipHtml += '<div class="list"><h3>' + response[1][i] + '</h3><p>' + response[2][i] + '</p></div>';
}
$("#list-container").html(wikipHtml);
}
$.getJSON(wikipApi, input, getWikipHtml);
}//getWikiInfo end
You need to do minor change. Initialize wikipHtml to empty string and check if the response[1][i] is not undefined. Below is the updated code:
var wikipHtml = '';
for (var i = 1; i < 6; i++) {
if (response[1][i] !== undefined)
wikipHtml += '<div class="list"><h3>' + response[1][i] + '</h3><p>' + response[2][i] + '</p></div>';
}
This is happening because you are not initializing wikipHtml before appending to it, but I would strongly advise that you use proper DOM manipulation instead of building your HTML using string concatenation:
$("#search-word").on("keydown", function(event) {
if (event.keyCode == 13) {
event.preventDefault();
var input = {
search: $(this).val()
};
getWikiInfo(input);
}
}); //search end
function getWikiInfo(input) {
var wikipApi = "https://en.wikipedia.org/w/api.php?format=json&action=opensearch&callback=?";
var getWikipHtml = function(response) {
var content = [0, 1, 2, 3, 4, 5].map(function(i) {
return $('<div class="list">')
.append($('<h3>').text(response[1][i]))
.append($('<p>').text(response[2][i]));
});
$("#list-container").html(content);
}
$.getJSON(wikipApi, input, getWikipHtml);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id='search-word' type='text' />
<div id='list-container'></div>
What I'm trying to do is get one of my drop down list to change its contents whenever the selected item in another one cahnges. I have this code in my aspx file:
function ModifyDDLItems(id1, id2)
{
var ddlcontrolShown = document.getElementById(id1);
var ddlcontrolHidden = document.getElementById(id2);
if (ddlcontrolShown.options[ddlcontrolShown.selectedIndex].value == "DD1")
{
//Get number of items of hidden ddl
var length = ddlcontrolHidden.options.length;
//Clear items of shown ddl
ddlcontrolShown.options.length = 0;
//Add itmems of hidden ddl to shown ddl
for (i = 0; i < length; i++)
{
ddlcontrolShown.options.add
var newoption = document.createElement("option")
newoption.text = ddlcontrolHidden.options[i].text;
newoption.value = ddlcontrolHidden.options[i].text.value;
}
}
}
Now, i give it the front end ID's thru this:
protected void SetDD1ConfItems(GridViewRow gvRow, DataSet BaseConfItems)
{
DataView dvConfType = new DataView(BaseConfItems.Tables[0]);
DataSet dsTemp = BaseConfItems.Clone();
DropDownList ddlConfType2 = (DropDownList)form1.FindControl("ddlConfType2");
DropDownList ddlBA = (DropDownList)gvRow.FindControl("ddlBA");
DropDownList ddlConfType = (DropDownList)gvRow.FindControl("ddlConfType");
dvConfType.RowFilter = "ref_code = 'FAX' or ref_code = 'EEX' or ref_code = 'EPD'";
dsTemp.Tables.Clear();
dsTemp.Tables.Add(dvConfType.ToTable());
ddlConfType2.DataSource = dsTemp;
ddlConfType2.DataBind();
//ddlBA.Attributes["onchange"] = "function GetDDLD(" + ddlConfType.ClientID + ", " + ddlConfType2.ClientID + ") {ModifyDDLItems(id1, id2);}";
ddlBA.Attributes.Add("onchange", "ModifyDDLItems('" + ddlConfType.ClientID + "', '" + ddlConfType2.ClientID + "')");
}
When I run it, VS keeps on telling me that id1 and id2 are both null, it seems the id's aren't passed to the client properly.
I think you have code wrongly, the first mistake i found at a glance is,
You cannot find the controls inside gridview by using
gvRow.FindControl("ddlBA");
There may be multiple rows in GridView, so you have to find your controls in each Row as all of them will have different ClientIDs. First to try to replace the below code
gvRow.Rows[RowIndex].FindControl("ControlID");
ALso, it should be written in the some kind of loop in order to find the RowIndex value of the Grid.
Describe your exact requirement in brief. So, that i can help you in writing the proper code.
I want create a web application that display a list of items. Suppose I have displayed a list view (say listobject1) of 3 items. when clicked on any of them I get new list view (say listobject2) which its value is according to listobject1. When again I click one of them I get another view. Now when I click back button i want to go back to previous list view i.e. when I'm now on listobject2 and again when back button is pressed I want to show listobject1. Can anybody tell me how I can do this in JavaScript?
Edit
I'm still study about the stuff but I can't solve this problem yet. In order to clarify my problem now, here's my code:
$(document).ready(function() {
$("#result").hide();
$("input[name='indexsearch']").live("click", function() {
$("#result").show();
$("#result").empty();
loading_img();
var $textInput = $("[name='valueLiteral']").val();
$.getJSON("get_onto", {
"input" : $textInput
}, function(json) {
if(json.length > 0 ) {
var arrayPredicate = [];
var arrayObject = [];
var arraySubject = [];
$.each(json, function(index, value) {
arraySubject[index] = value.subject;
arrayPredicate[index] = value.predicate;
if(value.objectGeneral != null) {
arrayObject[index] = value.objectGeneral;
} else {
arrayObject[index] = value.objectLiteral;
}
}
);
var stmt = [];
//concat all related array into string (create triple statement)
$.each(arrayPredicate, function(k,v){
stmt[k] = "<span class='subject' id="+arraySubject[k]+">"
+ arraySubject[k] + "</span> " + " -> " + v + " : "+
//change object from text to be button form
"<button class = 'searchAgain-button' name = 'searchMore' \n\
value = " + arrayObject[k] + ">" + arrayObject[k] + "</button><br> <br>";
});
stmt = stmt.sort();
$.each(stmt, function(k,v){
$("#result").append(v);
});
} else {
var $noresult = "No Result : Please enter a query";
$("#result").append($noresult);
}
});
});
$("button").live("click", function() {
$("#result").show();
$("#result").empty();
loading_img();
var $textInput = $(this).attr("Value");
//var $textInput = "G53SQM";
$.getJSON("get_onto", {
"input" : $textInput
}, function(json) {
if(json.length > 0 ) {
var arrayPredicate = [];
var arrayObject = [];
var arraySubject = [];
$.each(json, function(index, value) {
arraySubject[index] = value.subject;
arrayPredicate[index] = value.predicate;
if(value.objectGeneral != null) {
arrayObject[index] = value.objectGeneral;
} else {
arrayObject[index] = value.objectLiteral;
}
}
);
var stmt = [];
var searchMore = "searchMore";
//concat all related array into string (create triple statement)
$.each(arrayPredicate, function(k,v){
stmt[k] = "<span class='subject' id="+arraySubject[k]+">" + arraySubject[k] + "</span> " + " -> " + v + " : "+ " <button class = 'searchAgain-button' name = " +searchMore + " value = " + arrayObject[k] + ">" + arrayObject[k] + "</button><br><br>";
});
stmt = stmt.sort();
$.each(stmt, function(k,v){
$("#result").append(v);
});
} else {
var $noresult = "No Result : Please enter a query";
$("#result").append($noresult);
}
});
});
At first, user only see one button name "valueLiteral". After user perform 1st search, the result is return in a form of JSON and eventually put in stmt[] to display, which at this state the second button was create as a clickable-result which will automatically take the value of result and do second search if user click the second button.
Now the problem is, I want to add a 3rd HTML button name "back" to make the web display the previous result in stmt[] if user click on the button.
Hope this helps in clarify the problems, I'm still doing a hard work on this stuff since I'm a newbie in JavaScript. Appreciate all helps.
This is what you want almost exactly the way you want it.
You'll have to use history.pushState to push these fake events into the history.
Alternatively, you can use location.hash to store the current object, and update the hash every time you display a new list. Then onhashchange find the hash and display the appropriate list.
See http://jsfiddle.net/cFwME/
var history=[new Array(),new Array()];
history[0].id="#back";
history[1].id="#next";
Array.prototype.last=function(){
return this[this.length-1];
}
$('#list>li:not(:first)').click(function(){
if(!history[0].length || history[0].last().html()!=$('#list').html()){
history[0].push($('#list').clone(true,true));
$(history[0].id).prop('disabled',false);
history[1].length=0;
$(history[1].id).prop('disabled',true);
}
$('#list>li:first').html('This is List '+$(this).index());
});
$('#back').click(getHistory(0));
$('#next').click(getHistory(1));
function getHistory(n){
return function(){
if(!history[n].length){return false;}
history[(n+1)%2].push($('#list').replaceWith(history[n].last()));
history[n].pop();
$(history[(n+1)%2].id).prop('disabled',false);
if(!history[n].length){$(history[n].id).prop('disabled',true);}
}
}
Check out jQuery BBQ - http://benalman.com/projects/jquery-bbq-plugin/