Can I add a spreadsheet row and then format it? - excel

I'm using cfspreadsheet to generate an excel spreadsheet. I'm adding rows one by one. Immediately after I add the row, I want to format it. Something like this:
<cfset SpreadsheetAddRow(mySpreadsheet, "hi,this,is,a,test") />
<cfset SpreadsheetFormatRow(mySpreadsheet,
{
fgcolor:red;
}) />
However, for the formatrow function, you have to provide a row number. Is there any way to format the row I just added without keeping a running counter of what row I'm up to?

The spreadsheet object itself knows how many rows are in it, similar to a query object.
<cfset CurrentRow = mySpreadsheet.RowCount />
Updating your example so that it works in ACF9:
<cfset SpreadsheetFormatRow(mySpreadsheet,
{
fgcolor = 'red'
}, mySpreadSheet.RowCount ) />

Related

PrimeFaces Extension - pe:sheet not Updating on Change

I am unsure how one updates a pe:sheet without directly editing a cell. Currently, when I make a modification to a cell, the data in the underlying list is not modified, and when I modify the underlying list, the front end is not updated. How should I user sync these two?
Here is the HTML:
<script type="text/javascript">
function sheetExtender() {
this.cfg.enterMoves = {row: 1, col: 0};
}
</script>
<pe:sheet id="mySheet" widgetVar="mySheet" value="#{myView.objList}"
var="obj" rowKey="#{obj.listIndex}" style="color: #757575;" height="400"
showRowHeaders="true" sortOrder="ascending" width="1000" extender="sheetExtender">
<p:ajax event="change" listener="#{myView.onChange}" update="mySheet"/>
<pe:sheetcolumn headerText="X" value="#{obj.x}" colType="checkbox"/>
<pe:sheetcolumn headerText="Y" value="#{obj.y}" />
<pe:sheetcolumn headerText="Z" value="#{obj.z}" />
</pe:sheet>
And the change handler:
public void onChange(SheetEvent event) {
Sheet sheet = event.getSheet();
List<SheetUpdate> updates = sheet.getUpdates();
SheetUpdate sheetUpdate = updates.get(0);
Integer rowId = (Integer) sheetUpdate.getRowKey();
Integer colId = (Integer) sheetUpdate.getColIndex();
String oldValue = (String) sheetUpdate.getOldValue();
String newValue = (String) sheetUpdate.getNewValue();
sheet.appendUpdateEvent(rowId, colId + 1, "DUMMY", null, "DUMMY");
sheet.commitUpdates();
}
I cannot find any particular method or widget function to keep these two in sync. If you know the correct method to do this, please let me know how you found it.
Thanks!
#Gumpf you have everything correct...you can only edit cells from the UI front end.
Changing the values in the backing bean will NOT reflect in the Sheet unless you update="sheet" to redraw the entire sheet again. The whole purpose of the Sheet is to not act like an Editable datatable where if you had a 10x10 editable DataTable it would submit 1000 values on Form Submit every time. If you had a 20x20 then it wouldn't work as Most servers stop you at 1000 Form Submit items.
So the Sheet is designed just to update a cell or cells at a time to update your backing bean WITHOUT having to redraw the sheet. What you want to do by updating your backing bean values and have it reflect in the Sheet the only way that will work is update="sheet" to force the whole Sheet to re-render after updating your backing bean values which might not be what you want. Or it might be....

Speedier alternative to spreadsheetaddrows in coldfusion?

I'm trying to create excel sheets using coldfusion's spreadsheetaddrows function. Because the tables I'm creating spreadsheets from can be very large, I'm creating the excel file in chunks to avoid timing out the server. Everything works fine at first, but as the process goes on it really slows down. For example, I was testing with a table that had 50,000 plus rows and almost 100 columns. Five hours later, the excel file hadn't even reached 10,000 rows. =T
It seems that spreadsheetaddrows is the problem according to google. Is there a faster and better way for me to do this?
<cfloop index="i" from="#start#" to="#end#" step="10">
<!-- variables -->
<cfset plus = #i# + 9>
<cfif #plus# gt #end#>
<cfset plus = #end#>
</cfif>
<!-- /variables -->
<!-- get i to i+9 records -->
<cfquery name="query" datasource="datasource">
select *
from ( select a.*, rownum rnum
from ( select * from table order by id) a
where rownum <= #plus#
)
where rnum >= #i#
</cfquery>
<cfif it's the first time doing this>
<!-- import from database query and save as excel sheet -->
<cfset spreadsheetAddRows(theSheet, query)>
<cfif reached the end of the query>
<cfspreadsheet action = "write"
overwrite = "true"
filename = "file.xls"
name="theSheet"
>
</cfif>
<cfelseif 2nd or more time doing this>
<!-- add new rows to excel object -->
<cfset spreadsheetAddRows(temp, query)>
<!-- overwrite existing xls file with new data -->
<cfspreadsheet action="write"
overwrite="true"
filename="file.xls"
name="temp"
>
</cfif>
I was curious so I tried another approach. The code below executes successfully and quickly with a small dataset. For what you are doing, this might still be slow and it might create java heap space problems, but it's probably worth a shot.
First - Get your query results into a spreadsheet.
<cfquery name="dbdata" datasource="dw">
select query goes here
</cfquery>
<cfspreadsheet action="write" overwrite="yes" query="dbdata" filename="#completeFileName#">
Then read that spreadsheet, do what you have to do, and update the file.
<cfscript>
abc = spreadsheetread(completefilename);
spreadsheetsetcellvalue(abc, 'new value', 2, 1);
spreadsheetwrite(abc, completeFileName, true);
</cfscript>
When I opened the file in excel, row 1 had column headers from the query results, and cell A2 contained 'new value'.

cfspreadsheet causing variable undefined error

I'm attempting to read a spreadsheet that has predefined charts on multiple tabs using cfspreadsheet, but when it comes to processing the data, I get variable is undefined.
I've used the example from Adobe - http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec17cba-7f87.html
I've also looked at other examples, but those examples use a combination of cfspreadsheet and poi or java and I would prefer to only use cfspreadsheet if possible. Any idea looking at my code below where the problem is coming from? Thanks.
<cfcontent type="application/vnd.ms-excel" reset="Yes">
<cfquery name="GetData" datasource="#request.dsn#">
SELECT *
FROM v_Occurrences
</cfquery>
<cfset strFileName = "OccurrenceData" & "#now().getTime()#" & "#UserID#">
<!---
<cfdump var="#GetData#">
--->
<cfset filepath = "file:///...OccurenceData.xls">
<!--- Write the two sheets to a single file --->
<cfspreadsheet
action ="read"
columnnames = "Occurence_Date,Full_Name_Client"
columns = "2"
excludeHeaderRow = "false"
headerrow = "1"
query="GetData"
rows = "2"
src ="#filepath#">
<cfscript>
OccurrenceData = SpreadsheetNew("Data");
Month = SpreadsheetNew("Month");
Person = SpreadsheetNew("Person");
SpreadsheetAddRows(occurrencedata,getdata);
</cfscript>
<!--- Write the two sheets to a single file --->
<cfspreadsheet
action="write"
filename="#filepath#"
name="OccurrenceData"
overwrite="true"
sheetname="Data" >
<cfspreadsheet
action="Update"
filename="#filepath#"
name="Month"
sheetname="Month">
<cfspreadsheet
action="Update"
filename="#filepath#"
name="Person"
sheetname="Person" >
<cfspreadsheet
action="read"
src="#filepath#"
sheetname="occurrencedata"
query="GetData">
Here's where the error occurs: variable Occurrence_Data is undefined
<cfscript>
SpreadsheetAddRow(OccurrenceData,"Date,Name",1,1);
SpreadsheetAddRow(OccurrenceData,
"#Dateformat(Occurrence_Date,'mm/dd/yyyy')#,#Full_Name_Client#",
2,1,true);
</cfscript>
<cfspreadsheet
action="write"
autosize="true"
filename="#strFileName#.xls"
overwrite="true"
password="password"
query="GetData"
sheetname="Data" >
Problem number 1. This is probably doing more harm than good.
<cfcontent type="application/vnd.ms-excel" reset="Yes">
Problem number 2. If you want to write two sheets to a single file, you don't do this:
OccurrenceData = SpreadsheetNew("Data");
Month = SpreadsheetNew("Month");
You do something like this:
OccurrenceData = SpreadsheetNew("Data");
// do stuff with this sheet
SpreadsheetCreateSheet(OccurrenceData, "Month");
SpreadSheetSetActiveSheetNumber(OccurrenceData, 2);
// do stuff with this sheet
SpreadSheetWrite(OccurrenceData, whateverYourFileNameIs);
I also suggest that you do one thing at a time. Write a bit of code, run it, and look at the results. Then add a bit more code, run it, and look at the results. This will help you isolate the code that causes the error.

Can't create a proper Excel spreadsheet with ColdFusion

I have a request from a client to generate an Excel spreadsheet from a query. I have the query kicking out the fields and I can generate the Excel file without a hitch. The problem comes when the client takes that Excel file and then tries to manipulate it.
The majority of the trouble comes from fields that should be marked as currency or dates. I am, with some struggle, able to generate a "real" date field. Before this Excel was not sorting the dates properly. I was able to call an Excel formula by using the code below. DateValue forces Excel to acknowledge this as a real date field. However, this fails when this file is manipulated through Excel.
<cfset SpreadsheetSetCellFormula(s
,"DATEVALUE(#Chr(34)##Replacement_ETD##Chr(34)#)"
, therow
, 9)>
The next problem is the currency field. I can't get Excel to acknowledge the values as a currency. It always comes up custom. When this is set, the SUM function won't work in Excel. You can add the fields individually like A1+B1+C1 = TOTAL. However, this won't be helpful when there are 200 rows.
I was able to get a suggestion from another CF programmer who had a similar situation. He generated the Excel file first with the proper headings and set the columns to their proper fields such as date and currency, etc.
The next step would be to fill in the fields row by row and they should be properly formatted.
Code:
<cfset filename = expandPath("./reports/arrivals.xlsx")>
<cfspreadsheet action="read" src = "#filename#" name = "s" >
<cfset therow = 0>
<cfoutput query="myExcel" startrow="1">
<cfset therow = myExcel.currentrow + 1>
<cfset SpreadsheetSetCellValue(s, Incumbent, therow, 1)>
<cfset SpreadsheetSetCellValue(s, Section, therow, 2)>
<cfset SpreadsheetSetCellValue(s, Position_Number, therow, 3)>
<cfset SpreadsheetSetCellValue(s, Position_Title, therow, 4)>
<cfset SpreadsheetSetCellValue(s, Incumbent_Emplyment_Type, therow, 5)>
<cfset SpreadsheetSetCellValue(s, Incumbent_ETD, therow, 6)>
<cfset SpreadsheetSetCellValue(s, Tour_Comments, therow, 7)>
<cfset SpreadsheetSetCellValue(s, Replacement, therow, 8)>
<cfset SpreadsheetSetCellValue(s, Replacement_ETA, therow, 9)>
</cfoutput>
<cfheader name="content-disposition" value="attachment; filename=Departures_(#DateFormat(now(),'mmddyy')#).xls">
<cfcontent type="application/msexcel" variable="#spreadsheetReadBinary(s)#" reset="true">
The data in the cells has already been properly formatted. When this file is generated and streamed to the user the columns are not formatted as expected.
Does anyone else know if this method will work or have a better suggestion on getting CF to generate a proper date and currency field for Excel to acknowledge?
Adobe ColdFusion v10 running on RHEL 5.
Per request here is some code using queryNew that will generate code dates and currency.
Step one: I created an Excel file with the first row frozen and it has the column header. Column one has been designated as the date the format is long date - mm/dd/yyy; Column two is Dollar which as been set to currency.
I read that file then fill in the rows and stream the file to the user for download.
<cfset filename = expandPath("./reports/Test.xlsx")>
<cfspreadsheet action="read" src = "#filename#" name = "s" >
<cfset myQuery = QueryNew("MyDate, Dollar", "Date, Decimal")>
<cfset newRow = QueryAddRow(MyQuery, 5)>
<cfset temp = QuerySetCell(myQuery, "MyDate", "03-11-2000", 1)>
<cfset temp = QuerySetCell(myQuery, "Dollar", "403.45", 1)>
<cfset temp = QuerySetCell(myQuery, "MyDate", "01-01-2009", 2)>
<cfset temp = QuerySetCell(myQuery, "Dollar", "603.22", 2)>
<cfset temp = QuerySetCell(myQuery, "MyDate", "09-21-2013", 3)>
<cfset temp = QuerySetCell(myQuery, "Dollar", "103.55", 3)>
<cfset temp = QuerySetCell(myQuery, "MyDate", "01-15-2005", 4)>
<cfset temp = QuerySetCell(myQuery, "Dollar", "3.33", 4)>
<cfset temp = QuerySetCell(myQuery, "MyDate", "07-22-2003", 5)>
<cfset temp = QuerySetCell(myQuery, "Dollar", "13.75", 5)>
<cfset therow = 0>
<cfoutput query="myQuery" startrow="1">
<cfset therow = myQuery.currentrow + 1>
<cfset SpreadsheetSetCellValue(s, DateFormat(MyDate, 'mm/dd/yyyy'), therow, 1)>
<cfset SpreadsheetSetCellValue(s, Dollar, therow, 2)>
#myQuery.currentrow# <br>
#myQuery.MyDate# <br>
#myQuery.Dollar# <br>
</cfoutput>
<cfheader name="content-disposition" value="attachment;
filename=Departures_(#DateFormat(now(),'mmddyy')#).xls">
<cfcontent type="application/msexcel" variable="#spreadsheetReadBinary(s)#" reset="true">
You can open the file in MS Excel or in Google Sheets. Test one, with the first row frozen, we should be able to sort on the date field. My results are: The dates are not being sorted properly. On column 2 with the currency, if we try to do a SUM that does work! This had not worked before but it does now.
Also, when I try to open the file I am given the warning that this file is corrupt and Excel will try to open it. I get no such warning on Google Sheets.
CF can be a bit quirky when working with date cells. Excel is pretty good about guessing the correct cell type when a value is entered manually. However, it is a little trickier with CF. Since CF is relatively typeless, it does not always match up values and cell types correctly. Using functions that utilize a query object, instead of SpreadsheetSetCellValue(), usually produces better results. Most likely because query objects contains both values and data types. Though as of CF11, SpreadsheetSetCellValue supports a new data parameter, which allows you to specify both the value and cell data type. Since you are using CF10, try using SpreadsheetAddRows to populate the values instead.
Regarding, the warning that the file is corrupt, it is caused by the fact that the actual file content and the file extension in the download code do not match. The code is reading in an .xlsx file, but the download claims it is an .xls (application/msexcel) file. To get rid of the error, make sure the two match.
Here is a working example tested with CF11
<!---
Test.xlsx contains two columns, with headers on row 1
- Column A format: *m/d/yyyy
- Column B format: number with 2 decimal places
--->
<cfspreadsheet action="read" src="c:/temp/Test.xlsx" name="sheet" >
<cfset myQuery = QueryNew("")>
<cfset QueryAddColumn(MyQuery, "Dollar", "Decimal", [ 403.45, 703.22, 103.55, 3.33, 13.75] )>
<cfset QueryAddColumn(MyQuery, "MyDate", "date", [ parseDateTime("2000-03-11", "yyyy-mm-dd")
, parseDateTime("2009-01-01", "yyyy-mm-dd")
, parseDateTime("2013-09-21", "yyyy-mm-dd")
, parseDateTime("2005-01-15", "yyyy-mm-dd")
, parseDateTime("2003-07-22", "yyyy-mm-dd")] ) >
<cfset spreadsheetAddRows(sheet, myQuery)>
<cfset spreadsheetFormatColumn(sheet, {dataFormat="m/d/yy"}, 1)>
<cfset spreadsheetFormatColumn(sheet, {dataFormat="##,####0.00"}, 2)>
<cfheader name="content-disposition" value="attachment; filename=Departures_(#DateFormat(now(),'mmddyy')#).xlsx">
<cfcontent type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" variable="#spreadsheetReadBinary(sheet)#" reset="true">

Export to excel, Column header display error in ColdFusion

I am trying to get account numbers by a query and displaying those account numbers as column header after exporting into excel.
The following code is working fine but displaying wrongly:
<cfset variables.setHeader = "">
<cfoutput query="myLoop">
<cfset variables.setHeader = variables.setHeader & ", C_#myLoop.acc#>
</cfoutput>
The above code is displaying column header (just one sample data I am showing here) but it is showing as 'C_000391'. Which is wrong! For display, 'C_' should not be with account number, it is the account number which should create the column header alone. However, as soon as I am taking off 'C_' from infront of #myLoop.acc# it is giving the following error:
The column name 000391 is invalid.
Please let me know what will be the best solution(s) to resolve this toughest problem on earth :)
Thanks
Seems you are trying a form a comma separated list , try below , no looping required.
< cfset variables.setHeader = ValueList(myLoop.acc) >

Resources