Expand Row of Table on Click and draw D3 charts inside - javascript

I am trying to create a Table in which a row expands on click and two D3 charts are drawn inside the new expanded row.
My logic for Expand Row:-
<tbody>
<tr ng-repeat-start="item in nodeSummary">
<td>
<button ng-if="item.expanded" ng-click="tableRowExpand($index,false)">-</button>
<button ng-if="!item.expanded" ng-click="tableRowExpand($index,true)">+</button>
</td>
<td>{{item.a}}</td>
<td>{{item.b}}</td>
<td>{{item.c}}</td>
</tr>
<tr ng-if="item.expanded" ng-repeat-end>
<td colspan="4" style="height:100px;width:100%;">
<div id="expanded-tablerow-container" style="height:100%;width:100%;overflow:auto;" >
<div id="expanded-tablerow-circleProgress" style="height:100%;width:20%;float:left;">Here</div>
<div id="expanded-tablerow-barchart" style="height:100%;width:80%;float:left;">Here</div>
</div>
</td>
</tr>
</tbody>
So using ng-repeat-start/end, and onClick of button the particular row is expanded/shrinked.
tableRowExpand function:-
$scope.tableRowExpand = function(index, value){
$scope.nodeSummary[index].expanded = value;
if (value){
drawCircularProgressBar(d3.select("#expanded-tablerow-circleProgress"),0.8);
drawHorizontalBar("#expanded-tablerow-barchart",data);
}
};
So on checking value is true I am calling methods to draw d3 chart and bar. But they are not drawing.
My Problems:-
Well, I looked into the problem and it seems the problem is calling these method before the changes to the document is done, How I know this? Well, suppose if there are two rows, then on expanding 1st row, nothing is drawn but on then clicking the 2nd row, bar and chart is drawn on 1st row's expanded space. Am I able to make myself clear? So how to fix this problem?
Multiple rows can remain expanded at a time. I may have 100s of rows, and the d3 drawing is done based on data of each row which will vary, so each row will have unique d3 drawing on its expanded view. But as the drawing is done by passing the divs, which for all expanded rows will be same, won't this kill the uniqueness, saying same drawing on all expanded view?. In a way, how can I have unique drawing for each row?
Hope I was able to make myself clear. Please help me resolve this.
Thanks.

Well I was finally able to solve it. Lets go for it.
Problem 1)
Yeah I was right :) The problem is that the document was not fullt loaded, I solved adding a simple check code,
angular.element(document).ready(function(){
//calling the d3 draw code here
});
Problem 2)
I don't know if this is the most efficient way, if someone want to input, please do, but I did this.
var newCircleProgressId = 'expanded-tablerow-circleProgress' + index;
var newHoizontalBarId = 'expanded-tablerow-barchart' + index;
document.getElementById("expanded-tablerow-circleProgress").setAttribute('id',newCircleProgressId);
document.getElementById("expanded-tablerow-barchart").setAttribute('id', newHoizontalBarId);
i.e. dynamically altering the ID of the div inside the tableRowExpand function and passing this new ID along with row unique(extracted using index) to draw d3 functions.

Related

Color cell according to condition

I have a table in which I have to set background color when the cell in header and cell in row appear as pair in a certain list in data source.
For example:
column : "AUD, USD"
row : "BRL, CZK"
in the cell of column AUD and row is BRL I check if exists in the list in datasource "AUD-BRL" and if so I need to color in a green
Now, I thought to do it in this way:
columns and rows will be in lists.
I go over both lists and then color in those indexes the cell.
So that I will have one function for whole table and not have to call from each cell to function (There are 1200 cells overall).
How can that be done?
The answer from Fede MG is correct.
If I understand your question correctly, you want to add a highlighting rule to all cells in the table detail row. Unfortunately I think it is a bit cumbersome to achieve this in BIRT.
I assume that your table has e.g. bindings like COL_VALUE_1, ..., COL_VALUE_9 for the cell values and COL_TITLE_1, ..., COL_TITLE_9 for the column headers.
Furthermore I assume a bit of experience with using Javascript in BIRT.
The way I do this like this:
For each detail cell I create a onCreate event script with code like this:
highlightDetailCell(this, row, 1);
... where 1 is the column number. E.g. this is the code for the first column, for the second column i replace the 1 with 2 and so on. One can quickly do this with copy&paste.
Next I implement the logic in a function inside the onInitialize script of the report like this:
function highlightDetailCell(item, row, colnum) {
var colTitle = row["COL_TITLE_" + colnum];
var colValue = row["COL_VALUE_" + colnum];
var highlight = use_your_logic_to_decide(colTitle, colValue);
if (highlight) {
item.get_Style().backgroundColor = "yellow";
}
}
This is the basic idea. If you want to add the script to many cells, it might be a lot of work to do this by hand. In fact it is possible to attach the call to the highlightDetailCell function with a script (of course, this is BIRT :-). You should read the documentation and just tinker with the Design Engine API (DE API for short).
But be warned that writing and debugging such a script may be even more work than doing the donkey work of adding and editing a one-liner to 1200 cells!
What I once did was basically this (in the onFactory event of the report item):
// This code is a simplified version that modifies just the first cell,
// However it should point you into the right direction.
// Some preparation
importPackage(Packages.org.eclipse.birt.report.model.api);
var myconfig = reportContext.getReportRunnable().getReportEngine().getConfig();
var de = DataEngine.newDataEngine( myconfig, null );
var elementFactory = reportContext.getDesignHandle().getElementFactory();
// Find the item you want to modify (in my case, a "Grid Item").
// Note that for tables, the structure is probably a bit different.
// E.G. tables have header, detail and footer rows,
// while grids just have rows.
var containerGrid = reportContext.getDesignHandle().findElement("Layout MATRIX");
// Get the first row
var row0 = containerGrid.getRows().get(0);
// Do something with the first cell (:
var cell = row0.getCells().get(0).getContent();
cell.setStringProperty("paddingTop", "1pt");
cell.setStringProperty("paddingLeft", "1pt");
cell.setStringProperty("paddingRight", "1pt");
cell.setStringProperty("paddingBottom", "1pt");
cell.setStringProperty("borderBottomColor", "#000000");
cell.setStringProperty("borderBottomStyle", "solid");
cell.setStringProperty("borderBottomWidth", "thin");
cell.setStringProperty("borderTopColor", "#000000");
cell.setStringProperty("borderTopStyle", "solid");
cell.setStringProperty("borderTopWidth", "thin");
cell.setStringProperty("borderLeftColor", "#000000");
cell.setStringProperty("borderLeftStyle", "solid");
cell.setStringProperty("borderLeftWidth", "thin");
cell.setStringProperty("borderRightColor", "#000000");
cell.setStringProperty("borderRightStyle", "solid");
cell.setStringProperty("borderRightWidth", "thin");
// When you're finished:
de.shutdown( );
Things are more complicated if you have to handle merged cells.
You could even add content to the cell (I created a whole matrix dynamically this way).
The script does not exactly what you want (add the script to each cell), but I leave this as an exercise...
It is also helpful to save the dynamically modified report design for opening in the designer, to see the outcome:
reportContext.getDesignHandle().saveAs("c:/temp/modified_report.rptdesign");
HTH
Go to the cell you want to format (applies also to elements like rows or columns), on the "Property Editor" go to "Highlights" and click "Add...". You'll get a dialog where you can enter a condition for the highlight and what styling to apply on the element if the condition is true.
Screenshot here

insertBefore not updating rowIndex/nextSibling properties

It may sound stupid or even trivial for most experienced users, but I just landed a few hours ago on front-end javascript and I must say I am a bit puzzled with the behavior of the insertBefore javascript function.
My intention here is plain and simple: I have a table with its rows and cells, and in each row I have a cell with a button with the only purpose of duplicating that cell (with all its contents) and place the new duplicated cell right next to the original one.
I have a javascript function for it such like this one:
// id -> the id of the table I want the row to be added
// caller -> the object of the element that called the function
function duplicateRow(id, caller)
{
const table = document.getElementById(id);
const row = caller.parentNode.parentNode; // Caller is always a button inside a cell inside a row
const clone = row.cloneNode(true);
table.insertBefore(clone, row.nextElementSibling);
}
This function is called like this (from an extract of my HTML):
<tr>
<td>
<input type="text" name="competence-name">
</td>
<td>
<button name="duplicate-row-button" onclick="duplicateRow( 'competencies-table', this )"></button>
</td>
</tr>
So, what I would expect from it is that, at each click on the duplicate row button, it would create an exact copy of the row where the button is being clicked and add it right after that row.
My problem here is not with the duplicating (that is done just right and smooth as one would expect) but with where the new row is placed:
The first time, when there is only one row, it is placed at the end (since nextSibling is null).
The second time clicking the button on the first row (despite now having a sibling right after it), the new row is again placed at the end of the table (as if nextSibling for the first row was still null).
And so on (even strager placements happen when mixing duplications with the newly added rows).
Shouldn't the nextSibling and/or rowIndex properties be updated when adding a new node to the DOM? Is there a way of forcing them to update? What is it that I have wrong? My code, my understanding of how it should work?
I am surely open to any possible explanation/solution/alternative to achieve what I need, and thank you all in advance!
The problem is that initial table row is wrapped in a tbody element (for which you can omit both start and end tag), which is required according to the content model of tables. However, when you programmatically add more rows, they are inserted outside the tbody and your initial row is the only child of that implicit tbody, so the DOM tree looks like this:
<table>
<tbody>
<tr></tr>
</tbody>
<tr></tr>
<tr></tr>
</table>
To solve it I suggest to add a clone to cloned row's parent:
function duplicateRow(caller){
const row = caller.parentNode.parentNode; // Caller is always a button inside a cell inside a row
const clone = row.cloneNode(true);
row.parentNode.insertBefore(clone, row.nextElementSibling);
}
<table id="competencies-table">
<tr>
<td>
<input type="text" name="competence-name">
</td>
<td>
<button name="duplicate-row-button" onclick="duplicateRow( this )">Duplicate</button>
</td>
</tr>
</table>

Repeat Control using jQuery

I'll probably lose reputation for asking this; but I've been trying infinite variations of code, and failing every time. So I'm reaching out.
I'm working on an aspx. It's all built, they just want some additional functionality.
We're using ScriptSharp to trans-compile to JavaScript.
Basically, we've got an HTML table. Each row in the table represents an invoice. One column in the table represents the amount due (call it amountDue) on the invoice. Another column on the table contains a textbox wherein the user may enter the amount to apply to the invoice (call it amountToPay). If the amounts differ, another column populates with a textbox, pre-populated with the difference between the amount due and the amount entered (call it difference). Following this column is another column with a drop-down list of reasons to explain the discrepancy (call it reason). The user may change the difference in the difference textbox. If that happens, a new additional difference textbox and a new additional reason drop-down list need to appear on the same row of the table, each under its appropriate column.
My first attempts duplicated controls geometrically, for example, going from two difference textboxes to eight. I figured that out.
Now every combination of jQuery functions I try either duplicates all the controls, or none of the controls. So, on difference change, either no new difference textbox is added, or the number of difference textboxes that exist is added. So if two exist, four result. If four exist, eight result.
Okay, here's some code.
Here are the two columns for difference and reason.
<td class="currency">
<div>
<input class="difference_textbox" type="text" value="0.00" style="display: none;" />
</div>
</td>
<td>
<div>
<select style="display: none;" class="adj_reason_select">
<option></option>
</select>
</div>
</td>
I'll skip the ScriptSharp and just list the trans-compiled JavaScript (debug version):
// Let this function represent the function called on `difference` change.
ReceivePayment._addAdjustment = function ReceivePayment$_addAdjustment(e) {
var self = $(e.target);
var customerInvoice = self.parents('.customer_invoice');
var amountPaidBox = customerInvoice.find('.amount_to_pay_input');
// ...
var amountPaidTD = amountPaidBox.closest('td');
var diffTextBoxTD = ReceivePayment._duplicateInputControl(amountPaidTD);
var adjReasonSelectTD = ReceivePayment._duplicateInputControl(diffTextBoxTD);
// ...
}
ReceivePayment._duplicateInputControl = function ReceivePayment$_duplicateInputControl(td) {
// This is very verbose so that I can stop at any point and
// examine what I've got.
var o = td.next(); // Grab the next td.
var divs = o.children(); // Grab the div(s) contained within the td.
var div = divs.last(); // Grab the last div within the td.
// And here's where all my gyrations occur, infinite permutations
// of jQuery calls, not one permutation of which has succeeded in
// adding the contents of the final div to the list of divs.
var d = div[0];
var html = d.outerHTML;
var s = html.toString();
div.add(s);
return o;
}
My attempts include calling after, insertAfter, html, clone, cloneNode, appendChild, and on, and on, on different objects, including divs, div, o, etc.
Part of my problem is that I've not worked with jQuery much. I know just enough to be dangerous. But surely this is possible. Given a td, find the following td. Within that td will be a list of one or more divs. Get the last of those divs, copy it, and append that copy to the list of divs. Done.
What, oh what, am I missing? Flame on.
I appear to have stumbled upon the solution after a shameful amount of time spent spinning my wheels. I gave up. Then, of course, it hit me:
ReceivePayment._duplicateInputControl = function ReceivePayment$_duplicateInputControl(td) {
// This is very verbose so that I can stop at any point and
// examine what I've got.
var o = td.next(); // Grab the next td.
var divs = o.children(); // Grab the div(s) contained within the td.
var div = divs.last(); // Grab the last div within the td.
o.append(div.clone());
return o;
}

AngularJS toggle getting blocked by Math function

I have rows where cells are populated with words from an API. I've added a toggle feature on the cells to show if a user selected it. I'm using ng-class={selected:toggle} ng-click="toggle = !toggle" to toggle a CSS class to show the cell is selected or not.
HTML
<tr ng-repeat="row in game.rows">
<td id="row-{{$parent.$index+1}}-col-{{$index+1}}" ng-repeat="word in row.words" ng-class={selected:toggle} ng-click="toggle = !toggle"><div class="points">{{generateRandomPoints()}}</div>{{word}}</td>
</tr>
I added a Math function to the controller to randomly assign points to each cell (<div class="points">{{generateRandomPoints()}}</div>):
JavaScript
$scope.generateRandomPoints = function(){
return Math.floor((Math.random()*5)+2);
};
This function appears to be blocking my ability to use the toggle feature. Another oddity is that each click of a cell re-generates random numbers from the Math function.
Any thoughts why the toggling would be blocked?
It appears your generateRandomPoints call is punching through the local scope of your repeated element. Try creating a function like this:
$scope.toggle(index) {
game.rows[index].toggle = !game.rows[index].toggle;
}
And call it like ng-click="toggle($index)".
Full documentation for ngRepeat can be found here.

JS/JQUERY: Moving numbered table rows up and down without losing user input

I have created a table in HTML, consisting of table rows in a tbody tag.
I've used a javascript code snippet from mredkj.com to be able to add rows and delete them, too. The rows are sorted and their rank is in the first TD (cell) in every TR (row).
Now I would like the add the functionality of being able to manually 'resort' the tablerows.
The problems are:
my javascript/jquery knowledge is
very limited
the ranks of tablerows
do not get updated(when you delete a row, the
rowranks get updated by the
'reorderRows function, but calling this function from within my jQuery does not seem to
sort out the problem)
the user's input in textarea's gets erased as soon as up or down button is clicked.
For example: user adds a TR, that gets added at the bottom of the current list of tablerows, fills in the textarea and desides that the row (s)he filled should be ranked first, so she clicks the up arrow a couple of times, until it's on top.
The rank of the row is now #1 and the input is still in the textarea's.
My questions are:
Does anyone know how I can make the
rows update their ranking when the
user moves the row?
How do I maintain the user's input?
Any help is very much appreciated and if you have any other suggestions, please share them.
Code here: http://jsbin.com/eyefu5/edit - for some reason, the moving up and down doesn't work in js bin, it does however when I run it in my browser.
I updated your code to do what I think you were trying to do: http://jsbin.com/eyefu5/9/
My primary changes were to the following swap logic:
function swap(a, b){
b.before(a);
reorderRows(document.getElementById(TABLE_NAME), 0);
}
function getParent(cell){ return $(cell).parent('tr'); }
$('#diagnosetabel').on('click', '.upArrow', function(){
var parent = getParent(this);
var prev = parent.prev('tr');
if(prev.length == 1){ swap(parent, prev); }
});
$('#diagnosetabel').on('click', '.downArrow', function(){
var parent = getParent(this);
var next = parent.next('tr');
if(next.length == 1){ swap(next, parent); }
});
The biggest difference is that I switched the swap code to using jQuery's before method, which should take care of just about everything for you. I also added a call to the reorderRows method which you were already using. At the moment it starts at the beginning and reorders all the numbers after the swap, but you could narrow this down as needed because you know the only two rows which were modified.
Hope that helps!

Categories