7

I'm trying to write a function that will give me a compiled handlebars template (I have all my templates in separate files) using an ajax call to get the template and compile it for use, but I need to use a promise so I can actually use it.

function getTemplate(name){
    $.get('/'+name+'.hbs').success(function(src){
       var template = Handlebars.compile(src);
       //can't return the template here.
    });
}

How do I do this with promises so I can do something like:

$("a").click(function(e){
    getTemplate('form').done(function(template){
       $("body").append(template({
               name: "My Name"
           })
       );
    });
});
kumar
  • 1,796
  • 2
  • 15
  • 37
chovy
  • 72,281
  • 52
  • 227
  • 295
  • Possibly related: http://stackoverflow.com/questions/133310/how-can-i-get-jquery-to-perform-a-synchronous-rather-than-asynchronous-ajax-re – ubik Apr 20 '13 at 10:56
  • @PedroFerreira `async:false` is deprecated and is a terrible solution – charlietfl Apr 20 '13 at 11:01
  • @charlietfl Yes, I agreee. But it may still be interesting as an option, in particular cases. – ubik Apr 20 '13 at 11:04
  • @PedroFerreira there are numerous ways to avoid using it. Note that the link you provided is 5 years old – charlietfl Apr 20 '13 at 11:06
  • @charlietfl I am not advocating the use of `async: false`. I simply mentioned it as related to this discussion and a possible (although generally bad, I agree) solution. I am not suggesting that OP use it, otherwise I would have posted it as an answer. – ubik Apr 20 '13 at 11:09
  • Plus, as far as I can see, the use of `async: false` has been only deprecated for `jqXHR` objects. You can still use it with `success`, etc... – ubik Apr 20 '13 at 11:14
  • @PedroFerreira ..no idea why you are trying to defend a concept that clearly states `deprectaed` in docs and is an outdated bad idea – charlietfl Apr 20 '13 at 11:21
  • From the docs: *As of jQuery 1.8, the use of async: false **with jqXHR ($.Deferred)** is deprecated*. And I am not defending its use, as I've previously stated. – ubik Apr 20 '13 at 11:23

3 Answers3

16

Chovy, I see you have accepted an answer but you might be interested to know that getTemplate can, by chaining .then() rather than .success(), be written almost as in the question :

function getTemplate(name) {
    return $.get('/'+name+'.hbs').then(function(src) {
       return Handlebars.compile(src);
    });
}

or, adopting charlietfl's idea to pass in data and return a Promise of a fully composed fragment :

function getTemplate(name, data) {
    return $.get('/'+name+'.hbs').then(function(src) {
       return Handlebars.compile(src)(data);
    });
}

The nett effect is identical to charlietfl's version of getTemplate but .then() makes it unnecessary to create a Deferred explicitly. The code is thus more compact.

Beetroot-Beetroot
  • 18,022
  • 3
  • 37
  • 44
  • Thank you for the accept Chovy, though I rather feel I stole the rep off Charlie :-| – Beetroot-Beetroot Apr 22 '13 at 08:48
  • Don't forget that ajax is async and you should probably use a callback in that function to make sure you don't have issues. Used this code and had this exact problem – sMyles Apr 17 '14 at 03:08
  • Ah just noticed he is using .done on the actual function call ... doh! – sMyles Apr 17 '14 at 03:10
4

Following adds a data argument to the getTemplate function as well as template name.

$(function(){
  var postData={title: "My New Post", content: "This is my first post!"};
 getTemplate('template-1',postData).done(function(data){
   $('body').append(data)
 })
});

function getTemplate( name,data){
  var d=$.Deferred();

  $.get(name+'.html',function(response){

    var template = Handlebars.compile(response);
    d.resolve(template(data))
  });

  return d.promise();

}

DEMO

charlietfl
  • 170,828
  • 13
  • 121
  • 150
1

I created a library to help with this kind of issue, check at github

You only have to add this to your main app view:

<script type="text/x-handlebars" data-template-name="application">
    <!-- Your HTML code -->
    <div class="container">
        <div class="modal fade" id="editView" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content">
                    {{view MainApp.ModalContainerView elementId="modalContainerView"}}
                </div><!-- /.modal-content -->
            </div><!-- /.modal-dialog -->
        </div><!-- modal edit dialog -->
        {{view MainApp.AppContainerView elementId="appContainerView"}}
        {{outlet}}
    </div> <!-- main container -->
</script>

put this into your MainApp

var MainApp = Em.Application.create({
    LOG_TRANSITIONS: true,
    ready: function () {
    /** your code **/
    MainApp.AppContainerView = Em.ContainerView.extend({});
    MainApp.ModalContainerView = Em.ContainerView.extend({});
    /** And other containerView if you need for sections in tabs **/
    });

and them, for instance, to open a modal with the template that you want, you only have to:

FactoryController.loadModalTemplate(templateName, callback);

And not forget to add the FactoryController and RepositoryController

Juan Jardim
  • 2,232
  • 6
  • 28
  • 46