下划线中的外部模板

时间:2011-12-03 09:22:50

标签: javascript backbone.js underscore.js

我使用Underscore template。可以附加外部文件作为模板

在Backbone View中我有:

 textTemplate: _.template( $('#practice-text-template').html() ),

 initialize: function(){                                            
  this.words = new WordList;            
  this.index = 0;
  this.render();
 },

在我的HTML中是:

<script id="practice-text-template" type="text/template">
   <h3>something code</h3>
</script>

效果很好。但我需要外部模板。 我试试:

<script id="practice-text-template" type="text/template" src="templates/tmp.js">

textTemplate: _.template( $('#practice-text-template').load('templates/tmp.js') ),

$('#practice-text-template').load('templates/tmp.js', function(data){ this.textTemplate = _.template( data ) })

但它不起作用。

12 个答案:

答案 0 :(得分:107)

这是一个简单的解决方案:

var rendered_html = render('mytemplate', {});

function render(tmpl_name, tmpl_data) {
    if ( !render.tmpl_cache ) { 
        render.tmpl_cache = {};
    }

    if ( ! render.tmpl_cache[tmpl_name] ) {
        var tmpl_dir = '/static/templates';
        var tmpl_url = tmpl_dir + '/' + tmpl_name + '.html';

        var tmpl_string;
        $.ajax({
            url: tmpl_url,
            method: 'GET',
            dataType: 'html', //** Must add 
            async: false,
            success: function(data) {
                tmpl_string = data;
            }
        });

        render.tmpl_cache[tmpl_name] = _.template(tmpl_string);
    }

    return render.tmpl_cache[tmpl_name](tmpl_data);
}

在这里使用“async:false”并不是一个糟糕的方法,因为在任何情况下都必须等到模板加载完毕。

所以,“渲染”功能

  1. 允许您将每个模板存储在静态的单独html文件中 dir
  2. 非常轻量级
  3. 编译和缓存模板
  4. 抽象模板加载逻辑。例如,将来您可以使用预加载和预编译的模板。
  5. 易于使用
  6. [我正在编辑答案,而不是发表评论,因为我认为这很重要。]

    如果模板未显示在 原生应用 中,并且您看到HIERARCHY_REQUEST_ERROR: DOM Exception 3,请查看Dave Robinson对What exactly can cause an "HIERARCHY_REQUEST_ERR: DOM Exception 3"-Error?的回答。

    基本上,您必须添加

    dataType: 'html'
    

    到$ .ajax请求。

答案 1 :(得分:51)

编辑:这个答案已经过时了。我会删除它,但这是“接受”的答案。我会注意我的意见。

我不会主张这样做了。相反,我会将所有模板分成单独的HTML文件。有人建议异步加载这些(Require.js或排序的模板缓存)。这适用于小型项目,但对于包含大量模板的大型项目,您发现自己在页面加载时会产生大量的小异步请求,而我真的不喜欢这些请求。 (呃......好吧,你可以通过使用r.js预先编译初始依赖项来解决Require.js问题,但是对于模板,这对我来说仍然是错误的)

我喜欢使用grunt任务(grunt-contrib-jst)将所有HTML模板编译成单个templates.js文件并包含它。你可以获得所有世界中最好的IMO ...模板存在于一个文件中,所述模板的编译发生在构建时(而不是运行时),并且当页面启动时你没有一百个微小的异步请求。

以下所有内容都是垃圾

对我来说,我更喜欢将JS文件包含在我的模板中。因此,我可能会创建一个名为view_template.js的文件,其中包含模板作为变量:

app.templates.view = " \
    <h3>something code</h3> \
";

然后,它就像包含普通脚本文件一样简单,然后在你的视图中使用它:

template: _.template(app.templates.view)

更进一步,我实际上使用coffeescript,所以我的代码实际上看起来更像是这样,并避免使用行尾转义字符:

app.templates.view = '''
    <h3>something code</h3>
'''

使用这种方法可以避免在不需要的地方使用require.js。

答案 2 :(得分:17)

我不想将require.js用于这个简单的任务,所以我使用了修改过的koorchik解决方案。

function require_template(templateName, cb) {
    var template = $('#template_' + templateName);
    if (template.length === 0) {
        var tmpl_dir = './templates';
        var tmpl_url = tmpl_dir + '/' + templateName + '.tmpl';
        var tmpl_string = '';

        $.ajax({
            url: tmpl_url,
            method: 'GET',
            contentType: 'text',
            complete: function (data, text) {
                tmpl_string = data.responseText;
                $('head').append('<script id="template_' + templateName + '" type="text/template">' + tmpl_string + '<\/script>');
                if (typeof cb === 'function')
                    cb('tmpl_added');
            }
        });
    } else {
        callback('tmpl_already_exists');
    }
}

require_template('a', function(resp) {
    if (resp == 'tmpl_added' || 'tmpl_already_exists') {
        // init your template 'a' rendering
    }
});
require_template('b', function(resp) {
    if (resp == 'tmpl_added' || 'tmpl_already_exists') {
        // init your template 'b' rendering
    }
});

为什么要将模板附加到文档,而不是将它们存储在javascript对象中?因为在生产版本中我想生成包含所有模板的html文件,所以我不需要进行任何额外的ajax请求。同时我不需要在代码中进行任何重构,因为我使用

this.template = _.template($('#template_name').html());

在我的Backbone视图中。

答案 3 :(得分:17)

此mixin允许您以非常简单的方式使用下划线呈现外部模板:_.templateFromUrl(url, [data], [settings])。方法API与下划线 _.template()几乎相同。包括缓存。

_.mixin({templateFromUrl: function (url, data, settings) {
    var templateHtml = "";
    this.cache = this.cache || {};

    if (this.cache[url]) {
        templateHtml = this.cache[url];
    } else {
        $.ajax({
            url: url,
            method: "GET",
            async: false,
            success: function(data) {
                templateHtml = data;
            }
        });

        this.cache[url] = templateHtml;
    }

    return _.template(templateHtml, data, settings);
}});

用法:

var someHtml = _.templateFromUrl("http://example.com/template.html", {"var": "value"});

答案 4 :(得分:16)

这可能略显偏离主题,但你可以使用Grunt(http://gruntjs.com/) - 它运行在node.js(http://nodejs.org/,可用于所有主要平台)上运行命令行中的任务。这个工具有很多插件,比如模板编译器,https://npmjs.org/package/grunt-contrib-jst。请参阅GitHub上的文档,https://github.com/gruntjs/grunt-contrib-jst。 (您还需要了解如何运行节点包管理器https://npmjs.org/。不要担心,它非常容易和多样化。)

然后您可以将所有模板保存在单独的html文件中,运行该工具以使用下划线预编译它们(我认为这是JST插件的依赖项,但不要担心,节点包管理器将自动安装依赖项你)。

这会将您的所有模板编译为一个脚本,例如

templates.js

加载脚本将默认设置一个全局 - “JST” - 这是一个函数数组,可以这样访问:

JST['templates/listView.html']()

类似
_.template( $('#selector-to-your-script-template'))

如果您将该脚本标记的内容放在(templates /)listView.html

然而,真正的踢球者是这样的:Grunt带有这个名为'watch'的任务,它基本上监视你在本地grunt.js文件中定义的文件的变化(这基本上是你的Grunt项目的配置文件) ,在javascript)。如果你有咕噜声为你开始这个任务,输入:

grunt watch
从命令行

,Grunt将监视您对文件所做的所有更改,并自动执行您在grunt.js文件中为其设置的所有任务,如果它检测到更改 - 例如 jst 上述任务。编辑然后保存您的文件,并将所有模板重新编译为一个js文件,即使它们分布在多个目录和子目录中。

可以配置类似的任务来linting你的javascript,运行测试,连接和缩小/ uglifying你的脚本文件。所有这些都可以与监视任务相关联,因此对文件的更改将自动触发项目的新“构建”。

设置并了解如何配置grunt.js文件需要一些时间,但它非常值得花时间投入,而且我认为你不会再回到前咕噜咕噜的方式了工作

答案 5 :(得分:15)

我认为this可能会对您有所帮助。解决方案中的所有内容都围绕require.js库,这是一个JavaScript文件和模块加载器。

上面链接中的教程很好地展示了如何组织骨干项目。还提供了sample implementation。希望这会有所帮助。

答案 6 :(得分:4)

我对javascript模板感兴趣,现在我正在采取主干的第一步。这就是我提出的,似乎工作得很好。

window.App = {

    get : function(url) {
        var data = "<h1> failed to load url : " + url + "</h1>";
        $.ajax({
            async: false,
            url: url,
            success: function(response) {
                data = response;
            }
        });
        return data;
    }
}

App.ChromeView = Backbone.View.extend({
    template: _.template( App.get("tpl/chrome.html") ),
    render: function () {
        $(this.el).html(this.template());
        return this;
    },
});

App.chromeView = new App.ChromeView({ el : document.body });
App.chromeView.render();

答案 7 :(得分:4)

我必须将数据类型设置为“text”以使其适用于我:

get : function(url) {
    var data = "<h1> failed to load url : " + url + "</h1>";
    $.ajax({
        async: false,
        dataType: "text",
        url: url,
        success: function(response) {
            data = response;
        }
    });
    return data;
}

答案 8 :(得分:2)

我找到了一个使用jQuery的解决方案。

我使用jQuery.load()方法将下划线模板代码添加到主html文件中。

一旦它在那里,我就用它来生成模板。 一切都需要同步发生!

概念是:

我有一个下划线地图模板代码:

<!-- MAP TEMPLATE-->
<script type="text/template" id="game-map-template">
    <% _.each(rc, function(rowItem, index){ %>
      <ul class="map-row" data-row="<%- index %>">
        <li class="map-col <%- colItem.areaType ? 'active-area' : '' %>"></li>
        ...
</script>

我将该代码放在名为 map-template.html

的文件中

之后,我为模板文件创建了一个包装器。

<div id="templatesPool"></div>

然后我将该文件包含在我的主html文件中,如此。

在头上:

<!-- Template Loader -->
<script> 
    $(function(){
      $("#templatesPool").append($('<div>').load("map-template.html")); 
    });
</script> 

干杯。

答案 9 :(得分:1)

我知道这个问题已经很老了,但它是谷歌搜索下划线ajax模板的第一个结果。

我厌倦了没有为此找到一个好的解决方案,所以我创建了自己的:

https://github.com/ziad-saab/underscore-async-templates

除了使用AJAX加载下划线模板外,还添加&lt;%include%&gt;功能。我希望它对某人有用。

答案 10 :(得分:0)

我有点不安地迫使jQuery同步运行,因此我使用promises修改了之前的同步示例。它几乎一样,但是异步运行。我在这个例子中使用了hbs模板:

var asyncRenderHbs= function(template_name, template_data) {
    if (!asyncRenderHbs.template_cache) { 
        asyncRenderHbs.template_cache= {};
    }

    var promise= undefined;

    if (!asyncRenderHbs.template_cache[template_name]) {
        promise= new Promise(function(resolve, reject) {
            var template_url= '/templates/' + template_name;
            $.ajax({
                url: template_url,
                method: 'GET',
                success: function(data) {
                    asyncRenderHbs.template_cache[template_name]= Handlebars.compile(data);
                    resolve(asyncRenderHbs.template_cache[template_name](template_data));
                },
                error: function(err, message) {
                    reject(err);
                }           
            });
        });
    } else {
        promise= Promise.resolve(asyncRenderHbs.template_cache[template_name](template_data));
    }

    return promise;
};

然后使用渲染的html:

asyncRenderHbs('some_template.hbs', context)
    .then(function(html) {
        applicationMain.append(html);
        // Do other stuff here after html is rendered...
    })
    .catch(function(err) {
        // Handle errors
    });

注意:正如其他人所讨论的那样,最好将所有模板编译成单个templates.js文件并在开头加载,而不是在网页加载时进行许多小型同步AJAX调用来获取模板。

答案 11 :(得分:0)

前方警告 - 这是龙:

我提到下面显示的方法只是为了帮助那些努力使ASP.NET堆栈(和类似的框架)与js-libs生态系统协调工作。不言而喻,这不是一般的解决方案。说完了......

/ endforwardwarning

如果您使用的是ASP.NET,则只需将模板放在自己的一个或多个局部视图中即可将模板外部化。你的.cshtml中的Aka:

  @Html.Partial("path/to/template")

在template.cshtml中:

   // this is razorview and thusly if you ever need to use the @ character in here  
   // you will have to either escape it as @@ or use the html codepoint which is &#64
   // http://stackoverflow.com/questions/3626250/escape-character-in-razor-view-engine
   <script type="text/x-template" id="someId">
        <span class="foo"><%= name %></span>
   </script>

现在你可以像往常一样使用模板:

  _.template($("#someId").html())({ name: "Foobar" });

希望这种难以捉摸的显而易见的方法可以帮助某人节省一个小时的头脑。