DataTables Excel style based on cell class - excel

I'm using DataTables plugin to export a monthly calendar view; I need to set a cell style inside the excel file based on the class of the corrisponding cell in the DataTables calendar view.
I know how to style the exported excel file using the customize: function( xlsx, index ) {} however I'm not able to find, in the examples I saw on the forum, a way to set the style of the excel cell based on the class of the corrispondig cell in the DataTables view.
I have created my own xml style like this:
customize: function( xlsx, index ) {
var new_style = '<?xml version="1.0" encoding="UTF-8"?>'+
'<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" '+
'xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" '+
'xmlns:x14ac="https://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">'+
'<numFmts count="2">'+
'<numFmt numFmtId="164" formatCode="0.0" />'+
'<numFmt numFmtId="165" formatCode="\d\d\d" />'+
'</numFmts>'+
'<fonts count="4" x14ac:knownFonts="1">'+
...... a lot of stuff here ....
'<extLst>'+
'<ext xmlns:x14="https://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}">'+
'<x14:slicerStyles defaultSlicerStyle="SlicerStyleLight1" />'+
'</ext>'+
'</extLst>'+
'</styleSheet>';
This is a styles.xml as you can find iside an .xlsx file if you change the extension to .zip
and than unzip it.
To apply the styles to the excel cell I'm doing:
xlsx.xl['styles.xml'] = $.parseXML(new_style);
var sheet = xlsx.xl.worksheets['sheet1.xml'];
$('row:first c', sheet).attr( 's', '1' );
$('row:eq(1) c', sheet).attr( 's', '2' );
$('row:eq(2) c', sheet).attr( 's', '3' );
}
What I need to do is something like:
$('row:eq(3) c', sheet).hasClass('custom').attr( 's', '1' ); //not working
or:
$('row c[r^="B"]', sheet).each( function () {
if ( $(this).hasClass('custom') ) {
$(this).attr( 's', '4' );
}
}); // not working
Basically I'm working on a row of cells (more than 30, so I have AA, AB, AC and so on) and i need a method to discriminate some of them to add a different style, as you can see the header has 31 cells with calendars day/name and i would like the colums with Saturday and Sunday to be with a gray background like they are in the datatable table.
This is the datatable:
And this is the excel file so far, i need the Sab and Dom columns to be gray
UPDATE * with #andrewjames solution and #georg solution for double letters posted here Convert numbers to letters beyond the 26 character alphabet
function colName(n) {
var ordA = 'A'.charCodeAt(0);
var ordZ = 'Z'.charCodeAt(0);
var len = ordZ - ordA + 1;
var s = "";
while(n >= 0) {
s = String.fromCharCode(n % len + ordA) + s;
n = Math.floor(n / len) - 1;
}
return s;
}
var cellIndexes = tabellaOre.cells(".Sab, .Dom").indexes();
for (var i = 0; i < cellIndexes.length; i++) {
var cellIndex = cellIndexes[i];
var tblRow = cellIndex['row']+4; //changed to my needs
var tblCol = cellIndex['column']; //removed +1
// var xlCol = String.fromCharCode(64 + tblCol); changed with follow
var xlCol = colName(tblCol);
// .... previous stuff here, it was already in a for() loop, so still working
$('row c[r='+xlCol+tblRow+']', sheet).attr('s','12');
}
and this is the result:
As #andrewjames correctly says in his anwer:
My naive implementation will fail for more than 26 columns:
The colName(n) function solved the problem.
One last step would be to style the cells with thick borders with their own style, but I can consider this as solved.

Assumptions:
It sounds like you already have your embedded styles.xml customized the way you want it, and you therefore know what style index values to refer to, from its <cellXfs> section.
It sounds as if the missing piece is knowing which DataTables cells have been given which CSS style classes, so you can select the relevant <cellXfs> indexes for the equivalent Excel cells.
Proposed Approach:
This takes advantage of the fact that the customize function can be passed 3 variables:
the XML files for the Excel
an object representing the button object(s)
the DataTable instance
We use this last one to map classes in the HTML to cells in Excel:
var table = $('#example').dataTable({
dom: 'Bfrtip',
buttons: [
{
extend: 'excelHtml5',
title: '', // no title row
text: 'Excel',
customize: function ( xlsx, btnObj, tableInst ){
var cellIndexes = tableInst.cells(".mycustom").indexes();
for (var i = 0; i < cellIndexes.length; i++) {
var cellIndex = cellIndexes[i];
var tblRow = cellIndex['row']+1; // adjusted from 0 indexed
var tblCol = cellIndex['column']+1; // adjusted from 0 indexed
var xlCol = String.fromCharCode(64 + tblCol); // DANGER: fails after Z (26 columns)
alert('table row ' + tblRow + ' col ' + tblCol
+ ' maps to excel cell ref ' + xlCol + tblRow);
}
}
}
]
});
This doesn't apply any styles, it just shows you how to determine which DataTable cells have been given a specific style, and translates those cell indexes into Excel-style cell references.
So, for the following sample data...
<table id="example" class="display nowrap dataTable cell-border" style="width:100%">
<thead>
<tr>
<th>Head 1</th>
<th>Head 2</th>
<th>Head 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Row 1 column 1</td>
<td>Row 1 column 2</td>
<td>Row 1 column three</td>
</tr>
<tr>
<td>Row 2 column 1</td>
<td>Row 2 column 2</td>
<td>Row 2 column 3</td>
</tr>
<tr>
<td>Row 3 column 1</td>
<td class="mycustom">Row 3 column 2</td>
<td>Row 3 column 3</td>
</tr>
<tr>
<td>Row 4 column 1</td>
<td>Row 4 column 2</td>
<td>Row 4 column 3</td>
</tr>
<tr>
<td class="mycustom">Row 5 column 1</td>
<td>Row 5 column 2</td>
<td>Row 5 column 3</td>
</tr>
</tbody>
</table>
...the above code generates 2 alerts as follows:
table row 3 col 2 maps to excel cell ref B3
table row 5 col 1 maps to excel cell ref A5
You can then use the B3 and A5 values in the selectors you need - for example:
$('c[r=B3] t', sheet).attr( 's', '25' );
Additional notes:
The DataTables cells().indexes() function is described here.
My naive implementation will fail for more than 26 columns:
var xlCol = String.fromCharCode(64 + tblCol);
But it shouldn't be too hard to extend that for Excel columns "AA", "AB", and so on, if needed.
If you want to work at the column (or row) level, instead of the cell level, I have not tried that - but it should be a slightly simpler version of the above.

Related

WebScraping: Get nested element in HTML Table

Hi I am new to webscraping and got stuck on getting nested html element tag in a table, here is the html code I get from the url http://www.geonames.org/search.html?q=+Leisse&country=FR:
<table class="restable">
<tr>
<td colspan=6 style="text-align: right;"><small>1 records found for "col de la Leisse"</small></td>
</tr>
<tr>
<th></th>
<th>Name</th>
<th>Country</th>
<th>Feature class</th>
<th>Latitude</th>
<th>Longitude</th>
</tr>
<tr>
<td><small>1</small> <img src="/maps/markers/m10-ORANGE-T.png" border="0" alt="T"></td>
<td>Col de la Leisse<br><small></small><span class="geo" style="display:none;"><span class="latitude">45.42372</span><span class="longitude">6.906828</span></span></td>
<td>France, Auvergne-Rhône-Alpes<br><small>Savoy > Albertville > Tignes</small></td>
<td>pass</td>
<td nowrap>N 45° 25' 25''</td>
<td nowrap>E 6° 54' 24''</td>
</tr>
<tr class="tfooter">
<td colspan=6></td>
</tr>
</table>
This is the code for only one row to make things simple, but in my case I iterate over each row and check if the text of <td> element equal to a target value, if true I scrape the value of <span> element with class longitude and latitude. In my case I want to get the row with value Col de la Leisse
Here is my code: (not good)
soup = BeautifulSoup(response.text, "html.parser")
table = soup.findAll('table')[1] # second table
rows = table.find_all('tr')
target = "Col de la Leisse"
longitude, latitude = 0
for row in rows:
cols=row.find_all('td')
# I am stuck here...
# if cols.text == target:
# ...
Result:
longitude = 6.906828
latitude = 45.42372
With bs4 4.7.1 you can use :has and :contains to ensure row has an a tag element with your target string in.
target = 'Col de la Leisse'
rows = soup.select('.restable tr:has(a:contains("' + target + '"))')
for row in rows:
print([item.text for item in row.select('.latitude, .longitude')])
You can of course separate out .latitude and .longitude if you think they will not both be present, or if can occur in different order

Xpages - Repeat control with SSJS

I am building a history tracker, that continues to work with legacy code in non xpage apps. For xpage apps, I simply call the below function. This works fine:
function AddObjectivesHistoryItem(doc, dt, action, username){
var ArrDocHistory:array = doc.getItemValueArray("History");
if(ArrDocHistory.length < 1){
// This should always return an object as it is created when an objectives
document is first
// created but do this check to be safe and create an array if for some
reason it doesnt exist
ArrDocHistory = [dt+"|"+action+"|"+username];
}else{
// append new value to the array
ArrDocHistory.push(dt+"|"+action+"|"+username);
}
return ArrDocHistory;
}
The issue I have, is splitting and displaying the 3 data types. I have a 3 computed fields, which is date, username and status in a table within a repeat control, code, including first column and computed field below, this should be all values prior to the first pipe delimiter for each multi value.:
<table class="table table-hover">
<thead>
<tr>
<th>Date</th>
<th>Action</th>
<th>Username</th>
</tr>
</thead>
</table>
<table class="table table-hover">
<xp:repeat value="#{document1.History}" var="row">
<tbody>
<tr>
<td>
<xp:text escape="true" id="computedField1">
<xp:this.value><![CDATA[#{javascript:var ArrDocHistory:array = document1.getItemValueArray("History");
print ("LEN: " + ArrDocHistory.length);
var test:array = new Array();
for (i=0; i<ArrDocHistory.length; i++){
var split = ArrDocHistory[i].split("|");
test.push(split[0]);
//return split[i]
}
return test}]]></xp:this.value>
</xp:text></td>
However, what this displays is all the values, in a line, seperated with a comma. For example, "value1, value2, value3" where as I expected, and need, and can't seem to be able to get each value to display on its own, in a new row. For example:
value1
value2
value3
I know I'm doing something silly, but am currently suffering from tunnel vision so any pointers greatly appreciated.
You are returning Test, to a single computed field. Change your repeat to return the array 'test', (it gets the value like you do, than split it to the array you have). Than the computed field returns one of the elements of test.
Put a page break after the computed field to see it the way you want.

Angular 2 Primeng Datatable- single (exclusive) row selection on click

I am using a Primeng datatable to display editable entries from a database. When a row is clicked, a new component is displayed to allow editing of the associated data for the row. When a new row is clicked, the component will show the data for that row instead, and so on.
Obviously, this is exclusive- only one row can be selected at a time. I want to highlight the currently 'active' row in a different color, so when a new row is selected any previously hightlighted row should be reset to its default color.
At present I have the following in my template:
<p-dataTable [value]="rowData" [rowStyleClass]="highlightSelectedRow" [rowHover]="true" (onRowClick)="rowClick($event)">
<ng-template ngFor let-col [ngForOf]="cols">
<p-column [field]="col.field" [header]="col.header"></p-column>
</ng-template>
</p-dataTable>
...and in my component:
rowClick($event){
const rowData = $event.data;
rowData.selected = !rowData.selected;
// and do some other stuff...
}
highlightSelectedRow(rowData: any, rowIndex: number){
return (rowData.selected) ? 'selected' : '';
}
...CSS to style selected row:
.row.selected {
background-color: red;
}
This works insofar as highlighting the clicked row, but highlighted rows remain highlighted, and I'm wondering about the best way to reset them.
I guess I could loop through all the rows of data and find the row with selected property set to true and set it to false, but this seems a rather inefficient way of doing it, especially if I am dealing with many thousands of rows of data.
Am I missing a better, built-in way of doing this with Primeng?
This works insofar as highlighting the clicked row, but highlighted rows remain highlighted, and I'm wondering about the best way to reset them.
Create a class for unselected row such as
.row.unselected {
background-color: white;
}
and affect this class whenever you unselect a row
highlightSelectedRow(rowData: any, rowIndex: number){
return (rowData.selected) ? 'selected' : 'unselected';
}
See this simular SO question or Plunker for more details.
I have this scenario:
HTML:
<p-dataTable class="table table-responsive" #dealData [value]="dealList" sortMode="multiple"
[(selection)]="selectedDeals"
dataKey="hncId" scrollable="true" scrollHeight="540px" [rowStyleClass]="highlightSelectedRow"
styleClass="table" [paginator]="true" [rows]="100" [pageLinks]="5">
....
<p-column [header]="'Details' | translate" [style]="{'text-align': 'center'}">
<ng-template let-row="rowData" pTemplate type="body">
<button type="button" (click)="showDealDetail(row)" (mouseover)="row.selected = !row.selected" (mouseout)="row.selected = !row.selected" class="viewButton" pButton
[label]="'View' | translate"></button>
</ng-template>
</p-column>
...
</p-dataTable>
Component:
highlightSelectedRow(rowData: any, rowIndex: number) {
return (rowData.selected) ? 'rowSelected' : '';
}
CSS:
tr.rowSelected > td {
background-color: #ebf8ee !important;
}
This highlights one row at a time. Key is (mouseover) and (mouseout).

Change Row Color Based On Condition In C#

Hi I'm very new to C#. But I have this weblist that print out some result, that I'm trying to change, What I'm trying to do is to make an if statement. If row[4] is less then [2] then the row should be red. I have tried to copy some of the code.
//##############Print result##############
foreach ( string[] row in dataResult)
{
//Birn.Util.cellDecimalPrinter(row[0],"#,0.0")
//<tr id="PopUp" onclick="OpenWindow('file.aspx?querystr=row[0]&date=textbox.text','500','300')">
%>
<tr>
<td><%=row[0]%></td>
<td><%=row[1]%></td>
<td><%=row[2]%></td>
<td><%=row[3]%></td>
<td><%=row[4]%></td>
</tr>
<%
}
You can change the style of the row using CSS. I have assumed that whatever you are comparing in rows 2 and 4 is a string representation of a Decimal: adjust the Convert.ToDecimal to the appropriate type if needed:
foreach ( string[] row in dataResult)
{
string redStyle= "";
If (Convert.ToDecimal(row[4]) < Convert.ToDecimal(row[2])) {
// ideally you would have a style defined in a separate CSS file for this
redStyle = " style=\"background-color: Red; color: White\"";
}
}
%>
<tr<%=redStyle%>>
<td><%=row[0]%></td>
<td><%=row[1]%></td>
<td><%=row[2]%></td>
<td><%=row[3]%></td>
<td><%=row[4]%></td>
</tr>
<%
}

How to make a bilingual site without creating a separate ASP page for each language?

I need ideas on how to go about table layout problem.
I want to set different width of the columns dependent on the picked language.
You can have language specific CSS, and then simply load the appropriate CSS based on language.
In the CSS you can add styles to your table for defining the layout.
A variable switch, such as:
<%
dim columnWidth
if session("lang") = "eng" then
columnWidth = 50
else
columnWidth = 100
end if
%>
<table>
<tr>
<td width="<%= columnWidth %>px">[content]</td>
</tr>
</table>
For c#, the code would be:
<%
private int columnWidth;
if (session("lang") == "eng") {
columnWidth = 50;
} else {
columnWidth = 100;
}
%>
Use if-else inside scriplet based on the currently selected language and place appropriate "td" tags.
Hope this is what you are looking for !

Resources