Dynamic loading in JS

I'm currently working on a Single Page Application (SPA) that reloads parts of a single page rather than switching between pages. Maybe harder to code than traditional pages, but there are many upsides, for example you only load static CSS/JS once and you can maintain persistent WebSocket connections that would need to be broken / re-made on every traditional page transition.

I've noticed some interesting 'features' of this mechanism that weren't apparent before I actually started playing with the code. Whereas you can load static JS libraries once (and there are many reasons why this is good) you can also load additional JS on-the-fly as and when it's needed.

Consider for example a block of HTML you want to load in;

<div>
  This is my new block of html
  ((lots of other stuff, widgets etc))
</div>

If you have say a widget embedded inside the block, it may be the widget needs to be initialised, in which case we can do this;

Example # 1
<div>
  This is my new block of html
  ((lots of other stuff, widgets etc))
</div>
<script defer>
  LIBRARY.widget_init();
</script>

Assuming the widget initialisation is loaded statically as "widget_init" inside the LIBRARY namespace. Alternatively we could just include the code in-line. Point is that it all wraps up quite neatly. In practice we have a DIV placeholder on the screen into which we load, which looks something like this;

<div id="dynamic-container"></div>

And when we want to load content (I'm using IONMAN but any sort of AJAX loader would do the trick) we do something like this;

var success = function(data) {$('#dynamic-container').html(data.html);};
var failure = function(data) {$('#dynamic-container').html("no data"); };
var argv = {template:'my_content',vars:['Req'],'request_id':request_id};
ionman.call('page_template',argv,success,failure);

In this instance, my__content is my template, vars is a list of server side database entries to lookup and make available to the template, and 'request__id' is a variable we're going to send to the server to facilitate the database lookup. Essentially if all goes well, success is called with data.html set to (Example#1) above, and this is inserted into the main page 'inside' the div with id dynamic-container, and when the loading is complete, the script code, if available, is executed.

Example # 2

Just to press the point, IONMAN uses JINJA2 for templates, so this is an example page we can load in dynamically using the above mechanism;

{% from "request_macros.j2.html" import request_body,request_pdf %}

<section style="padding:100px; padding-bottom:30px; min-height:1200px">
    <div class="container">
        <div class="row text-center">
            <div class="col-sm-12">
                <h1>Candidate Request</h1>
                <h4>Details of Position you are trying to fill</h4>
            </div>
        </div>
        <P/>
        <div class="row">
            <div class="col-sm-5">
                <div id="request-body">
                    {{ request_body(item) }}
                </div>
            </div>
            <div class="col-sm-7">
                <div id="req-view">{{ request_pdf('request') }}</div>
            </div>
        </div>
    </div>
</section>
<script defer>
   REQUEST.init();
</script>

In real-life this page displays two columns side-by-side, one is filled by request__body which is a form filled by a database request, and the other is filled by request__pdf which uses PDF.js to display a PDF file associated with the specified request.

It's almost a 'manageable' web page!

Moving on again, just to show this thing scales while remaining readable, request__body looks like this;

Next level down
{% macro request_body(item) -%}
<form class="frame-full">
 {{ request_field_title(item)     }}
 {{ request_field_location(item)  }}
 {{ request_field_client(item)    }}
 {{ request_field_salary(item)    }}
 {{ request_field_essential(item) }}
 {{ request_candidates(item)      }}
 {{ request_upload(item)          }}
 <p/>
 <div class="row" style="padding-bottom:8px">
  <div class="col-sm-12">
   {% if not item.request._id %}
     {{ btn(item,'request','Submit','REQUEST.save','alt',true,'success')}}
   {% else %}
    {% if item.editmode %}
     {{ btn(item,'request','Save',"REQUEST.save",'save',true,'success') }}
     {{ btn(item,'request','Cancel','NAV.pop','remove',false,'warning') }}
    {% else %}
     {{ btn(item,'request','Edit',"REQUEST.edit",'edit',true,'success') }}
     {{ btn(item,'request','Cancel','NAV.pop','remove',false,'warning') }}
    {% endif %}
   {% endif %}
  </div>
 </div>
</form>
<script defer>
   REQUEST.init(item);
</script>
{%- endmacro %}

Obviously this relies on yet more macros, but these are all relatively simple, and it does leave a very readable skeleton which can be modified with ease .. (it'll get smoother as things progress)

Just to come full-circle, as an example of one of these macros, here's the definition for request__field__essential which is a tokenfield widget. This is a relatively complex widget and needs to be pre-populated in order to display pre-existing data.

And this is the basement ...
{% macro request_field_essential(item) -%}
<div class="row">
  <div class="col-sm-12">
    <div class="form-group">
      <label for="request-essential">Essential Requirements</label>
      <input type="text" class="form-control" id="request-essential"
        {{'placeholder="Press return per item"' if item.editmode else ""}}
        {{"readonly" if not item.editmode}}>
    </div>
  </div>
</div>
<script defer>
  $('#request-essential').tokenfield({});
  var token,tokens = [];
  {% for entry in item.request.essentials %}
    token = new Object();
    token.label = "{{ entry }}"
    token.value = "{{ entry }}"
    tokens.push(token);
  {% endfor %}
  $('#request-essential').tokenfield('setTokens',tokens);
</script>
{%- endmacro %}

So we're pre-packaging a field with the JS required to initialise that specific field, then the 'package' gets included in a form, and the script tag makes it way into the food-chain and is activated once the whole HTML block is activated.

In this instance we're passing item down as a variable, which is a data-block populated by the server from database records. Attribute editmode is provided to indicate whether we're in view or edit mode, and the widget is displayed either in read-only mode with no place-holder, or in edit mode with a place-holder prompt.

Resulting page looks like this ...

So we have; Layout -> { Panes } -> { Fields } , where each component consists of any mixture or HTML and Javascript. It's almost like Object Orientated HTML ... ;-)