I wrote up some javascript code to export an excel grid that would only export the records filtered on the page by the filter tab and not the total value of records brought back. So for example if the record pulls back 23 records but you only have the view set to 10 you'll only get 10 records exported. I'm loading this function on the grid initialization as I usually do, however, for some reason it always seems to print out an empty sheet first with just the header rows, followed by the actual sheet I need. So the code is working but I can't figure out how to stop the first empty sheet from downloading as well. I don't believe I'm calling the method twice and there's definitely no looping logic... I've read over the Telerik documentation and I figured I'd come to stackoverflow since the Telerik support team just seems to refer everyone back to the same documentation
excelExport: function (e) {
var items = grid.dataSource.pageSize();
sheet = e.workbook.sheets[0];
var newSheet = sheet.rows.splice(0, items + 1) // +1 is for the header row
sheet.rows = newSheet
},
Related
This is more of an architectural issue, but basically, I have a server-side React app that renders a bunch of charts and tables with page breaks in-between, so that a puppeteer instance can open the page then print, and send that printed report back to the user in another app.
I need to be able to take some data that is normally rendered into a table format on this app, but make it printable so that the data extends as far as possible before a page break is required, then renders a new table past the page break (so it appears on a new page when printing), and continues until all of the data is rendered into the report. Essentially, I need pagination on a table, without the user interaction that pagination usually comes with.
The thing I'm struggling with is that the length of the data is dynamic, and so are the widths and heights of the rows.
Any suggestions on how to tackle this? The only thing I can think of so far is to basically hide the table, and measure the height of it after every row is attached, and compare that to the max height (the height of a standard letter size in pixels), and if it exceeds it, remove the row, add a page break, then start a new table.
Thanks in advance.
EDIT:
FYI, The solution mentioned here doesn't apply: How to apply CSS page-break to print a table with lots of rows?
This needs to be an entirely new table because I have custom headers and footers that are going above and below it (showing metadata like the name of the chart, how many rows are shown out of how many total, etc.), so it can't just be one continuous table that's split.
Here's a codepen with a shell of what I'm trying to do. If you open in debug view, and print it, you'll see in the print preview that the table is split up across two pages, but the footer I created will only be on the second page (where it needs to be on both pages, after the table). Additionally, the footer needs to display the dynamic count of rows that were able to fit on the page, so it can't be a static part of the table as a tfoot element. https://codepen.io/nicholaswilson/pen/GRWNzMa
So I'm trying to figure out now if I can mount the table to the DOM, but hide it, and calculate the height as I add rows to it so I can try my original method above. But I'm also open to other suggestions.
Alright, I think I got it. Still needs some tweaking (and there are probably more performant ways to do it) but this is my concept: https://codepen.io/nicholaswilson/pen/abJpLYE. Currently I'm splitting the tables after they've exceeded the height, but I'll be fixing that later. The concept is here.
Basically, the idea is to build a 4D array to represent the instances of tables that need to be rendered. Then in componentDidMount() and componentDidUpdate(), I can add new tables to the state as needed:
componentDidMount() {
const { tableData } = this.props;
if (this.state.currentRowIndex === 0) {
// just starting out
this.setState((state, props) => {
const tables = state.tables;
tables.push([tableData.data[state.currentRowIndex]]); // push first new table and first row
return {
tables,
currentRowIndex: state.currentRowIndex + 1,
currentTableIndex: 0
};
});
}
}
componentDidUpdate() {
const { tableData } = this.props;
if (this.state.currentRowIndex < tableData.data.length) {
this.setState((state, props) => {
const tables = state.tables;
const currentTableHeight = this.tableRefs[this.state.currentTableIndex]
.clientHeight;
console.log(
`Table ${this.state.currentTableIndex} height: ${currentTableHeight}`
);
if (currentTableHeight > MAX_TABLE_HEIGHT_IN_PIXELS) {
// push a new table instead based on this condition
tables.push([tableData.data[state.currentRowIndex]]);
return {
tables,
currentRowIndex: state.currentRowIndex + 1,
currentTableIndex: state.currentTableIndex + 1
};
} else {
tables[state.currentTableIndex].push(
tableData.data[state.currentRowIndex]
); // push new row to existing table
return {
tables,
currentRowIndex: state.currentRowIndex + 1,
currentTableIndex: state.currentTableIndex
};
}
});
}
}
See the codepen for the rest of the implementation.
I have a vlookup formula looking for names and returning the average for that person. I record these averages throughout the week, and identify them as W1, W2, W3, etc. in different columns. The problem is, the data I import only has ONE average on it, meaning that if I import it, it will override what I had already put in a few days ago using the same report, just an older version.
My question is, is there a way for me to stop the formula for W1 from updating when the data it is calling on changes? Essentially, to freeze the values? That way I don't have to keep adding new tabs to import the new data in an effort to save the history. The data is robust, and I am gathering more info than just averages, so I need the whole thing.
I wouldn't mind scripting something too that would help solve my issue, I just need some guidance.
Edit: thoughts on scripting a menu button that would copy, paste values only of a selected range that I could trigger right before I update the data?
I want to be able to select a range in my sheet, select the menu "Save Info", have it copy what I selected, and paste it right back where it already was. This will remove the formulas that would otherwise cause my values to change upon the new data import, and leave my history intact.
The following is what I have come up with so far, but I receive this error:
"TypeError: sourceSheet.getDataRange is not a function".
function saveInfo(){
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = spreadSheet.getActiveSheet
var sourceRange = sourceSheet.getDataRange();
var targetSheet = spreadSheet.getActiveSheet();
sourceRange.copyTo(targetSheet.getActiveRange());
}
function updateMenu() {
SpreadsheetApp.getActiveSpreadsheet().updateMenu('Save Info', generateMenu())
};
function onOpen() {
SpreadsheetApp.getActiveSpreadsheet().addMenu('Save Info', generateMenu());
};
function generateMenu() {
var entries = [{
name: "Save Data Before Update",
functionName: "saveInfo"
}];
return entries;
}
Any ideas??
Thanks!
Here you go:
function saveInfo() {
var range = SpreadsheetApp.getActiveSheet().getActiveRange();
var values = range.getDisplayValues();
range.setValues(values);
}
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Save Info')
.addItem('Save Data Before Update', 'saveInfo')
.addToUi();
}
The script converts data inside selected cells into a plain text.
But actually you don't need any script. You can just select the cells, press Ctrl+C and Ctrl+Shift+V.
Here is my onDataChanged() event. It's plugged into my Ag-Grid HTML. It does fire (3 times) but each time it only thinks there's 1 row being displayed. I'm using the serverSide row model and data coming in from the server is a bit slow, so I think that's the problem. I need to have this event fire when the data is changed though, so I can perform some actions when I have a full list of data. At the moment, again, it only ever thinks there's 1 row being displayed when I see that there are 20 in the list.
onDataChanged: function(event) {
var count;
console.log("data changed");
count = this.gridOptions.api.getDisplayedRowCount();
console.log(count);
}
// only ever outputs "1" even though I see 20+ items in the list
Because I'm using the serverSide row model, I'm using a serverDatasource to populate the data. Is there another way to detect when data has been changed? Thank you
I'm trying to use tablesorter.js but im running into issues when using ajax to update my table. I followed the code on this page but it doesn't seem to be working properly. One thing i also notice is that the code doesnt work properly even on the example website. When i click "append new table data" it adds the data to the table but it isn't sorting it correctly. If i copy the javascript code and paste it into the console, it works fine and sorts the table correctly. The code im using is the following:
var updateTableSort = function(){
var table = $('#transaction-table')
//tells table sorter that table has been updated
table.trigger("update");
//re sorts after table has been updated based on
//current sort patern
var sorting=table.get(0).config.sortList;
table.trigger('sorton', [sorting]);
}
Again, if i copy and past this into console it works fine, but when i have it in my success ajax function, it doesnt sort the table properly. Any help figuring out what the issue is would be greatly appreciated
Try my fork of tablesorter. It automatically resorts the table after an update:
// pass anything BUT false and the table will resort
// using the current sort
$("table").trigger("update", [false]);
Here is the updated append data to the table using ajax demo:
$(function() {
$("table").tablesorter({ theme : 'blue' });
$("#ajax-append").click(function() {
$.get("assets/ajax-content.html", function(html) {
// append the "ajax'd" data to the table body
$("table tbody").append(html);
// let the plugin know that we made a update
// the resort flag set to anything BUT false (no quotes) will
// trigger an automatic
// table resort using the current sort
var resort = true;
$("table").trigger("update", [resort]);
// triggering the "update" function will resort the table using the
// current sort; since version 2.0.14
// use the following code to change the sort; set sorting column and
// direction, this will sort on the first and third column
// var sorting = [[2,1],[0,0]];
// $("table").trigger("sorton", [sorting]);
});
return false;
});
});
I am exploring using DGrid for my web application. I am trying to have a table similar to this.
The code for the example above is here.
The table uses a memory store as the source of its data - the summary field there is what shows up when we click and expand each row.
I want the details(i.e the text shown when we click a row) to be fetched from the server on clicking on the row instead of being statically loaded along with rest of the page.
How do I modify the above code to be able to do that?
(My requirement is that of an HTML table, each row expandable on clicking, where the data on each expansion is fetched from the server, using AJAX for instance. I am just exploring dgrid as an option, as I get sortable columns for free with dgrid. If there is a better option, please let me know)
EDIT: Basically I am looking for ideas for doing that and not expecting anyone to actually give me the code. Being rather unfamiliar with Dojo, I am not sure what would be the right approach
If your ajax call returns html, you could place a dijit/layout/ContentPane in your renderer, and set the url of the contents you want to fetch in the ContentPane's href property. Assuming that your initial data (the equivalent of the example's memory store) would have a property called "yourServiceCallHref" containing the url you want to lazy load, your could try this :
require(["dijit/layout/ContentPane", ...], function(ContentPane){
renderers = {
...,
table: function(obj, options){
var div = put("div.collapsed", Grid.prototype.renderRow.apply(this, arguments)),
cp = new ContentPane({
href : obj.yourServiceCallHref
}),
expando = put(div, "div.expando", cp.domNode);
cp.startup();
return div;
}
});
If your service returns json, you could probably do something with dojo/request in a similar fashion. Just add your dom creation steps in your request callback and put them inside the div called "expando"...
Another option would be to replace the Memory store by a JsonRest store, and have the server output the same json format than the one you see on the Memory store. That means all the data would be fetched in a single call though...