yui treeview dynamic loading - treeview

Can you please let me know how can I have 3rd level sub nodes in YUI treeview.
I know how to do it for first level sub nodes.
For example ,
for first level we have grandparents of family.
in the next level we have parents.
In the next we have grandchildren.
It works for me till second level.i.e first level sub nodes.
but for next level how should I set dynamic loading?
What I have done for first level sub nodes is associate callback to tree.
var tree=new YAHOO.widget.TreeView("tree");
var sUrl = "getJsonData-imp-ses";
var callback = {
success : function(oResponse) {
var array1 = YAHOO.lang.JSON.parse(oResponse.responseText);
if (YAHOO.lang.isArray(array1)) {
for (var i = 0, j = array1.length; i < j; i++) {
var tempNode = new YAHOO.widget.TextNode(array1[i], tree.getRoot(), false);
console.log(tempNode);
}
}
tree.draw();
},
failure : function(oResponse) {
},
timeout : 7000
};
YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
tree.setDynamicLoad(function(node,fnLoadComplete){
YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
callback={
success:{},
failure:{},timeout:700
};
)};
Now I want attach sub nodes to the 1st level sub nodes.
I am using YUI 2.9.0.
Can anyone please help?

See: http://yui.github.io/yui2/docs/yui_2.9.0_full/examples/treeview/dynamic_tree.html for a full example.
In your code, instead of using tree.getRoot() as the place to append the loaded nodes to, you can reference any other node to append them to, just like in the above example.

Related

How to select multiple items in a TTreeView in code

I'm trying to select all the child node of a parent node when the parent is clicked, but when I for each node set the Selected = true i only end up with the last one being selected.
MultiSelect is true and I can do it with the mouse, so the setup should be ok.
For testing I use this code:
TTreeNode *node = Tv->Items->GetFirstNode();
node->Selected = true;
node = node->GetNext();
node->Selected = true;
node = node->GetNext();
node->Selected = true;
node = node->GetNext();
node->Selected = true;
Any tricks to make this work?
The TTreeNode::Selected property does not support multiple selections when toggling the node's selection state. Internally, it will call the Win32 TreeView_SelectItem() API, which selects a single node only.
For multi-select, use the TTreeView::Select() method instead:
The select method selects one or more tree nodes.
That being said, your example is attempting to select (potentially) every node in the TreeView, not just the child nodes of a parent node, as you claim.
Try this:
void AddNodeAndChildrenToList(TList *List, TTreeNode *Node)
{
List->Add(Node);
TTreeNode *child = Node->getFirstChild();
while (child)
{
AddNodeAndChildrenToList(List, child);
child = child->getNextSibling();
}
}
...
TList *nodes = new TList;
try
{
TTreeNode *parent = ...;
AddNodeAndChildrenToList(nodes, parent);
Tv->Select(nodes);
}
__finally
{
delete nodes;
}

Suitescript Saved Search Filter using other saved search results

I am trying to use the results of a specific saved search to try and filter another saved search in suitescript.
Basically, there is a button created on a project. Once the button is clicked, I need to go get all the tasks for that specific project and use each task to filter on a transaction saved search using a custom field and get whatever information is on that saved search.
This is what I have so far:
function runScript(context) {
var record = currentRecord.get();
var id = record.id;
var type = record.type;
var i = 0;
console.log(id);
var projectSearch = search.load({id: 'customsearch1532'})
var billableExpenseSearch = search.load({id: 'customsearch1533'})
var projectFilter = search.createFilter({
name:'internalId',
operator: search.Operator.IS,
values: id
});
projectSearch.filters.push(projectFilter);
var projectResults = projectSearch.run().getRange(0,1000);
while(i < projectResults.length){
var task = projectResults[i].getValue(projectSearch.columns[1]);
console.log(task);
var billableExpenseFilter = search.createFilter({
name:'custcol4',
operator: search.Operator.ANYOF,
values: task
});
billableExpenseSearch.filters.push(billableExpenseFilter);
var billableExpenseResults = billableExpenseSearch.run().getRange(0,1000);
console.log(billableExpenseResults.length);
for(var j = 0; j< billableExpenseResults.length; j++){
var testAmount = billableExpenseResults[j].getValue(billableExpenseSearch.columns[3]);
console.log(testAmount);
}
i++;
}
}
The log for the Task is correct. I have 2 tasks on the project I am trying this on but once we get to the second iteration, the billableExpenseSearch length is showing as 0, when it's supposed to be 1.
I am guessing that my logic is incorrect of the createFilter function doesn't accept changes once the filter is created.
Any help is appreciated!
EDIT:
var billableExpenseSearch = search.load({id: 'customsearch1533'});
var billableExpenseFilter = search.createFilter({
name:'custcol4',
operator: search.Operator.ANYOF,
values: task
});
billableExpenseSearch.filters.push(billableExpenseFilter);
var billableExpenseResults = billableExpenseSearch.run().getRange(0,1000);
console.log(billableExpenseResults.length);
for(var j = 0; j< billableExpenseResults.length; j++){
var taskid = billableExpenseResults[j].getValue(billableExpenseSearch.columns[0]);
console.log(taskid);
Thank you
I think your guess is correct your are keep pushing filters
billableExpenseSearch.filters.push(billableExpenseFilter);
After pushing the filter and extract the value you need to remove it before adding a new one, you can do this by pop() the last one:
billableExpenseSearch.filters.pop();
Note: You can fix this by re-loading the search every time before pushing the filter. this will reset your filters, but I do NOT recommend that since loading a search will consume more USAGE and might receive USAGE_LIMIT_EXCEEDED ERROR.
I also recommend the following:
1- Get all task ids before doing the second search, once you do that you only need to search once. Because if you have many records you might encounter USAGE_LIMIT_EXCEEDED ERROR. Since you work with a client or Suitelet script you only have 1000 USAGE.
Edit: Sample might help you.
var ids = [];
var pagedData = projectSearch.runPaged({pageSize : 1000});
// iterate the pages
for( var i=0; i < pagedData.pageRanges.length; i++ ) {
// fetch the current page data
var currentPage = pagedData.fetch(i);
// and forEach() thru all results
currentPage.data.forEach( function(result) {
// you have the result row. use it like this....
var id = result.getValue(projectSearch.columns[1]);
Ids.push(id);
});
}
Note: This search will extract all records not only first 1000.
After that add the array to the Filter
var billableExpenseFilter = search.createFilter({
name:'custcol4',
operator: search.Operator.ANYOF,
values: [ids]
});
2- Don't Use search.load use search.create it will make your script more readable and easier to maintain in the future.

Adding External filters for Kendo UI Grid

What i want to achieve is:
Have a Master Grid. Clicking on a row of this grid, i want to filter the rows of the ChildGrid.
What i have done so far:
function updateChildGridRows(field, operator, value) {
// get the kendoGrid element.
var gridData = $("#childGrid").data("kendoGrid");
var filterField = field;
var filterValue = value;
// get currently applied filters from the Grid.
var currFilterObj = gridData.dataSource.filter();
// if the oject we obtained above is null/undefined, set this to an empty array
var currentFilters = currFilterObj ? currFilterObj.filters : [];
// iterate over current filters array. if a filter for "filterField" is already
// defined, remove it from the array
// once an entry is removed, we stop looking at the rest of the array.
if (currentFilters && currentFilters.length > 0) {
for (var i = 0; i < currentFilters.length; i++) {
if (currentFilters[i].field == filterField) {
currentFilters.splice(i, 1);
break;
}
}
}
if (filterValue != "") {
currentFilters.push({
field: filterField,
operator: "eq",
value: filterValue
});
}
gridData.dataSource.filter({
filters: currentFilters
}); }
I got this code from the following jsfiddle:
http://jsfiddle.net/randombw/27hTK/
I have attached the MasterGrid's Change event to MasterGridSelectionChange() method. From there i am calling my filter method.
But when i click on the MasterGrid's row, all of the rows in my ChildGrid are getting removed.
One thing i can understand is, if i give wrong column name in the filter list, all the rows will be removed. But even though i have given correct ColumnName, my rows are getting deleted.
Sorry for the long post.
Please help me with this issue, as i am stuck with this for almost 4 days!
Thanks.
You can define ClientDetailTemplateId for the master grid and ToClientTemplate() for the Child grid
read Grid Hierarchy for a example

Assigning functions to multiple slickgrids

Please help to solve this very annoying problem. I am using a for loop to iterate over an array of data and create multiple grids. It is working well but the filter function is not binding properly (it only binds to the LAST grid created) Here is the code:
// this function iterates over the data to build the grids
function buildTables() {
// "domain" contains the dataset array
for (i = 0; i < domains.length; i++) {
var dataView;
dataView = new Slick.Data.DataView();
var d = domains[i];
grid = new Slick.Grid('#' + d.name, dataView, d.columns, grids.options);
var data = d.data;
// create a column filter collection for each grid - this works fine
var columnFilters = columnFilters[d.name];
// this seems to be working just fine
// Chrome console confirms it is is processed when rendering the filters
grid.onHeaderRowCellRendered.subscribe(function (e, args) {
$(args.node).empty();
$("<input type='text'>")
.data("columnId", args.column.id)
.val(columnFilters[args.column.id])
.appendTo(args.node);
});
// respond to changes in filter inputs
$(grid.getHeaderRow()).delegate(":input", "change keyup", function (e) {
var columnID = $(this).data("columnId");
if (columnID != null) {
// this works fine - when the user enters text into the input - it
// adds the filter term to the filter obj appropriately
// I have tested this extensively and it works appropriately on
// all grids (ie each grid has a distinct columnFilters object
var gridID = $(this).parents('.grid').attr('id');
columnFilters[gridID][columnID] = $.trim($(this).val());
dataView.refresh();
}
});
//##### FAIL #####
// this is where things seem to go wrong
// The row item always provides data from the LAST grid populated!!
// For example, if I have three grids, and I enter a filter term for
// grids 1 or 2 or 3 the row item below always belongs to grid 3!!
function filter(row) {
var gridID = $(this).parents('.grid').attr('id');
for (var columnId in grids.columnFilters[gridID]) {
if (columnId !== undefined && columnFilters[columnId] !== "") {
var header = grid.getColumns()[grid.getColumnIndex(columnId)];
//console.log(header.name);
}
}
return true;
}
grid.init();
dataView.beginUpdate();
dataView.setItems(data);
dataView.setFilter(filter); // does it matter than I only have one dataView instance?
dataView.endUpdate();
grid.invalidate();
grid.render();
In summary, each function seems to be binding appropriately to each grid except for the filter function. When I enter a filter term into ANY grid, it returns the rows from the last grid only.
I have spent several hours trying to find the fault but have to admit defeat. Any help would be most appreciated.
yes, it matters that you have only one instance of dataView. and also sooner or later you will come up to the fact that one variable for all grids is also a bad idea
so add a var dataView to your loop, it should solve the problem

facing performance issues with knockout mapping plugin

I have decent large data set of around 1100 records. This data set is mapped to an observable array which is then bound to a view. Since these records are updated frequently, the observable array is updated every time using the ko.mapping.fromJS helper.
This particular command takes around 40s to process all the rows. The user interface just locks for that period of time.
Here is the code -
var transactionList = ko.mapping.fromJS([]);
//Getting the latest transactions which are around 1100 in number;
var data = storage.transactions();
//Mapping the data to the observable array, which takes around 40s
ko.mapping.fromJS(data,transactionList)
Is there a workaround for this? Or should I just opt of web workers to improve performances?
Knockout.viewmodel is a replacement for knockout.mapping that is significantly faster at creating viewmodels for large object arrays like this. You should notice a significant performance increase.
http://coderenaissance.github.com/knockout.viewmodel/
I have also thought of a workaround as follows, this uses less amount of code-
var transactionList = ko.mapping.fromJS([]);
//Getting the latest transactions which are around 1100 in number;
var data = storage.transactions();
//Mapping the data to the observable array, which takes around 40s
// Instead of - ko.mapping.fromJS(data,transactionList)
var i = 0;
//clear the list completely first
transactionList.destroyAll();
//Set an interval of 0 and keep pushing the content to the list one by one.
var interval = setInterval(function () {if (i == data.length - 1 ) {
clearInterval(interval);}
transactionList.push(ko.mapping.fromJS(data[i++]));
}, 0);
I had the same problem with mapping plugin. Knockout team says that mapping plugin is not intended to work with large arrays. If you have to load such big data to the page then likely you have improper design of the system.
The best way to fix this is to use server pagination instead of loading all the data on page load. If you don't want to change design of your application there are some workarounds which maybe help you:
Map your array manually:
var data = storage.transactions();
var mappedData = ko.utils.arrayMap(data , function(item){
return ko.mapping.fromJS(item);
});
var transactionList = ko.observableArray(mappedData);
Map array asynchronously. I have written a function that processes array by portions in another thread and reports progress to the user:
function processArrayAsync(array, itemFunc, afterStepFunc, finishFunc) {
var itemsPerStep = 20;
var processor = new function () {
var self = this;
self.array = array;
self.processedCount = 0;
self.itemFunc = itemFunc;
self.afterStepFunc = afterStepFunc;
self.finishFunc = finishFunc;
self.step = function () {
var tillCount = Math.min(self.processedCount + itemsPerStep, self.array.length);
for (; self.processedCount < tillCount; self.processedCount++) {
self.itemFunc(self.array[self.processedCount], self.processedCount);
}
self.afterStepFunc(self.processedCount);
if (self.processedCount < self.array.length - 1)
setTimeout(self.step, 1);
else
self.finishFunc();
};
};
processor.step();
};
Your code:
var data = storage.transactions();
var transactionList = ko.observableArray([]);
processArrayAsync(data,
function (item) { // Step function
var transaction = ko.mapping.fromJS(item);
transactionList().push(transaction);
},
function (processedCount) {
var percent = Math.ceil(processedCount * 100 / data.length);
// Show progress to the user.
ShowMessage(percent);
},
function () { // Final function
// This function will fire when all data are mapped. Do some work (i.e. Apply bindings).
});
Also you can try alternative mapping library: knockout.wrap. It should be faster than mapping plugin.
I have chosen the second option.
Mapping is not magic. In most of the cases this simple recursive function can be sufficient:
function MyMapJS(a_what, a_path)
{
a_path = a_path || [];
if (a_what != null && a_what.constructor == Object)
{
var result = {};
for (var key in a_what)
result[key] = MyMapJS(a_what[key], a_path.concat(key));
return result;
}
if (a_what != null && a_what.constructor == Array)
{
var result = ko.observableArray();
for (var index in a_what)
result.push(MyMapJS(a_what[index], a_path.concat(index)));
return result;
}
// Write your condition here:
switch (a_path[a_path.length-1])
{
case 'mapThisProperty':
case 'andAlsoThisOne':
result = ko.observable(a_what);
break;
default:
result = a_what;
break;
}
return result;
}
The code above makes observables from the mapThisProperty and andAlsoThisOne properties at any level of the object hierarchy; other properties are left constant. You can express more complex conditions using a_path.length for the level (depth) the value is at, or using more elements of a_path. For example:
if (a_path.length >= 2
&& a_path[a_path.length-1] == 'mapThisProperty'
&& a_path[a_path.length-2] == 'insideThisProperty')
result = ko.observable(a_what);
You can use typeOf a_what in the condition, e.g. to make all strings observable.
You can ignore some properties, and insert new ones at certain levels.
Or, you can even omit a_path. Etc.
The advantages are:
Customizable (more easily than knockout.mapping).
Short enough to copy-paste it and write individual mappings for different objects if needed.
Smaller code, knockout.mapping-latest.js is not included into your page.
Should be faster as it does only what is absolutely necessary.

Resources