9

I'm planning to use backbone.js and underscore.js for creating website, and I will have lots of underscore templates:

<script type="text/template" id="search_template">
<p id="header">
//header content will go here
</p>
<p id="form">
    <label>Search</label>
    <input type="text" id="search_input" />
    <input type="button" id="search_button" value="Search" />
</p>
<p id="dynamic_date">
//dynamic data will be displayed here
</p>
</script>

Of course my templates will be much more complicated.

Since I will have lots of them, I don't want to load all templates every time when page loads. I want to find a solution, where I can load specific template only when it will be used.

Another thing is that most of my templates will have same structure, only <p id="form"></p> and <p id="dynamic_date"></p> content will differ.

Could you please suggest me how should I do it?

Thanks,

Ken Redler
  • 23,863
  • 8
  • 57
  • 69
kaha
  • 1,417
  • 2
  • 17
  • 21

3 Answers3

8

Edit: I did some research and ported my iCanHaz code to underscore it also uses localStorage is available

Here is a github repository: https://github.com/Gazler/Underscore-Template-Loader

The code is:

  (function() {
    var templateLoader = {
      templateVersion: "0.0.1",
      templates: {},
      loadRemoteTemplate: function(templateName, filename, callback) {
        if (!this.templates[templateName]) {
          var self = this;
          jQuery.get(filename, function(data) {
            self.addTemplate(templateName, data);
            self.saveLocalTemplates();
            callback(data);
          });
        }
        else {
          callback(this.templates[templateName]);
        }
      },

      addTemplate: function(templateName, data) {
        this.templates[templateName] = data;
      },

      localStorageAvailable: function() {
       try {
          return 'localStorage' in window && window['localStorage'] !== null;
        } catch (e) {
          return false;
        }
      },

      saveLocalTemplates: function() {
        if (this.localStorageAvailable) {
          localStorage.setItem("templates", JSON.stringify(this.templates));
          localStorage.setItem("templateVersion", this.templateVersion);
        }
      },

      loadLocalTemplates: function() {
        if (this.localStorageAvailable) {
          var templateVersion = localStorage.getItem("templateVersion");
          if (templateVersion && templateVersion == this.templateVersion) {
            var templates = localStorage.getItem("templates");
            if (templates) {
              templates = JSON.parse(templates);
              for (var x in templates) {
                if (!this.templates[x]) {
                  this.addTemplate(x, templates[x]);
                }
              }
            }
          }
          else {
            localStorage.removeItem("templates");
            localStorage.removeItem("templateVersion");
          }
        }
      }



    };
    templateLoader.loadLocalTemplates();
    window.templateLoader = templateLoader;
  })();

Calling it would look something like:

      templateLoader.loadRemoteTemplate("test_template", "templates/test_template.txt", function(data) {
        var compiled = _.template(data);
        $('#content').html(compiled({name : 'world'}));
      });

Here is my original answer

Here is a method I wrote for ICanHaz (mustache) that performs this exact function for the same reason.

window.ich.loadRemoteTemplate = function(name, callback) {
  if (!ich.templates[name+"_template"]) {
    jQuery.get("templates/"+name+".mustache", function(data) {
      window.ich.addTemplate(name+"_template", data);
      callback();
    });
  }
  else {
    callback();
  }
}

I then call it like so:

ich.loadRemoteTemplate(page+'_page', function() {
  $('#'+page+'_page').html(ich[page+'_page_template']({}, true));
});
Gazler
  • 83,029
  • 18
  • 279
  • 245
  • 1
    why are you using setimeout and causing a delay of 2 seconds? other than this, great answer! – Derick Bailey Sep 24 '11 at 23:05
  • @Derick Bailey, Oops, I completely just copied and pasted this from my github repository just before I went to bed last night. That was in there purely to visualize the difference between templates that are pre-loaded and templates that are dynamically loaded. Thanks for pointing that out. – Gazler Sep 25 '11 at 09:09
  • @Gazler, thank you for your response. I didn't have time to look to the code yet, but I think this is very good solution. As I can see ICanHaz works with mustache, I would like to know if it will also work with underscore? templates look the same... – kaha Sep 25 '11 at 20:36
  • @user616822 I was actually in the middle of creating an answer as you posted that response. Personally, I prefer mustache and ICanHaz, but the code I added should hopefully cover your needs. ICanHaz is merely a convenience for mustache and won't work with underscore templates. Hope this helps. – Gazler Sep 25 '11 at 20:38
  • @Gazler, that is awesome. I just tried to run it on my localhost and it worked. Now I need to figure out how to implement it to my site. I'm also interested in localStorage idea, but know I want to concentrate to make it work, and after that implement localStorage. Do you think I should integrate it now, or I can do it later on? – kaha Sep 25 '11 at 22:29
  • @user616822 it is exactly the sort of thing that can be added in later as everything is wrapped in a single function. I will probably implement this into some of the projects I work on through the week for my ICanHaz method. I will port it to this and notify you/update the repository when I do. If I forget then raise an issue on the github repo to remind me. :) – Gazler Sep 25 '11 at 22:35
  • @Gazler, thank you very much man )) You helped me a lot. For sure, I will be checking for update in repository, and once I will get to that point I will remind you if you are ok with it :) – kaha Sep 25 '11 at 23:09
  • @user616822: As promised, I have added the localStorage in. There is a variable that maintains the version, if you increment that then it clears the localStorage, I recommend updating this each time you alter any of the templates. – Gazler Sep 27 '11 at 17:49
  • @Gazler, sorry I didn't have access to computer, and I was not able to thank you for your help... Thank you, I will check now the code :) – kaha Oct 02 '11 at 18:35
3

I really like the way the stackoverflow team has done templating with the mvc-miniprofiler. Take a look at these links:

Includes.js (Github link)

Includes.tmpl (Github link)

They use the local storage to cache the templates locally if your browser supports local storage. If not they just load it every time. Its a pretty slick way to handle the templates. This also allows you to keep your templates that aren't required immediately in a separate file and not clutter up your html.

Good luck.

Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
  • @Kyle Rogers, Thank you very much. I also liked local storage idea ) – kaha Sep 25 '11 at 22:29
  • 1
    FYI: These links now 404. The Github uris are: https://github.com/SamSaffron/MiniProfiler/blob/master/StackExchange.Profiling/UI/includes.js and https://github.com/SamSaffron/MiniProfiler/blob/master/StackExchange.Profiling/UI/includes.tmpl – Dan Atkinson Apr 03 '13 at 10:30
2

Although both of the above answers work, I found the following to be a much simpler approach.

Places your templates wrapped in script tags into a file (say "templates.html") as follows:

<script type="text/template" id="template-1">
  <%- text %>
</script>

<script type="text/template" id="template-2">
  oh hai!
</script>

Then the following bit of javascript:

$(document).ready(function() {
  url ='http://some.domain/templates.html'
  templatesLoadedPromise = $.get(url).then(function(data) {
    $('body').append(data)
    console.log("Async loading of templates complete");
  }).fail(function() {
    console.log("ERROR: Could not load base templates");
  });
});

Which then let's you select your templates quite simply using the IDs you previously defined. I added the promise

$.when(templatesLoadedPromise).then(function() {
  _.template($('#template-1').html(), {'text':'hello world'} )
});

You can then extend this and load multiple files if you want.

As a side note, I've found that any core templates needed for initial page render are better embedded in the HTML (I use tornado modules on the server) but that the above approach works very nicely for any templates needed later (e.g., in my case the templates for a registration widget which I want to use across pages is perfect for this as it'll only be loaded on user interaction and is non-core to the page)

Mike N
  • 6,395
  • 4
  • 24
  • 21