Recursive function for detailCellRendererParams, Ag-Grid? - javascript

I have data which consists of multiple rows of data. Each row contains a 'children' array property, which may have data in the form of more rows, or may be empty. On top of that, each of the rows within the 'children' array property may also contain more 'children' data or rows and so on, so it looks like this (think of each line as a row and each indented line as a child row of that row):
r|-------
r1|------*
r1a|------
r1b|------*
r1b1|------
r1c|------*
r1c1|------
r1c2|------
r2|------
r3|------*
r3a|------
r3b|------
Each parent containing child rows (I marked them with '*') must have detailCellRendererParams defined, which is fine if I was just going to define each one manually (as shown in Ag-Grid documentation under Nesting Master / Detail, however, it is uncertain how many parent/children rows there will be. I am looking to create a recursive function that defines the detailCellRendererParams for each parent row with children. How might I write something like this?

No recursion required, just use the tree data functionality of ag-grid:
https://www.ag-grid.com/javascript-grid-tree-data/
You need to enable tree functionality with:
var gridOptions = {
treeData: true,
...
}
and provide the grid with the field that creates your tree-hierarchy
gridOptions.getDataPath: function(data) {
return data.myHierarchyField;
},

Related

Bootstrap Vue Table - Getting the selections from multiple tables?

I've been playing around with Bootstrap Vue and tables.
My problem is as follows: I have several tables that are dynamically loaded on a page, and users should be able to select items on each of those tables. All selections should then be concatenated into one array that I can then display at the top of the page.
So far I've added the following to each of the tables:
#row-selected="onRowSelected"
As well as the following method:
methods: {
onRowSelected(items) {
items.forEach((item) => {
if (!this.selectedHashtags.includes(item.hashtag)) {
this.selectedHashtags.push(item.hashtag);
}
})
},
}
The issue is that as soon as I deselect an item from the table it doesn't remove it from the array, and I'm struggling to find a way to make this work.
Unfortunately the #row-selected event doesn't send the ID / ref of the table, and I can't find find a method of getting all the selected rows from each individual table. That way I could just loop through all the this.$refs and get all the selected rows and bundle them together on every row-click.
Essentially the easiest way would be if there was a way to programmatically obtain all the selected items from a table?
Any thoughts on what the best way to achieve this might be?
Probably the easiest way would be to store the selected value together with a unique key of each table. You would then call the method like with the keyword $event (see the documentation):
#row-selected="onRowSelected('table1', $event)"
You could also wrap an inline function in the template to achieve the same result:
#row-selected="(items) => onRowSelected('table1', items)"
Then, you would store the items in an object depending on the table key:
onRowSelected(tableKey, items) {
// clears the list of this table key and overwrites it with the current entries
this.selectedHashtags[tableKey] = items;
}
You can then define a computed variable to retrieve all selected hashtags (over all tables):
allSelectedHashtags() {
const allSelectedHashtags = [];
Object.keys(this.selectedHashtags).forEach(tableArray => {
allSelectedHashtags.concat(tableArray);
});
return allSelectedHashtags;
}

IN CQ, how to set value of all the items in Panel to blank

In ExtJS panel I need to set value of all items (e.g. textfield, pathfield) to blank. I don't want to set value of each individual item to blank but of whole panel in one go.
I am able to get list of items
function getAllChildren (panel) {
/*Get children of passed panel or an empty array if it doesn't have thems.*/
var children = panel.items ? panel.items.items : [];
/*For each child get their children and concatenate to result.*/
CQ.Ext.each(children, function (child) {
children = children.concat(getAllChildren(child));
});
return children;
}
but how to set to blank for whole panel? Please suggest what need to be done in this case.
Actually, it's not possible to do it with one liner - all at the same time. What your method returns is purely an array of objects. In fact if such syntax existed, it would iterate over all fields anyway.
Though clearing all fields, having the method you've proposed is very trivial to do. Just iterate over them all and call reset method. Mind some (especially custom) widgets might not handle it.
var fields = getAllChildren(panel);
CQ.Ext.each(fields, function(field) {
if (child.reset) {
child.reset();
}
});
You've got similar loop in your getAllChildren code - you might reset field at the same place.
The method is defined in Field type which is usually a supertype of each dialog widget. You can read more here.

DataTables: How to bypass the filtering rules?

How can I exempt a single row in a DataTables.js table from DataTables' builtin filtering, so thta it is always shown?
Background: I'm building a table editing component using the jQuery-based DataTables.js library. Instead of using dialogs or overlays, I wanted to present editing controls right within the datatable, like this:
This works like a charm, even with active filters: I keep the original, unchanged data in the record while it is being edited, so I can use that data for the 'sort' and 'filter' modes of mDataProp, and my row stays in place and visible until editing is finished.
A bigger problem arises when I add a new row: There is no data to use for filtering, so if a filter is active, my row won't be visible. This breaks the workflow where the user searches through the dataset, sees that some record is missing, and (without clearing the filter) presses the "Add" button, waiting for an empty row with edit controls to appear:
How can I exempt this special row from DataTables' filtering?
After reading through the source code of DataTables.js for some time, I came to the conclusion that there is no way to hook into the filtering in the desired way. There are hooks for custom filters, but they can only be used to hide stuff, not to show stuff.
However, there's a 'filter' event which is triggered after filtering, but before the table is rendered. My solution installs an handler for this event:
$('table#mydatatable').bind('filter', function() {
var nTable = $(this).dataTable();
var oSettings = nTable.fnSettings();
//collect the row IDs of all unsaved rows
var aiUnsavedRowIDs = $.grep(oSettings.aiDisplayMaster, function(iRowID) {
var oRowData = nTable.fnGetData(iRowID);
return is_unsaved(oRowData);
});
//prepare lookup table
var oUnsavedRecordIDs = {};
$.each(aiUnsavedRowIDs, function(idx, iRowID) {
oUnsavedRecordIDs[iRowID] = true;
});
//remove unsaved rows from display (to avoid duplicates after the
//following step)
for (var i = oSettings.aiDisplay.length; i >= 0; i--) {
//iterate backwards, because otherwise, removal from aiDisplay
//would mess up the iteration
if (oUnsavedRecordIDs[ oSettings.aiDisplay[i] ]) {
oSettings.aiDisplay.splice(i, 1);
}
}
//insert unsaved records at the top of the display
Array.prototype.unshift.apply(oSettings.aiDisplay, aiUnsavedRowIDs);
//NOTE: cannot just say oSettings.aiDisplay.unshift(aiUnsavedRowIDs)
//because this would add the array aiUnsavedRowIDs as an element to
//aiDisplay, not its contents.
});
What happens here? First, I find all unsaved rows by looking through oSettings.aiDisplayMaster. This array references all rows that are in this DataTable, in the correct sorting order. The elements of aiDisplayMaster are integer indices into DataTables' internal data storage (one index per row).
The filtering process goes through the rows in aiDisplayMaster, and places the row IDs of all matching rows in oSettings.aiDisplay. This array controls which rows will be rendered (after this event handler has finished!). The whole process looks like this:
[1, ..., numRows]
|
| sorting
v
oSettings.aiDisplayMaster
|
| filtering
v
oSettings.aiDisplay
|
| rendering
v
DOM
So after having located all unsaved records in aiDisplayMaster (using custom logic that I wrapped in an is_unsaved() function for the sake of this snippet), I add them all to aiDisplay (after removing existing instances of these rows, to avoid duplicates).
A side-effect of this particular implementation is that all unsaved rows appear at the top of the table, but in my case, this is actually desirable.

datatables dynamically add row with fnAddData or similar and add a class to a specific column

Ok I am trying to dynamically add new rows to a already rendered table using datatables. Thus far what I have is
oTable.fnAddData(["D:\Exlab", '[Edit] [Delete]']);
Which this works for adding a single row (if anyone knows how to use a similar function to add multiple rows without running a loop that would be bonus). However I want to have a specific column in this case the second column have a special class, is there a means of adding a class to a column thats being added on the fly?
I think you could accomplish this by controlling the column definitions and assigning the class via fnRender. After your columns are defined, feed the fnAddData function some data.
Here is a similar SO questions.. CLICK HERE that I think you would find useful.
In your case, I think that the column definitions would look something like this
...
"aoColumns": [
{
"sClass": "datasource_row_edit",
"fnRender": function( oObj ) {
return 'Edit';
}
},
{
"sClass": "datasource_row_delete",
"fnRender": function( oObj ) {
return 'Delete';
}
}
],
...
Via their api .. http://www.datatables.net/api ... You could feed the table any number of rows via json
var json = eval("[" + response + "]");
oTable.fnAddData(json);
and let the datatable render any formatting itself dynamically
For your first question, you can hook up to the "fnCreatedRow" callback, http://www.datatables.net/usage/callbacks. This will allow you to listen to row add events and manipulate them as necessary.
The "bonus" is that you can pass 2d-arrays to fnAddData to avoid looping

Strategy to extract structured data with xpath

Is there a pattern to extract structured data from an HTML page using XPath? I'm trying to extract data from one or more HTML tables on a page. XPath makes it easy to find the table(s), but I'm struggling once I've got that far.
I'm currently doing the following:
Iterate the tables (there may be more than one)
Iterate the rows within that table
Iterate the cells within that row
(Then probably put them in an array and parse the contents)
My code is something like this:
var tables = mydoc.evaluate( "//table", mydoc, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
table = tables.iterateNext();
while (table)
{
var rows = mydoc.evaluate("tbody/tr", table, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
row = rows.iterateNext();
while (row)
{
var tds = mydoc.evaluate("td", row, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null)
td = tds.iterateNext()
while(td)
{
// TODO: store content in an array to process later
print('*' + td.textContent);
td = tds.iterateNext();
}
row = rows.iterateNext();
}
table = iterator.iterateNext();
}
This seems a little nasty as all the XPath examples seem to do their processing in one step. There appear to be few non-trivial examples where two types of data (e.g. labels and values in a table) are selected and combined. I can use the following selectors, but I end up with two lists with no structure:
//table/tbody/tr/td[#class='label']
//table/tbody/tr/td/a[#class='value']
(I know I'm using XPath for HTML parsing for which it wasn't really intended, but it seems to work so far.)
There appear to be few non-trivial
examples where two types of data (e.g.
labels and values in a table) are
selected and combined. I can use the
following selectors, but I end up with
two lists with no structure:
//table/tbody/tr/td[#class='label']
//table/tbody/tr/td/a[#class='value']
Use:
//table/tbody/tr/td[#class='label']
|
//table/tbody/tr/td/a[#class='value']
This single XPath expression selects all the wanted nodes (all XPath engines I am aware of return the selected nodes in document order). The | (union) operator produces the set union of its arguments.
If the (x)Html document has regular structure, you may expect in the returned result every selected td element (label) to be followed by its corresponding a element (value)
If it's on the main HTML page, you could just do:
for(var tables=document.getElementsByTagName("table"),i=0;i<tables.length;++i)
for(var rows=tables[i].getElementsByTagName("tr"),j=0;j<rows.length;++j)
for(var cells=rows[j].getElementsByTagName("td"),k=0;k<cells.length;++k)
print("*"+cells[i].textContent);
getElementsByTagName does /not/ return an array - it returns a live NodeList similar to ORDERED_NODE_ITERATOR_TYPE.

Categories