I have a dashboard that is displayed on the main screen when the user logs on.
Initially I have two graphics, one of lines and one of bars. The problem is that the graphical queries are a bit heavy and take about 30 seconds to load.
I already did optimization of the queries and they improved a little, the problem is that when wanting to access other options of the system does not leave me, because I have to wait until the graphs are loaded.
It is difficult for the user to wait until the graphs load, I would like to know if there is a way that other system options can be accessed, regardless of the graphs being loaded.
The code I have is the following:
/*
* You get the urls that the user has access to in the dashboard. This is executed * when the user is on the main screen.
*/
$.getJSON(getBaseUri() + 'dashboard/index', function(data) {
var datas = data['return'];
if(datas == ""){
$productivities.removeClass('chartID');
$productions.removeClass('chartID');
$combox.addClass('hidden');
}
for (var i in datas) {
receiveData({
div : datas[i].div,
title : datas[i].privilege,
sign : datas[i].sign,
iconClass : datas[i].class,
id : datas[i].id,
label : datas[i].label,
xaxis : datas[i].xaxis,
yaxis : datas[i].yaxis,
background: datas[i].background,
url : datas[i].route,
type : datas[i].type
});
}
});
/*
* The function receiveData () receives the parameters of the dashboard that the user has access and brings the data of each url consulted, and according to the type of graph the function is executed.
*/
function receiveData(param) {
$.ajax({
url: param.url,
method: 'GET',
dataType: 'json',
success: function(data){
var datas = data['return'];
if(param.type === "LineChart"){
lineChart({
data : datas,
div : param.div,
title: param.title,
url : param.url
});
$loaderProduction.addClass("hidden");
}
if (param.type === "BarChart") {
barCharts({
data : datas,
div : param.div,
title : param.title,
url : param.url,
label : param.label,
xaxis : param.xaxis,
yaxis : param.yaxis,
background: param.background
});
$loaderProductivity.addClass('hidden');
}
if (param.type === "Indicator") {
indicatorsChart({
data : datas,
div : param.div,
title : param.title,
icon : param.sign,
class : param.iconClass,
idDash: param.id
});
}
if (param.type === "Sowing") {
sowingIndicator({
data : datas,
div : param.div,
title : param.title,
idDash: param.id
});
}
},
error: function(error){
console.log(error);
}
});
}
Basically these are the functions I'm using to display dashboard graphs.
I think that this case I need to use WebWorkers, but I donĀ“t know how to use it
Right now you're making an AJAX call for every iteration of your for...in loop. Depending on the number of iterations this could mean a huge overhead, although it is best to reduce the number of round trips to the server in all cases.
You could try to modify both the front- and backend logic to combine multiple datasets into one request instead of one dataset per request and multiple requests.
Other than this there are no obvious bottlenecks in your code, so if the slowness persists the problem is most likely with your backend code.
Of course you got some options:
go through a Single Page Application, i.e. make your app in the way that, when user navigate other application options, the execution remains always in the same page. It may be heavy, exspecially if your application is already structured. Angular (or AngularJs), as other frontend frameworks, do this for you in an automated way.
let execute your loading js in an iframe, old and dirty but it works. When finished send a notification message in the main page,
Anyway you should optimize your code too, reducing number of calls from n (one for each element in datas) to 1.
Related
In my Rails I have two models, both with a method giving JSON back. One model is rent.rb the other is buy.rb. I also have a map.js, which will show a map with different pins, depending on the model, basically. So one map shows pins with apartments to rent, another - with apartments to buy. So, now I need a tooltip with a different information, depending on buy / rent. So I defined this part (as it's not coming from database), for rent.rb:
information {
...
type: "rent"
}
and for buy.rb with :
information {
...
type: "buy"
}
So now, in my map.js I wish to find out type and depending on what I get show the right information
(type == "rent") ? (show information for rent) : (type == "buy") ? (show information for buy)
But somehow JS won't access this value and won't customize information shown. Unfortunatelly I can't show the code, as it's for work. But maybe I am just using the wrong syntax here ...
You cannot join conditional operators like way you showed. Also you cannot have a function between ? and :
It will be better to do something like this
var rentInformation="some value";
var buyInformation="some other value";
var displayInformation = (type == "rent") ? rentInformation : buyInformation;
somePopupFunction(displayInformation);
I'm building an app with many different datasets. Locations, customers, ratings etc...
Throughout the app there are popups and dynamically filled modules, dropdowns etc... At the moment my method is to attach "data-id" as an attribute to any buttons that create dynamic content then run ajax functions using the attribute to get content for the popup.
I'm assuming this is the correct thing to do for large modules that require a lot of dynamic data, but take the below example.
I have a list of locations, when the user clicks (add link) I'd like the popup module to have the title 'Adding link to [location name]'. Would I really need to create an ajax function simply to fill in the name of the location from the database? I could get it from the DOM but that seems silly because most of the popups require data that isn't in the dom.
Basically, my question is; What is the easiest way to get basic data from the database in javascript?
Here's an example of what I have for a whole bunch of buttons with various modules and titles:
$('body').on('click','.add_board_to_loc',function(){
var id = $(this).attr('data-id');
let getLocation = function(id){
$.ajax({
type: 'POST',
url: 'includes/ajax.php',
data: {
action: 'getLocation',
loc_id: id
},
success: function(data){
$('#add_link_modal_title').text(data['location_name']);
}
});
}
$('#addBoardModal').modal('show');
});
I would split this code into several layers.
One layer can be a transport layer. On transport layer you make ajax request and process errors.
The next layer can be a general layer, here you pass params as table, where statmets and so on. Anyone will be able to access this data through api so be carful with permissions.
And the last layer can be Buisness layer where you request things like: get Location.
I have a pretty standard jqGrid using free-jqGrid 4.13 with loadonce: true;
What I want to do is something I came across a couple of times:
I want to save the state of the grid (without the data);
that means the settings (number of current page for example), filters the user applied and also the text in the filter-toolbar.
and then load and apply this data to the grid later.
jqGrid is such a powerful tool, but stuff like this seems such a pain to accomplish.
after hours and hours of work i came up with a complicated solution; it is working, but it's not a nice solution imho:
1) saving jqGrid filter-data (locally):
// in this example i am using the "window unload" event,
// because i want the filters to be saved once you leave the page:
$(window).on("unload", function(){
// i have to access the colModel in order to get the names of my columns
// which i need to get the values of the filter-toolbar textboxes later:
var col_arr = $("#list").jqGrid("getGridParam", "colModel");
// my own array to save the toolbar data:
var toolbar_search_array = [];
for(var i = 0, max = col_arr.length; i < max; i++)
{
// only saving the data when colModel's "search" is set to true
// and value of the filterToolbar textbox got a length
// the ID of the filterToolbar text-input is "gs_" + colModel.name
if(col_arr[i].search && $("#gs_" + col_arr[i].name).val().length)
{
toolbar_search_array.push({ name: col_arr[i].name, value: $("#gs_" + col_arr[i].name).val() });
}
}
// putting everything into one object
var pref = {
// 1. toolbar filter data - used to fill out the text-inputs accordingly
toolbar : toolbar_search_array,
// 2. postData - contains data for the actual filtering
post : $("#list").jqGrid("getGridParam", "postData"),
// 3. settings - this data is also included in postData - but doesn't get applied
// when using 'setGridParam'
sortname: $('#list').jqGrid('getGridParam', 'sortname'),
sortorder: $('#list').jqGrid('getGridParam', 'sortorder'),
page: $('#list').jqGrid('getGridParam', 'page'),
rowNum: $('#list').jqGrid('getGridParam', 'rowNum')
};
//saving in localStorage
localStorage.setItem("list", JSON.stringify( pref ));
});
2) loading and applying jqGrid filter-data:
for use in a document-ready closure, for example
var localsave = JSON.parse(localStorage.getItem("list"));
// these settings are also saved in postData,
// but they don't get applied to the grid when setting the postData:
$('#list').jqGrid('setGridParam', {
sortname: localsave.sortname,
sortorder: localsave.sortorder,
page: localsave.page,
rowNum: localsave.rowNum
});
// this applies the filtering itself and reloads the grid.
// it's important that you don't forget the "search : true" part:
$("#list").jqGrid("setGridParam", {
search : true,
postData : localsave.post
}).trigger("reloadGrid");
// this is loading the text into the filterToolbar
// from the array of objects i created:
console.log(localsave.toolbar);
for(i = 0, max = localsave.toolbar.length; i < max; i++)
{
$("#gs_" + localsave.toolbar[i].name).val( localsave.toolbar[i].value );
}
it seems strange to me that postData contains all the data necessary but it's impossible to apply this data; at least as far as i know.
my question(s):
is it really this inconvenient to apply all the filter- and paging-data or am i missing something?
why can't this be as simple as:
// saving:
var my_save = $("#list").jqGrid("getGridParam", "postData");
// loading:
$("#list").jqGrid("setGridParam", {
search : true,
postData : my_save
}).trigger("reloadGrid");
?
thank you for any help and/or suggestions!
The answer with the demo provides an example of the implementation. The method refreshSerchingToolbar is relatively long. On the other side the code is still not full. It's not restore the state of operands, if the option searchOperators: true is used. I wanted to rewrite many parts of the filterToolbar method to make easier the saving/restoring of the the state of the filter toolbar or refreshing based on the changed postData. It's just the problem of the time and nothing more. What you describe is close to the feature forceClientSorting: true, which was difficult to implement in original code of jqGrid 4.7, but it was easy to implement after I rewrote large part of processing of the server response. The same problem is with the code of filterToolbar. The changes of filterToolbar is still in my TODO list and I'll made the corresponding changes soon.
For days now I've been searching for an explanation on how to do this right. I tried to find a decent tutorial for either JS or Ajax, but all the one's I found were rather basic and didn't go deep enough. When I want to solve a problem with Python, I go to Python.org, for PHP I use php.net, but I haven't found a decent ressource for JS and Ajax yet. Any tips are appreciated.
So I'll try to explain what I did. I have a rather simple page, it has a div on the left and a div on the right. Within the left one, a tree is rendered, representing a registry, within the right one, values are displayed and edited.
The tree is stored in a global variable. I have read all the warnings about using global variables, but I think it's justified in this case. Whenever this page is accessed, the tree is displayed.
var tree;
...
function initTree(treeRootConfig) {
return new Ext.tree.TreePanel({
renderTo : 'tree'
,width : 500
,height : 498
,autoScroll : true
,resizable : true
,useArrows : false
,animate : true
,loader : {
dataUrl : urls.urlLoadTree,
listeners: { beforeload : function(treeLoader, node) {} }
},listeners: {
click : function(node, e) { valueGrid.load(node.id); }
,contextmenu : function(node, e) {
cmPath.node = node;
cmPath.showAt(e.getXY());
},
},root : treeRootConfig
});
}
There is a textfield which holds a textual represantation of the path, that the tree is displaying.
function initTextfieldPath() {
return new Ext.form.TextField({
fieldClass : 'textfieldPath',
applyTo : 'path',
enableKeyEvents: 'true',
listeners : { change : function() { refreshTree(); } },
});
}
Every node is a "folder" in the path. Now when a new node is accessed, the value of the textfield is modified accordingly. This is working fine. When a new path is entered into the textfield, I want the tree to be reloaded to show the nodes. The path is parsed and and then I use Ajax to get the nodes out of the database. This works to the point, that I get the string represantation of the node, encode via JSON and try to set the node as new root for the tree.
function refreshTree() {
Ext.Ajax.request({
url : urls.DirectAccess
,scope : tree
,params : { path: textfieldPath.getValue() }
,success: function(response, opts) {
alert(response.responseText);
tree.setRootNode(Ext.util.JSON.decode(response.responseText)); }
,failure: function(response, opts) { noConnection(response); }
});
}
As you can imagine, the alert is only in there for testing purposes. When the server request is done, it displays the string represantation of the node that was fetched from the database. Just setting this node to the tree is something I cannot get done.
The ext license that we have is for Ext 3.4.0.
The problem was something else entirely. I thought it was not possible to communicate with a variable declared outside of an Ajax statement from within the Ajax statement. Turns out I had a '[' and a ']' too much. My target was expecting something like this '' but I gave it an array with only one member '[]'. It is kinda weird though, because in other cases Ext will only accept nodes as an array, no matter how many there are.
I've used the webOS Ares tool to create a relatively simple App. It displays an image and underneath the image are two labels. One is static, and the other label should be updated with new information by tapping the image.
When I tap the image, I wish to obtain a JSON object via a URL (http://jonathanstark.com/card/api/latest). The typcial JSON that is returned looks like this:
{"balance":{"amount":"0","amount_formatted":"$0.00","balance_id":"28087","created_at":"2011-08-09T12:17:02-0700","message":"My balance is $0.00 as of Aug 9th at 3:17pm EDT (America\/New_York)"}}
I want to parse the JSON's "amount_formatted" field and assign the result to the dynamic label (called cardBalance in main-chrome.js). I know that the JSON should return a single object, per the API.
If that goes well, I will create an additional label and convert/assign the "created_at" field to an additional label, but I want to walk before I run.
I'm having some trouble using AJAX to get the JSON, parse the JSON, and assign a string to one of the labels.
After I get this working, I plan to see if I can load this result on the application's load instead of first requiring the user to tap.
So far, this is my code in the main-assistant.js file. jCard is the image.
Code:
function MainAssistant(argFromPusher) {}
MainAssistant.prototype = {
setup: function() {
Ares.setupSceneAssistant(this);
},
cleanup: function() {
Ares.cleanupSceneAssistant(this);
},
giveCoffeeTap: function(inSender, event) {
window.location = "http://jonathanstark.com/card/#give-a-coffee";
},
jcardImageTap: function(inSender, event) {
//get "amount_formatted" in JSON from http://jonathanstark.com/card/api/latest
//and assign it to the "updatedBalance" label.
// I need to use Ajax.Request here.
Mojo.Log.info("Requesting latest card balance from Jonathan's Card");
var balanceRequest = new Ajax.Request("http://jonathanstark.com/card/api/latest", {
method: 'get',
evalJSON: 'false',
onSuccess: this.balanceRequestSuccess.bind(this),
onFailure: this.balanceRequestFailure.bind(this)
});
//After I can get the balance working, also get "created_at", parse it, and reformat it in the local time prefs.
},
//Test
balanceRequestSuccess: function(balanceResponse) {
//Chrome says that the page is returning X-JSON.
balanceJSON = balanceResponse.headerJSON;
var balanceAmtFromWeb = balanceJSON.getElementsByTagName("amount_formatted");
Mojo.Log.info(balanceAmtFromWeb[0]);
//The label I wish to update is named "updatedBalance" in main-chrome.js
updatedBalance.label = balanceAmtFromWeb[0];
},
balanceRequestFailure: function(balanceResponse) {
Mojo.Log.info("Failed to get the card balance: " + balanceResponse.getAllHeaders());
Mojo.Log.info(balanceResponse.responseText);
Mojo.Controller.errorDialog("Failed to load the latest card balance.");
},
//End test
btnGiveCoffeeTap: function(inSender, event) {
window.location = "http://jonathanstark.com/card/#give-a-coffee";
}
};
Here is a screenshot of the application running in the Chrome browser:
In the browser, I get some additional errors that weren't present in the Ares log viewer:
XMLHttpRequest cannot load http://jonathanstark.com/card/api/latest. Origin https://ares.palm.com is not allowed by Access-Control-Allow-Origin.
and
Refused to get unsafe header "X-JSON"
Any assistance is appreciated.
Ajax is the right tool for the job. Since webOS comes packaged with the Prototype library, try using it's Ajax.Request function to do the job. To see some examples of it, you can check out the source code to a webOS app I wrote, Plogger, that accesses Blogger on webOS using Ajax calls. In particular, the source for my post-list-assistant is probably the cleanest to look at to get the idea.
Ajax is pretty much the way you want to get data, even if it sometimes feels like overkill, since it's one of the few ways you can get asynchronous behavior in JavaScript. Otherwise you'd end up with code that hangs the interface while waiting on a response from a server (JavaScript is single threaded).