Datatable export Excel, customize height of row based on content - javascript

I've a datatable that export in excel.
Each html cell has some newlines (using br, p or div) so my export in excel need to strip all tags to be compatible with excel format, but keep newline.
After some research and help from datatables users, I get this good result:
https://jsfiddle.net/jx9hom27/4/
Now my problem is to set the height of each row to show all the text inside it.
Actually i've hardcode value of 60 for height, but it must be calculated based on content of row.
My idea was to get the size of splitData array: count is the number of lines.
The biggest value (for each row) must be multiplied for a costant (maybe 30) to get final result to apply to entire row.
The problem is that splitData variable is outside the function that set the style for excel....how can I solve this?
The code to set the height for a row is:
$('row:nth-child('+( firstExcelRow + rowLoop )+')', sheet).attr('ht', 60);
$('row:nth-child('+( firstExcelRow + rowLoop )+')', sheet).attr('customHeight', 1);

Answer myself. This is a working fiddle: https://jsfiddle.net/jx9hom27/5/
counting " inside the entire string, and divide by 2. Then for each row establish the max found and use it for the height
var countLines = ($('is t', this).text().match(/\"/g) || []).length / 2;

Related

How to setup a range in "Script lab" for Office Excel add-ins , using variables (and not hard coded values for cell references)

Script lab shows examples of how to get a range using rows, columns and cell values here:
https://learn.microsoft.com/en-us/office/dev/add-ins/excel/excel-add-ins-ranges-advanced
However, all the values in examples were hardcoded. I could not find on any page any example of how to use variables in the get range? Maybe its a simple javascript thing but I am totally new to it. Can anyone share an example of how to do say?
sheet.getRange("4:9") using variables for number 4 and number 9?
And if you do know that answer for above, can you also share, how to do this using cell references in the below example?
Assume that I can find the values of rows and column names and set them in some variables. How would I use it in below code replacing the values for G1, A1 and E1?
sheet.getRange("G1").copyFrom("A1:E1");
Thanking you in advance for your help!
P.S: I already tried searching on stack overflow with keywords for "script lab range variables" but found no answers. Hence asking here.
Excel.run(function (context) {
var sheet = context.workbook.worksheets.getItem("Sample");
// Group the larger, main level. Note that the outline controls
// will be on row 10, meaning 4-9 will collapse and expand.
sheet.getRange("4:9").group(Excel.GroupOption.byRows);
// Group the smaller, sublevels. Note that the outline controls
// will be on rows 6 and 9, meaning 4-5 and 7-8 will collapse and expand.
sheet.getRange("4:5").group(Excel.GroupOption.byRows);
sheet.getRange("7:8").group(Excel.GroupOption.byRows);
// Group the larger, main level. Note that the outline controls
// will be on column R, meaning C-Q will collapse and expand.
sheet.getRange("C:Q").group(Excel.GroupOption.byColumns);
// Group the smaller, sublevels. Note that the outline controls
// will be on columns G, L, and R, meaning C-F, H-K, and M-P will collapse and expand.
sheet.getRange("C:F").group(Excel.GroupOption.byColumns);
sheet.getRange("H:K").group(Excel.GroupOption.byColumns);
sheet.getRange("M:P").group(Excel.GroupOption.byColumns);
return context.sync();
}).catch(errorHandlerFunction);
So, I got an answer on this here: https://github.com/OfficeDev/office-js-docs-pr/issues/1699
Thanks to AlexJerabek (you can find his id on the above github link).
In summary:
The getRange method takes in a string representing a cell range in the worksheet, such as "A1:D4", "A:D" for just the columns, or "1:4" for just the rows. As long as your variables map to Excel rows or columns, you can use string concatenation to build the argument. You may need to use range.columnIndex, range.rowIndex, and range.getCell to translate to and from zero-based numbers into the letter-based columns.
Based on this answer, I tried below (and it worked):
const sheet = context.workbook.worksheets.getActiveWorksheet();
var firstrow = 1
var lastRow = 6
var range = sheet.getRange(firstrow + ":" + lastRow)

Best way to present editable tables

I've inherited the job of maintaining and developing an internal journaling system for registering inventory in tables on a local website. It is a website made in PHP, using jquery and handontable to list data from a MySQL database. All fields in the table are editable by the users.
Today the loading of data can be slow (10-15 seconds in the largest tables), which is mainly because of the loops used to populate the table and adjust the column sizes.
What do you think would be the best way to fix this issue? Should I reduce load times by fixing the loops, and keep handsontable as table library? Or should I scrap the old solution and implement something new?
Thanks :)
EDIT
I just saw you're using handsontable so my answer doesn't really provide a solution, as handsontable already uses a kind of list virtualization. I'll leave my answer anyway
Original Answer
What you can probably do is some sort of list virtualization, although this might be a bit tricky with table elements because you need absolute positioning and control of heights. Also it generally assumes that all rows have the same height.
The general idea is you only want to bother with rendering what's currently on the screen. Assuming you can fit 50 rows into your viewport at any time, you're measuring and updating 650 rows that don't matter. If you have 500000 rows, like in the fiddle, you're problem is going to be exponentially out of control.
Without knowing what you're doing exactly, here's a very general approach to the problem:
var elements = [];
var maxLength = 500000; // Number of elements we're going to generate
var itemHeight = 20; // We need a static row height for this to work
var totalHeight = itemHeight * maxLength; // The total height of the content
var $scrollContainer = $('#scroller-container'); // The container that will scroll
var $scrollContent = $('#scroller-contents'); // The content container for our items.
// We need to set the total height of the content so that absolute positioning works and the container receives the correctly sized scroll bar.
$scrollContent.css({ height: `${totalHeight}px` });
// Generate elements.
for (let i = 0; i < maxLength; i++) {
elements.push({
name: `item_${i}`,
value: `value_${i + 100}`
});
}
// By taking some measurements we will find out
// here exactly what items need to be rendered.
function obtainRenderableItems () {
// The size of our scrollable container
var containerHeight = $scrollContainer.height();
// How many items will fit inside the viewable area of our scrollable container
var viewport_count = Math.ceil(containerHeight / itemHeight);
// Where is it currently scrolled to.
var scrollPosition = $scrollContainer.scrollTop();
// The index of the first item in the viewable area
var start = Math.floor(scrollPosition / itemHeight);
// This calculation gives us a number of items to buffer on either side
// which prevents some janky behaviour when scrolling over yet unrendered items
var preScan = start - viewport_count <= 0 ? 0 : start - viewport_count;
// Basically we get the elements visible on the viewports by the current start
// index, and a buffer at the beginning and the end of the same amount of items
// in the viewport.
return elements.slice(preScan, preScan + (viewport_count * 3)).map((element, index) => {
return [preScan + index, element];
});
};
// Convert it to HTML, you can do whatever here, demo only.
function generateHTML (elements) {
return elements.map(el => {
let div = document.createElement('div');
div.className = 'element';
div.style.height = `${itemHeight}px`;
div.style.top = `${el[0] * itemHeight}px`;
div.innerHTML = `${el[1].name} - ${el[1].value}`;
return div.outerHTML;
}).join('');
}
// When we scroll we recalculate what items need to be shown and rerender them
// inside the page.
function onScroll (event) {
let items = obtainRenderableItems();
let htmlContent = generateHTML(items);
$scrollContent.html(htmlContent);
}
$scrollContainer.scroll(onScroll);
// Run at the beginning
onScroll();
The jQuery example above is based on a React component I wrote for exactly this purpose. You'll have to excuse my jQuery I haven't used it in years.
See the fiddle
There are a number of caveats with this approach. The major one being the row height must be the same for all rows, which is not workable for a number of situations. It also relies on a fixed container height, although the flex model can work around this.

.toFixed() Number If Too Big For Element

There is a unique number displayed to users of particular groups on their account dashboard. This number is fetched from the database and changed frequently. The number is less than 10,000,000.
What would be the best way to change the number from let's say "2,643,977" to the likes of "2.6M" if the width of the element starts to cut off the text, so lets say 100px can show 2,643,977, but reduced to 50px it shows 2.6M?
Question Clarification
The text element is a percentage, therefore smaller screens will cut of large numbers.
Is there a way obtain the inner elements text size?
On this inner text width being greater than element width, shorten the number, be it 1,000 - 1k, 1,000,000 - 1m...
You could use a js function like the following to check if your content is too wide, then use one of the functions provided in the other answers to shorten it if so.
This function accepts the text you want to check, the width you want to constrain it to in px, and the css classes your actual element uses (in a space-separated string). It returns a bool indicating whether the text fits.
function checkIfFits(text, width, classes) {
var s = document.createElement("span");
s.className = classes;
s.innerHTML = text;
if (parseInt(s.offsetWidth, 10) > width) return false;
return true;
}
How about this:
n2Display = (n<1000000) ? n : (n/1000000).toFixed(1) + 'M';
I'm assuming that n is a number, not a string here
function shorten(n){
var l=Math.floor(n.length/3-1);
return n.slice(0,n.length-l*3)+["","k","M","B"][l];
}
alert(shorten("123456"));
alert(shorten("123456789123"));
Note that this requires a String from Int to be passed.
It simply cuts off every block of 3 numbers and replaces it with its shortform:
123,456 => 123k
123,456,789 => 123M

Using data in HTML to display ChartJS Doughnut chart

I have a method where I am display products and when those products are filtered it shows X (amount that matches the filter) OF X (total number in that category)
I want a corresponding doughnut chart us ChartJS, I need it to use the data points which I currently have set in the html as...
<h4>
<strong>17</strong>
of 17
</h4>
I was thinking od possibly wrapping these 2 numbers in a div and giving it a dataset but I am unsure how I would correspond this to ChartJS.
You can see this my current progress here above the filters sidebar - http://bootsandlaces.net/products/footballs/
Can anyone help me ?
Well if you have to pull data from the DOM here is the possible solution (not the prettiest one, but it works... ). Give the corresponding HTML elements ids e.g. amount and total.
var total = document.getElementById("total").innerText;
var amount = document.getElementById("amount").innerText / total * 100;
var left = 100 - amount;
if ( amount && total ) {
data.datasets[0].data = [amount, left];
}
See this working fiddle
And do some styling...

how to get the column count in multi-column layout in webkit

I have some static HTML content. I need to format it using multiple columns, which are then presented as pages to the user. I use something quite simple which boils down to this:
bodyID = document.getElementsByTagName('body')[0];
bodyID.style.width = desiredWidth;
totalHeight = bodyID.offsetHeight;
pageCount = Math.ceil(totalHeight/desiredHeight);
bodyID.style.width = desiredWidth*pageCount;
bodyID.style.height = desiredHeight;
bodyID.style.webkitColumnGap = 0;
bodyID.style.webkitColumnCount = pageCount;
Now, my problem is that webKit honors the height attribute as it should and can create more columns than asked for if the content does not fit into pageCount number of columns.
I need to be able to get the number of columns afterwards to implement paging correctly. However the value of document.getElementsByTagName('body')[0].style.webkitColumnCount does not differ from pageCount even if there are more columns.
Any ideas, how to get the total number of rendered columns?
Thanks in advance.
The solution turned out to be much simpler than I thought. It was just a matter of getting the actual width of the page using bodyID.scrollWidth and then dividing by desiredWidth to get the actual number of pages.
Hope this helps somebody.

Categories