Scenario: In our application a user can create a invoice by filling in certain fields on a Knockout view. This invoice can be previewed, via another Knockout page. I want to use the preview url within our PDF creator (EVOPdf), so we can provide the user with a PDF from this invoice.
To preview the invoice we load the data (on document ready) via an ajax-request:
var InvoiceView = function(){
function _start() {
$.get("invoice/GetInitialData", function (response) {
var viewModel = new ViewModel(response.Data);
ko.applyBindings(viewModel, $("#contentData").get(0));
});
};
return{
Start: _start
};
}();
My problem is within the data-binding when the PDF creator is requesting the url: the viewModel is empty. This makes sense because the GetInitialData
action is not called when the PDF creator is doing the request. Calling this _start
function from the preview page directly at the end of the page does not help either.
<script type="text/javascript">
$(document).ready(function() {
InvoiceView.Start();
});
</script>
Looking at the documentation of EvoPdf, JavaScript should be executed, as the JavaScriptEnabled
is true
by default: http://www.evopdf.com/api/index.aspx
How could I solve this, or what is the best approach to create an pdf from a knockout view?
Controller action code:
public FileResult PdfDownload(string url)
{
var pdfConverter = new PdfConverter();
// add the Forms Authentication cookie to request
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
pdfConverter.HttpRequestCookies.Add(
FormsAuthentication.FormsCookieName,
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
}
var pdfBytes = pdfConverter.GetPdfBytesFromUrl(url);
return new FileContentResult(pdfBytes, "application/pdf");
}
Javascript:
var model = this;
model.invoiceToEdit = ko.observable(null);
model.downloadInvoice = function (invoice) {
model.invoiceToEdit(invoice);
var url = '/invoice/preview';
window.location.href = '/invoice/pdfDownload?url=' + url;
};
The comment of xdumaine prompted me to think into another direction, thank you for that!
It did take some time for the Ajax request to load, but I also discovered some JavaScript (e.g. knockout binding) errors along the way after I put a ConversionDelay
on the pdf creator object
pdfConverter.ConversionDelay = 5; //time in seconds
So here is my solution for this moment, which works for me now:
To start the process a bound click event:
model.downloadInvoice = function (invoice) {
var url = '/invoice/preview/' + invoice.Id() + '?isDownload=true';
window.open('/invoice/pdfDownload?url=' + url);
};
which result in a GET
resquest on the controller action
public FileResult PdfDownload(string url)
{
var pdfConverter = new PdfConverter { JavaScriptEnabled = true };
// add the Forms Authentication cookie to request
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
pdfConverter.HttpRequestCookies.Add(
FormsAuthentication.FormsCookieName,
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
}
pdfConverter.ConversionDelay = 5;
var absolutUrl = ToAbsulte(url);
var pdfBytes = pdfConverter.GetPdfBytesFromUrl(absolutUrl);
return new FileContentResult(pdfBytes, "application/pdf");
}
The Pdf creator is requesting this action on the controller, with isDownload = true
(see bound click event):
public ActionResult Preview(string id, bool isDownload = false)
{
return PartialView("PdfInvoice", new InvoiceViewModel
{
IsDownload = isDownload,
InvoiceId = id
});
}
Which returns this partial view:
PartialView:
// the actual div with bindings etc.
@if (Model.IsDownload)
{
//Include your javascript and css here if needed
@Html.Hidden("invoiceId", Model.InvoiceId);
<script>
$(document).ready(function () {
var invoiceId = $("#invoiceId").val();
DownloadInvoiceView.Start(invoiceId);
});
</script>
}
JavaScript for getting the invoice and apply the knockout bindings:
DownloadInvoiceView = function() {
function _start(invoiceId) {
$.get("invoice/GetInvoice/" + invoiceId, function(response) {
var viewModel = new DownloadInvoiceView.ViewModel(response.Data);
ko.applyBindings(viewModel, $("#invoiceDiv").get(0));
});
};
return {
Start: _start
};
}();
DownloadInvoiceView.ViewModel = function (data) {
var model = this;
var invoice = new Invoice(data); //Invoice is a Knockout model
return model;
};