JS tree Loading time issue - javascript

I have implemented the Tree with js tree and here i'm getting lot of time(Around 1 min) when loading the tree..
I wanna to find the way to reduce the time and i have over 5000 nodes in my implementaion.
In my view
$("#tree").jstree({
checkbox: {
real_checkboxes: true,
real_checkboxes_names: function (n) { return [("check_" + (n[0].id || Math.ceil(Math.random() * 10000))), n[0].id] }
}, "plugins": ["themes", "html_data", "checkbox", "sort", "ui"]
}).bind('check_node.jstree', function (event, data) {
$('#SearchView').show();
}).delegate("a", "click",
function (event, data) {
event.preventDefault();
});
html for load js tree
<tbody>
<div id="tree">
<ul>
#HtmlHelpers.RenderjsTree(Model.CurrentNode)
</ul>
</div>
</tbody>
RenderjsTree will recursively call and load the tree nodes.. any way to reduce the time?

There are a couple of ways to approach this slow loading problem.
One way is to use the ajax method in the json_data plugin of jstree. Mike Tyka has given a pretty neat description of doing that here - http://www.miketyka.com/2012/10/lazy-loading-with-jstree-and-ajax/
Another way is via a simple javascript method - if you are open to using the v3 of jstree which is still in beta version. In my project I had around 2200 nodes and the json data came from the server side via a single ajax call in less than a second. But the json parsing took around 8-10 seconds till which the page just stopped responding. Jstree v3 has a method of getting the data of a node from a function when the node is opened. I used that method and the page now loads in just under 2 seconds.
function initiate_jstree() {
$("#tree_container").jstree({
"core": {
"data": getTree,
"themes":{
"icons":false
},
},
"plugins": [ "themes", "ui" ,"types"]
});
}
function makeTreeData(node){
if(node.original && node.original.actualData){
data=node.original.actualData;
}else{
data=gData;
}
treeData=[];
for(i=0;i<data.length;i++){
iter=data[i];
item={"text": iter.data};
if(iter.children){
item["children"]=true;
item["actualData"]=iter.children;
}
treeData.push(item);
}
return treeData;
}
var getTree = function (obj, cb) {
console.log("called");
data=makeTreeData(obj);
cb.call(this,
data);
}
initiate_jstree();
The gdata variable here is a global variable in which the data to be loaded is stored in json format.
Here is the code on jsfiddle - http://jsfiddle.net/Lge8C/

Related

jQuery text change from HTML range slider

I'm new at jQuery, so I expect there is an easy answer.
I have a JSON file with different text strings for different dates. I also have an html range slider that I uses the <datalist> tag to define specific dates on the slider. I have written a $.getJSON function that nests a $.each() function to pull the strings from the JSON file.
I need to write in the functionality to display different strings based on the slider position.
This is my jQuery:
var location = $('#state-dates')[0];
$(document).on('input', '#state-dates', function() {
if (location === 1911) {
$.getJSON('Arizona.json', function(inputOne){
$.each(inputOne.first, function(i, field){
$("#leg-control").html(field.legControl);
});
});
}
else if (location === 1943) {
$.getJSON('Arizona.json', function(inputTwo){
$.each(inputTwo.second, function(i, field){
$("#leg-control").html(field.legControl);
});
});
}
});
And my HTML:
<input type="range" min="1911" max="2013" class="bar" step=".1" list="date-list" value="1911" id="state-dates">
Is there a different jQuery method that I should be using to detect the change in the slider, and so display the new string? I also realize that I should probably use < or > instead of = since I want the same text to only change when it reaches a new defined position. Thank you!
EDIT
To help clarify, I'm adding in the relevant JSON and HTML.
JSON:
{
"first": [
{
"legControl": "Not recorded",
}],
"second": [
{
"leg-control": "Democratic",
}]
}
And the HTML for entering the text:
<div class="json-text">
<p class="fill-in" id="leg-control"></p>
</div>
I was able to work out a solution with a web developer friend of mine, so in case anyone else stumbles across this question, the solution I used:
//call the JSON file and store it as a variable called jdata
$.getJSON('Arizona.json', function(json) {
jdata = json;
});
var slider = $('#sliderValue')[0];
slider.oninput = function() {
var position = this.value;
var jrows = $(jdata).filter(function (i, n) {
return n.sYear <= position && n.eYear > position;
});
$("#year-start").html(jrows[0].jsonLineOne);
$("#year-end").html(jrows[0].jsonLineTwo);
};
Essentially this takes the slider input, and runs a return command to check the position. If it is within the bounds of one subsection of the JSON file, then that section is pulled. Hopefully this helps anyone else who comes across it in the future.

Ag-grid-Enterprise expand/collapse all row using button? Very slow crashing FF and Edge

I created a button to expand all the rows in ag-grid (Enterprise) having 150 rows in the grid. It is working fine in Chrome but it is showing an alert in the latest FF and Edge, saying the web page is making your browser slow. Any better approach to expand all the row? It is taking almost 10-15 second
HTML
<button (click)="expandAll(expand)">Expand/Collapse</button>
JavaScript
this.columnDefs = [
{
headerName: "",
field: "",
cellRenderer: "group",// for rendering cell
suppressMenu: true,
suppressSorting: true
}
]
// This is how I am creating fullrow width
this.gridOptions = <GridOptions>{
isFullWidthCell: function (rowNode) {
var rowIsNestedRow = rowNode.flower;
return rowIsNestedRow;
},
fullWidthCellRendererFramework: AgGridInventorRowComponent,
doesDataFlower: function (dataItem) {
return true;
}
public expandAll(value:boolean) {
if(value) {
this.gridOptions.api.forEachNode((node) =>{
node.setExpanded(true);
});
} else {
this.gridOptions.api.forEachNode((node) =>{
node.setExpanded(false);
});
}
}
As per the documentation:
Calling node.setExpanded() causes the grid to get redrawn. If you have many nodes you want to expand, then it is best to set node.expanded=true directly, and then call api.onGroupExpandedOrCollapsed() when finished to get the grid to redraw the grid again just once.
So i modified my code like below:
this.gridOptions.api.forEachNode(node => {
node.expanded = true;
});
this.gridOptions.api.onGroupExpandedOrCollapsed();
Ag-gridDocumentation page inside Group Api
I'm supposing that you are using the row grouping feature, and that you meant that there are 150 grouped rows that are able to be expanded.
Currently your code is getting executed for every single row of data... not just the ones that are able to be expanded. So supposing you have 50 rows or so of data in each group, your calling the setExpanded function 7500 times. You can limit this to just calling the setExpanded on the grouped rows by putting in a check before calling setExpanded:
public expandAll(value:boolean) {
this.gridOptions.api.forEachNode((node) =>{
if (node.group)
node.setExpanded(value);
});
}
testing it on this example, it took roughly 2 seconds for 110 row groups and 5 seconds for 511 row groups in firefox
The API has expandAll and collapseAll:
api.expandAll();
api.collapseAll();
Note that due to the crappy architecture of AG Grid the expansion state (along with row selection etc) is lost if the row data changes or the grid is re-mounted/re-rendered. You should use deltaRowDataMode but make sure you give your rows a unique ID to help prevent this (though this option of course can cause some hard to debug bugs which I have reported).
Also if you want to restore the user expansion in this case you have no choice but to iterate and expand/collapse individual nodes.
Also they don't seem to work on a master-detail (enterprise feature) grid...
I hope this would help, the performance seems to be fine. Took reference from -
https://github.com/ag-grid/ag-grid/issues/2179
But it is always better to check if the groups are present are not. That increases the performance and expanding is much much faster.
this.gridApi.forEachNode((node) => {
if(node?.group) {
node.setExpanded(true)
}
})
I have an update as a complete solution.
Please see this example that I created o Plnkr.
Basically you can use the following code blocks to expand and collapse the tree data on the grid:
At first, imports, definitions and assignments:
import { GridApi } from 'ag-grid-community';
then:
gridApi!: GridApi; // variable
constructor() {}
onGridReady(params: GridReadyEvent) {
this.gridApi = params.api;
// other codes...
}
expand() {
this.gridApi.expandAll();
}
collapse() {
this.gridApi.collapseAll();
}
However if you want to collapse and expand a specific node level groups, you can use the following examples:
collapse2ndLevel() {
this.gridApi.forEachNode((node) => {
if (node.level === 1) {
node.setExpanded(false);
}
});
}
expand2ndLevel() {
this.gridApi.forEachNode((node) => {
if (node.level < 2 && node.isExpandable) {
node.setExpanded(true);
}
});
}
collapse3rdLevel() {
this.gridApi.forEachNode((node) => {
if (node.level === 2 && node.isExpandable) {
node.setExpanded(false);
}
});
}
expand3rdLevel() {
this.gridApi.forEachNode((node) => {
if (node.level < 3 && node.isExpandable) {
node.setExpanded(true);
}
});
}
Please check out this example that I created o Plnkr.
I use server-side row model and single decision for me it is purgeServerSideCashe() after update data. https://www.ag-grid.com/archive/23.2.0/javascript-grid-server-side-model-grouping/#example-purging-caches. It closes all expanded rows

AppGyver Steroids Supersonic Views

I'm trying to get my head around switching views / passing views to another view.
I have an app that is calling in a kimono API, that's all setup with the supersonic background and looks fine. I have 1 string and 2 objects in the API. I have a page that is calling in the full list of events using a page called event:
{{ event.eventdescription }}
The Event#Index controller is:
angular
.module('event')
.controller("IndexController", function ($scope, Event, supersonic) {
$scope.events = null;
$scope.showSpinner = true;
Event.all().whenChanged( function (events) {
$scope.$apply( function () {
$scope.events = events;
$scope.showSpinner = false;
});
});
});
And all of that displays properly. The issue is when I click on one of those items shown which should go to the specific event I get nothing. And I'm sure I'm doing this wrong or don't understand enough about switching views. I've read many examples, but I'm not getting how it all goes together.
here is my event#show page. Very generic just trying to load any information at this point.
<div ng-controller="ShowController">
<super-navbar>
<super-navbar-title>
Show
</super-navbar-title>
</super-navbar>
<div class="padding">
{{ event.eventdescription }}
</div>
</div>
And the showcontroller:
angular
.module('event')
.controller("ShowController", function ($scope, Event, supersonic) {
$scope.events = null;
Event.all().whenChanged( function (events) {
$scope.$apply( function () {
});
});
});
And this always returns a blank page. When i check the log it says Undefined.undefined which i'm not sure what that means.
Any insight on this is greatly appreciated. In the appgyver docs I saw something called.
var view = new supersonic.ui.View("bananas#show");
supersonic.ui.layers.push(view);
But I'm not sure how to use this?
ANY insight is appreciated.
So, UPDATED I have:
here's the event#index i'm working with.
<div ng-controller="IndexController">
<super-navbar>
<super-navbar-title>
Event Index
</super-navbar-title>
</super-navbar>
<ul class="list" ng-hide="events.length == 0">
<super-navigate view-id="event#show" data-params-id="{{event.id}}" ng-repeat="event in events">
<li class="item item-icon-right">
<h2 ng-bind="event.EventTitles['text']"></h2>
<img ng-src="{{ event.HeadlineImages.src }}" width="100px" height="100px">
<p> {{ event.eventdescription }} </p>
<i class="icon super-ios7-arrow-right"></i>
</li>
</super-navigate>
</ul>
</div>
And the Index Controller
angular
.module('event')
.controller("IndexController", function ($scope, Event, supersonic) {
$scope.events = null;
Event.all().whenChanged( function (events) {
$scope.$apply( function () {
$scope.events = events;
});
});
});
The show html page.
<div ng-controller="ShowController">
<super-navbar>
<super-navbar-title>
Show
</super-navbar-title>
</super-navbar>
<div class="padding">
<p>
{{event.eventdescription}}
</p>
</div>
</div>
The ShowController
angular
.module('event')
.controller("ShowController", function ($scope, Event, supersonic) {
supersonic.ui.views.current.params.onValue( function (Event) {
$scope.events = event.id;
});
Event.find($scope.events).then( function (Event) {
$scope.$apply( function () {
$scope.event = Event;
});
});
});
And I also updated the structure.coffee as so
rootView:
location: "event#index"
preloads: [
{
id: "event#show"
}
{
id: "using-the-scanner"
location: "example#using-the-scanner"
}
]
Any help is appreciated.
It doesn't look like the data is being set in the your ShowController. I commented about this before. I think you need to pass the id of the event using <super-navigate> with a location property and a data-params-id or whatever you want the parameter name to be. Then in your ShowController you can access it with:
supersonic.ui.views.current.params.onValue( function (values) {
// values.nameOfPropertyPassedInCouldBeEventId
$scope.id = values.id;
});
Then you might be able to do something like this to access the Event by id:
Event.find($scope.id).then( function (theEvent) {
$scope.$apply( function () {
$scope.event = theEvent;
});
});
Now in your view where you have {{ event.eventdescription }} there should be some data.
And another piece for when the view is visible meaning every time you see that view page this will fire:
supersonic.ui.views.current.whenVisible( function () {
// your code for watching events
});
Ok, after a couple weeks of trying to get this working and although, I still haven't been able to get this to work yet.. I think I'm getting somewhere with this FINALLY... It seems the biggest problem here is using Kimono and AppGyver. The JSON file has been updated in Kimono using:
function transform(data) {
data.results.collection1 = data.results.collection1.map(function(o) {
o.eventdescription = {
text: o.eventdescription
}
return o;
});
return data;
}
This cleans up the JSON file exported/ coming in as API to App Gyver so that all parts are objects. ( I know, maybe not a big deal, but I just wanted to make this as clean as possible). To give you an idea of the before and after of using this script in the Kimono Modify Results box -->
BEFORE:
"EventTitles": {
"href": "http://",
"src": "http://.jpg",
"text": "Lorem Ipsum"
},
"HeadlineImages": {
"href": "http://",
"src": "http://.jpg",
"text": "Lorem Ipsum"
},
"eventdescription":"Lorem Ipsum"
},
which leaves eventdescription as a string rather than object and then the AFTER:
"EventTitles": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"HeadlineImages": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"eventdescription": {
"text": "TEXT"
},
So, after running this into Kimono as you can see all entries are "objects". And you'd use &kimmodify=1 AFTER the apikey in the link thusly:
https://www.kimonolabs.com/api/{indentifier}{apikey}&kimmodify=1
NEXT, as I was explained to by the AppGyver community one would pretty much need an "id" of sorts for each item in the JSON / API that's being created to be able to use the ShowController to create a reasonable/ feasible url string on the show.html.
Which should create something like /app/tier/showid=123456789 when going from the index to a specific entry view.
(You find the URLs by using the debug mode in AppGyver either via Safari Web Inspector on Mac with the IOS Emulator. or a browser using http://localhost:[some port number]/location/of/app when using the Android Emulator (the recommended Genymotion).
So, to do this, in Kimono use the API Hash addition &kimhash=1 to the end of your url AFTER the APIKEY but BEFORE the modify such as this:
https://www.kimonolabs.com/api/{indentifier}{apikey}&kimhash=1&kimmodify=1
. See: Kimono API Docs- Re:Hash.
This creates something like
"EventTitles": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"HeadlineImages": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"eventdescription": {
"text": "TEXT"
},
"hash":"1a2b3c4d5e6f7g8h9z"},
a random 'indentifier' is created for each entry.
Now, that's where I'm stuck now. ...because the API URL needing to come in is:
https://www.kimonolabs.com/api/{indentifier}{apikey}&kimhash=1&kimmodify=1
and when you go to configure your API on the backend there is no area I see to enter this new &kimhash=1&kimmodify=1 that needs to be at the end of the URL to call in the correctly formatted and id'd API and as far as I can see there is no reference for doing this.
http://docs.appgyver.com/supersonic/guides/data/other-data-providers/kimono-labs/
I feel like this is the next to last step in figuring this all out and finally being able to get this up and working. The last being to obviously revisit pulling in the id to the ShowController which I'm feeling somewhat confident about if I can somehow figure out this last part.
Any ideas??

jquery templating - import a file?

I'm working with backbone.js, but as far as I've seen, it doesn't care what templating system you use. Currently I'm trying out mustache.js, but I'm open to other ones. I'm a little annoyed though with the way I have to put a template into a string:
var context = {
name: this.model.get('name'),
email: this.model.get('email')
}
var template = "<form>Name<input name='name' type='text' value='{{name}}' />Email<input name='email' type='text' value='{{email}}' /></form>";
var html = Mustache.to_html(template, context);
$(this.el).html(html);
$('#app').html(this.el);
I'd like if I could load it from a different file or something somehow. I want to be able to have template files in order to simplify things. For example, if I put it all in a string, I can't have breaks (well I can have html breaks, but that's not the point). After the line starts to get very long, it becomes unmanageable.
Tips?
Updated (4/11/14):
As answered by OP below:
Unfortunately, the jQuery team has moved the templating functionality out of jQuery Core. The code is still available as a library here: github.com/BorisMoore/jquery-tmpl and here: github.com/borismoore/jsrender
Original Answer:
I just used this a couple of hours ago:
http://api.jquery.com/category/plugins/templates/
It's an official jQuery plugin(i.e. the devs endorse it).
This is the function you need to use for loading templates from things other than strings: http://api.jquery.com/template/
Here's the code to have a template in HTML:
<script id="titleTemplate" type="text/x-jquery-tmpl">
<li>${Name}</li>
</script>
___________
// Compile the inline template as a named template
$( "#titleTemplate" ).template( "summaryTemplate" );
function renderList() {
// Render the movies data using the named template: "summaryTemplate"
$.tmpl( "summaryTemplate", movies ).appendTo( "#moviesList" );
}
It's in a <script> tag, because that's not visible by default.
Note the type="text/x-jquery-tmpl". If you omit that, it will try to parse it as JavaScript(and fail horribly).
Also note that "loading from a different file" is essentially the same as "reading a file" and then "loading from a string".
Edit
I just found this jQuery plugin - http://markdalgleish.com/projects/tmpload/ Does exactly what you want, and can be coupled with $.tmpl
I have built a lightweight template manager that loads templates via Ajax, which allows you to separate the templates into more manageable modules. It also performs simple, in-memory caching to prevent unnecessary HTTP requests. (I have used jQuery.ajax here for brevity)
var TEMPLATES = {};
var Template = {
load: function(url, fn) {
if(!TEMPLATES.hasOwnProperty(url)) {
$.ajax({
url: url,
success: function(data) {
TEMPLATES[url] = data;
fn(data);
}
});
} else {
fn(TEMPLATES[url]);
}
},
render: function(tmpl, context) {
// Apply context to template string here
// using library such as underscore.js or mustache.js
}
};
You would then use this code as follows, handling the template data via callback:
Template.load('/path/to/template/file', function(tmpl) {
var output = Template.render(tmpl, { 'myVar': 'some value' });
});
We are using jqote2 with backbone because it's faster than jQuery's, as you say there are many :)
We have all our templates in a single tpl file, we bind to our template_rendered so we can add jquery events etc etc
App.Helpers.Templates = function() {
var loaded = false;
var templates = {};
function embed(template_id, parameters) {
return $.jqote(templates[template_id], parameters);
}
function render(template_id, element, parameters) {
var render_template = function(e) {
var r = embed(template_id, parameters);
$(element).html(r);
$(element).trigger("template_rendered");
$(document).unbind(e);
};
if (loaded) {
render_template();
} else {
$(document).bind("templates_ready", render_template);
}
}
function load_templates() {
$.get('/javascripts/backbone/views/templates/templates.tpl', function(doc) {
var tpls = $(doc).filter('script');
tpls.each(function() {
templates[this.id] = $.jqotec(this);
});
loaded = true;
$(document).trigger("templates_ready");
});
}
load_templates();
return {
render: render,
embed: embed
};
}();
They look like
<script id="table" data-comment="generated!!!!! to change please change table.tpl">
<![CDATA[
]]>
</script>

ExtJs: Tree: how download then select using AJAX calls?

Here's my goal :
- open a tree
- download the root nodes
- expand automatically one specific node using AJAX (and loop n times here) until i find a leaf then select the leaf
Here's the function that works when I declare the Tree :
listeners: {
load: function(n) {
console.log('load(n)');
n.eachChild(
function(n) {
if ((n.id=='lys/2007') ||
(n.id=='lys/2007/08') ||
(n.id=='lys/2007/08/29')) {
n.expand(false,false);
}
});
}
}
But if I don't know how to make it more "generic" (almost exactly like the ExtJs documentation). But they don't jump automatically to a specific node (i.e. I want no user interaction).
Any idea / advice how to do this?
Don't hesitate to edit my post to make it proper English :)
If you already have a handle to your node, use node.getPath() to get the full "path" of it, and then use selectPath to "select" it programatically.
tree.selectPath(node.getPath());
Since you seem to know the exact path, you can probably just call selectPath on the tree.
tree.selectPath('lys/2007/08/29'); //adjust to match node.getPath() format
Thanks for the answer.
Here's the code that works : I moved it outside, into the TreeLoader object, this way :
var lysTreeLoader = new Ext.tree.TreeLoader({
dataUrl: 'json/lys.php',
listeners: {
load: function(loader,n,response) {
console.log('Données chargées');
n.eachChild(
function(n) {
if ((n.id=='lys/2007') ||
(n.id=='lys/2007/08') ||
(n.id=='lys/2007/08/29')) {
n.expand(false,false);
}
if (n.id=='lys/2007/08/29/21_14_04') {
n.select();
console.log(n.id);
console.log(n.getPath());
}
});
}
}
});
Then in the tree declaration, declare the lysTreeLoader :
...blabla...
id: 'treepanel-labys',
xtype: 'treepanel',
width: 400,
autoScroll: true,
split: true,
// use a TreeLoader :
loader: lysTreeLoader,
...blabla...
And I just had to use the function select(); (which didn't work as expected in my question)
Thanks again !

Categories

Resources