This is driving me nuts and I'm sure I'm missing something simple because I have zero JS experience. I'm trying to call a js function from the code-behind of a razor page. I keep getting an error it can't find the function.
[2023-11-14T15:49:45.869Z] Error: Microsoft.JSInterop.JSException: Could not find 'saveFile' ('saveFile' was undefined).
findFunction/<@https://localhost:7261/_framework/blazor.server.js:1:497
findFunction@https://localhost:7261/_framework/blazor.server.js:1:465
E@https://localhost:7261/_framework/blazor.server.js:1:2606
beginInvokeJSFromDotNet/s<@https://localhost:7261/_framework/blazor.server.js:1:3494
beginInvokeJSFromDotNet@https://localhost:7261/_framework/blazor.server.js:1:3475
_invokeClientMethod/<@https://localhost:7261/_framework/blazor.server.js:1:72077
_invokeClientMethod@https://localhost:7261/_framework/blazor.server.js:1:72063
_processIncomingData@https://localhost:7261/_framework/blazor.server.js:1:70105
kt/this.connection.onreceive@https://localhost:7261/_framework/blazor.server.js:1:64508
connect/</o.onmessage@https://localhost:7261/_framework/blazor.server.js:1:48842
at Microsoft.JSInterop.JSRuntime.InvokeAsync[TValue](Int64 targetInstanceId, String identifier, Object[] args)
at HR_Taxonomy_Change_Management.Pages.Administration.GetSpreadsheet() in C:\Users\v-reedchris\source\repos\HR Taxonomy Change Management\HR Taxonomy Change Management\Pages\Administration.razor.cs:line 169
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState) blazor.server.js:1:116297
I can go into Inspector and it will open the js file in a browser window
I added the js file in the _Layout page with the other js file references.
I first put the file in wwwroot/js/saveFile.js
, then moved it to the root and no diff.
I've tried putting this js file first and putting it last in the loading order in the _Layout page.
I'm starting the download from a button click
private async Task GetSpreadsheet()
{
IBinaryWorkbookFormatProvider formatProvider = new Telerik.Windows.Documents.Spreadsheet.FormatProviders.OpenXml.Xlsx.XlsxFormatProvider();
var docService = await AdminDomain.GetReportAsync().ConfigureAwait(false);
byte[] bytesFromWorkbook = formatProvider.Export(docService);
//MemoryStream stream = new System.IO.MemoryStream(bytesFromWorkbook);
//stream.Position = 0;
// var result = new FileStreamResult(stream, "application/octet-stream");
await JSRuntime.InvokeAsync<object>("saveFile", "test.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", bytesFromWorkbook);
}
The code is creating a Telerik spreadsheet and sending it into another method that updates the data. Once it comes back it creates a byte[]
for the download.
Here is the js function to download a file
function saveFile(file, contentType, content) {
// Create the URL
const file = new File([content], filename, { type: contentType });
const exportUrl = URL.createObjectURL(file);
// Create the <a> element and click on it
const a = document.createElement("a");
document.body.appendChild(a);
a.href = exportUrl;
a.download = filename;
a.target = "_self";
a.click();
// We don't need to keep the object URL, let's release the memory
// On older versions of Safari, it seems you need to comment this line...
URL.revokeObjectURL(exportUrl);
}
After 2 days of banging my head, I found the source. There was an error earlier in the console:
Redeclaration of formal parameter file
. If you look back at the JS file, there is a parameter named "file" and there is anew File()
declaration in the body. I guess JS doesn't like this because as soon as I change the parameter name it was fine.