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;
};
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top