Вопрос

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