2

I am on Linux -both browser side & server side- with a recent Firefox 38 or 42 if that matters; this question gives more context, and the github GPLv3 project containing my code. It is not a usual Web application (it would have usually one, and perhaps a dozen, of simultaneous Web users). I am writing or generating both server & browser side code

Let's suppose I have some HTML5 code like

<div id="mydyndiv_id"></div>

I am making an AJAX request with JQuery. On success it should insert some (AJAX generated) HTML element, e.g. <b>bold</b> (in reality it is a much bigger HTML fragment with nested <span>-s whose content is dynamically generated from the POST argument of the AJAX request), into that div and call some other Javascript function doit, e.g. doit(42) only once just after the AJAX request (e.g. that function would clear some other <textarea> in my page, and the 42 argument is provided by the AJAX response). I can change code both on server side (e.g. alter the AJAX processing) and on browser side.

What is the most idiomatic way to achieve that?

  • making a JSON AJAX which contains both the inserted HTML & the function argument, so the AJAX response could be {"text":"<b>bold</b>", "data": 42}" of Content-type: "application/json" and the Javascript code would be

     $.ajax
      ({url: "/someajax",
        method: "POST",
        data: {"somearg": "foo"},
        datatype: "json",
        success: function(jsa) {
           $("#mydyndiv_id").html(jsa.text);
           doit(jsa.data);
        }});
    

    this is rather heavy, the server should double-encode HTML&JSON the HTML fragment: it needs first to construct the <b>bold</b> fragment -with HTML encoding, and then to construct the JSON object and send it.

  • making an HTML AJAX which has some <script> element. The AJAX response would be of Content-type: text/html and would contain <b>bold</b><script>doit(42)</script>, and the Javascript code would be

     $.ajax
      ({url: "/someajax",
        method: "POST",
        data: {"somearg": "foo"},
        datatype: "html",
        success: function(ht) {
           $("#mydyndiv_id").html(ht);
        }});
    

    this might be wrong, since the doit(42) function could be perhaps called more than once and is kept in the DOM and I don't want that

  • making a Javascript AJAX; the AJAX response would be of Content-type: application-javascript and would contain:

    $("#mydyndiv_id").html("<b>bold</b>");
    doit(42);
    

    with the AJAX invocation in Javascript being

     $.ajax
      ({url: "/someajax",
        method: "POST",
        data: {"somearg": "foo"},
        datatype: "script",
        success: function(jscode) { /* empty body */ }
       }) 
    

    This is brittle w.r.t. errors in doit(42) (see this question; the only debugging technique I found is lots of console.log and that is painful) and also requires double encoding on server side.

Of course, any other technique is welcome!

PS. If you are curious, the code is commit a6f1dd7514e5 of the MELT monitor (alpha stage) and you would try the http://localhost.localdomain:8086/nanoedit.html URL in your browser; this software (which is also a specialized HTTP server!) would have only very few simultaneous Web users (usually one, perhaps a dozen); in that sense it is not a usual web application. In my dreams it could become a workbench for a small team of (C & C++) software developers, and the GUI of that workbench would be their browser.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • I’d probably go with the first one. No idea what you mean by _“this is rather heavy, the server should double-encode HTML&JSON the HTML fragment”_ though. – CBroe Jan 08 '16 at 09:28
  • The first option seems to be the best. Quoting @CBroe "Why you think it's heavy?" the encoding has to be done and if you think encoding itself is "heavy" find an alternate solution for encoding but the ajax request. – Vinay Chittora Jan 08 '16 at 09:33

3 Answers3

1

These different approaches have pros and cons, but generally the first two options are more advisable, let's see:

  1. JSON AJAX

    First of all, working with templating on your server is the right approach. If you use this method you will be able to pass more flexible data from your server to your client as you can e.g. use {"text":"<b>bold</b>", "data": 42, "more_data": 43}".

    You are not bound to use just the data at the moment you initially create the service but expand passed data easily.

  2. HTML AJAX

    This method is simple and if you would like to have a service for every single piece of data you need to pass, rather than a service for multiple pieces, this is the preferable choice. In difference to the JSON AJAX method, you will not be able to expand here and if needed, you'll naturally have to create a new service for passing new data.

  3. Javascript AJAX

    Altough it is possible, tis method is rather unadivsable, as you can not maintain your application in a reasonable way, as your templating is client-side. See what Peter-Paul Koch says here:

    Although templating is the correct solution, doing it in the browser is fundamentally wrong. The cost of application maintenance should not be offloaded onto all their users’s browsers (we’re talking millions of hits per month here) — especially not the mobile ones. This job belongs on the server.

    Further reading : Why client-side templating is wrong.

Clemens Himmer
  • 1,340
  • 2
  • 13
  • 26
  • I'd personally prefer option one, as it is easily expandable and allows you to keep your services flexible. You'll have to deal with encoding, as you said, but in my opinion the con here is way less weighty than the pro. – Clemens Himmer Jan 08 '16 at 09:39
  • In my case I have very few Web users (usually one, perhaps a dozen). So I might be in the case where client-side templating might be acceptable. – Basile Starynkevitch Jan 08 '16 at 09:45
  • I don't really understand your comment about HTML AJAX. What are the services you are talking about? – Basile Starynkevitch Jan 08 '16 at 09:49
  • That is after all for you to choose, but keep in mind your application might get bigger (?). Quoting [the trump](http://www.brainyquote.com/quotes/quotes/d/donaldtrum153798.html#xiJwyACA3DwCTpEo.99) "You have to think anyway, so why not think big?". – Clemens Himmer Jan 08 '16 at 09:52
  • If i understood that question right, i think you mean the thing with the multiple services, let's assume you now need two html strings, option one allows you to `{"text":"bold", "data": 42, "more_text":"italic", "more_data": 43}"`, whilst option two would force you to have two services with `bold` and `italic` to keep your service reasonable. – Clemens Himmer Jan 08 '16 at 09:56
  • I'm just not understanding the *service* word in your answer. – Basile Starynkevitch Jan 08 '16 at 09:56
  • With _service_ i mean the server-side service that provides your client with data. – Clemens Himmer Jan 08 '16 at 09:58
1

First approach looks good for me, but generally it's a little bit ugly to transfer raw HTML via AJAX, if you have to transfer raw HTML it's better to use techniques called PJAX, see jquery-pjax plugin for more information of how to use and customize it.

From my point of view best approach would start using jquery-template to avoid transferring HTML over AJAX and start transfer only object witch would be rendered to template on frontend.Call doit method within handling success is ok until it use data provided in response.

Andriy Ivaneyko
  • 20,639
  • 6
  • 60
  • 82
1

I would rather go with a variation of first approach. But, it depends on the kind of generated HTML that you are currently returning from the server-side.

If it is a simple element, then you could just return a JSON object from server with one of the properties identifying the element.

For example, the response from the web-service would be like:

{'elem': 'b', 'text': 'bold', 'value': '42'}

And you consume that in the AJAX call like this:

$.ajax({ 
    datatype: "json",
    ...
    success: function(response) { 
        // create the required element client-side
        var elem = document.createElement(response.elem);
        // use other properties of the response object
        elem.textContent = response.text + doit(response.value);
        // add the element to your div
        $('#mydiv-1')[0].appendChild(elem);
    }
});

Where doit is the Javascript function that is already part of your client-side code-base and you just use the arguments returned by the web-service.


Alternatively, if your generated HTML is a complex fragment, then you need to identify common patterns and use client-side templates to transform the returned data into presentation.

For example, your client-side template may look like this:

<script type='text/template' id='tmpl'>
    <div><h3></h3><p></p><h5></h5></div>
</script>

Your web-service returns something like this:

{'title': 'title', 'text': 'paragraph', 'value': '42'}

And you consume that in the AJAX call like this:

$.ajax({ 
    datatype: "json",
    ...
    success: function(response) { 
        // clone the client-side template
        var template = $('#tmpl').html(), $elem = $(template);
        // append to your div
        $('#mydiv-2').append($elem);
        // populate the cloned template with returned object properties
        $elem.find('h3').text(response.title);
        $elem.find('p').text(response.text);
        $elem.find('h5').text(doit(response.value));
    }
});

This way you avoid returning generated HTML from your server and manage the presentation details at the client-side only. Your web-service needs not to know the presentational aspects and deals only with raw data (consuming or spewing). The client-side code gets data from the web-service and deals with using and/or presenting that data as part of the client-side app.

Demo for both the variations: https://jsfiddle.net/abhitalks/wuhnuv99/

Bottom-line: Don't transfer code. Transfer data. Code should then use that data.

Abhitalks
  • 27,721
  • 5
  • 58
  • 81