Using iTextSharp with knockout JavaScript or other

2019-03-06 11:58发布

Ok, since I did find out that I can use iTextSharp to generate a PDF, I went that route.

I've got it to where it will save HTML with basic formatting to a PDF. That's not the problem.

What I'm needing to do now is take markup with knockout in it, and use the resultant HTML (i.e. the DOM) as my string to pass into the method that creates the PDF.

So, for instance, I have a table that's generated with knockout. I need to pass the DOM that was generated by knockout, as a string, into my C# controller, so that I can build the PDF.

Basically, if you look at what's generated here:

http://knockoutjs.com/documentation/foreach-binding.html

And if you read through Example 2 (it generates three bullet points), it illustrates the generation I'm talking about. In my case, I would want to take the generated bullet points and pass them into my controller -- HTML and all -- as a string, so that I can save them.

Any thoughts? I'm not even sure where to begin here, honestly.

1条回答
疯言疯语
2楼-- · 2019-03-06 12:43

The question applies to any JavaScript Framework that does MVC or MVVM. Mentioned above:

I need to pass the DOM that was generated by knockout, as a string, into my C# controller, so that I can build the PDF.

So am going to go with a simple working example to get this done in ASP.NET MVC. Never used knockout.js before, so going to get the DOM element and send Ajax request with jQuery.

The view, based on the example you referenced above: 1. Gets outerHTML of ul; 2. sends an Ajax request to the controller:

<h4>People</h4>
<ul id='wanted' data-bind="foreach: people">
    <li>
        Name at position <span data-bind="text: $index"> </span>:
        <span data-bind="text: name"> </span>
        <a href="#" data-bind="click: $parent.removePerson">Remove</a>
    </li>
</ul>
<button data-bind="click: addPerson">Add</button>
<button data-bind="click: getPdf">Get PDF</button>

@section scripts
{
<script src="~/Scripts/libs/knockout-3.4.0.js"></script>
<script src="~/Scripts/ajax/FileSaver.js"></script>
<script src="~/Scripts/ajax/jquery.binarytransport.js"></script>
<script src="~/Scripts/ajax/jquery-binary.js"></script>
<script type="text/javascript">
    function AppViewModel() {
        var self = this;

        self.getPdf = function (data, event) {
            $(this).downloadFile(
                '@Url.Action("Index", "DomToPdf")',
                { xHtml: $('#wanted').prop('outerHTML') }
            );
        }

        self.people = ko.observableArray([
            { name: 'Bert' },
            { name: 'Charles' },
            { name: 'Denise' }
        ]);
        self.addPerson = function () {
            self.people.push({ name: "New at " + new Date() });
        };
        self.removePerson = function () {
            self.people.remove(this);
        }
    }
    ko.applyBindings(new AppViewModel());
</script>
}

Notes:

  1. FileSaver.js isn't technically required, but saves a great deal of time not having to deal with different browser implementations. Reference Browser compatibility.
  2. BinaryTransport is required if using jQuery - sadly the framework doesn't support this feature out of the box last I checked. Other frameworks like AngularJS do support binary. Reference Sending and Receiving Binary Data.

jquery-binary.js is a simple jQuery Plugin I wrote for a couple of internal projects:

(function ($) {
    $.fn.downloadFile = function(url, data, requestType) {
        $.ajax({
            url: url,
            data: data,
            type: requestType || 'POST',
            dataType: 'binary'
        })
        .done(function(data, textStatus, jqXHR) {
            var type = jqXHR.getResponseHeader('Content-Type');
            var filename = jqXHR.getResponseHeader('Content-Disposition');
            filename = filename && filename.indexOf('attachment') > -1
                ? filename.replace(/(?:[^=])+=/, '')
                : 'file.bin';
            var blob = new Blob([data], { type: type });
            saveAs(blob, filename);
        })
        .fail(function(jqXHR, textStatus, errorThrown) {
            alert(errorThrown);
        })
        ;
        return false;
    };
}(jQuery));

And the controller: 1. parses the HTML string with XMLWorkerHelper; 2. returns the PDF.

[HttpPost]  // some browsers have URL length limits
[ValidateInput(false)] // or throws HttpRequestValidationException
public ActionResult Index(string xHtml)
{
    Response.ContentType = "application/pdf";
    Response.AppendHeader(
        "Content-Disposition", "attachment; filename=test.pdf"
    );
    using (var stringReader = new StringReader(xHtml))
    {
        using (Document document = new Document())
        {
            PdfWriter writer = PdfWriter.GetInstance(
                document, Response.OutputStream
            );
            document.Open();
            XMLWorkerHelper.GetInstance().ParseXHtml(
                writer, document, stringReader
            );
        }
    }

    return new EmptyResult();
}

Will let you decide how to deal with the inline styles. ;)

查看更多
登录 后发表回答