下划线中的外部模板

IT技术 javascript backbone.js underscore.js
2021-01-29 11:36:36

我使用下划线模板可以附加外部文件作为模板吗?

在主干视图中,我有:

 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 ) })

但它没有用。

6个回答

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

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 文件中
  2. 非常轻巧
  3. 编译和缓存模板
  4. 抽象模板加载逻辑。例如,将来您可以使用预加载和预编译的模板。
  5. 易于使用

[我正在编辑答案而不是发表评论,因为我认为这很重要。]

如果模板未显示在本机应用程序中,并且您看到了HIERARCHY_REQUEST_ERROR: DOM Exception 3,请查看 Dave Robinson 对什么会导致“HIERARCHY_REQUEST_ERR:DOM 异常 3”-错误的回答.

基本上,您必须添加

dataType: 'html'

到 $.ajax 请求。

这也适用于嵌套视图吗?显然,如果一个视图引用另一个视图,我就不能让它工作。
2021-03-24 11:36:36
是的,它也应该适用于嵌套模板。只需添加渲染助手并调用它: <%= render('nested_template', data) %>
2021-04-05 11:36:36
@BinaryNights - 我们是否应该总是添加dataType: 'html'到我们的 ajax 请求中,以防万一?
2021-04-09 11:36:36
你好,你能解释一下“编译和缓存模板”吗?当我尝试调用渲染函数时,它没有添加 tmpl_data 来返回值,它只是按原样传递它。之后我不得不调用“Handlebars.compile”方法。谢谢你。
2021-04-13 11:36:36

编辑:这个答案是旧的和过时的。我会删除它,但它是“已接受”的答案。我来发表一下我的看法。

我不会再提倡这样做了。相反,我会将所有模板分成单独的 HTML 文件。有些人会建议异步加载这些(Require.js 或各种模板缓存)。这在小型项目上效果很好,但在包含大量模板的大型项目中,您会发现自己在页面加载时发出了大量的小异步请求,我真的很不喜欢。(呃……好吧,你可以通过使用 r.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。

好吧,如果没有很多模板,我认为前一种解决方案确实是最有效的。
2021-03-15 11:36:36
实际上,我想删除这个答案,但我不能,因为它是公认的答案。它已经过时了,还有比这更好的解决方案。今天,我会将它们作为单独的模板文件,并使用 grunt 任务(例如 JST)将它们构建到单独的 templates.js 文件中,以避免单独获取它们的异步性质。这是 IMO 两全其美的方法。
2021-03-20 11:36:36
对不起,但我不得不对这个答案投反对票。它非常笨拙,因为它仍然会将模板文件保留为脚本文件,只是被迫看起来像模板。模板必须是模板,所以如果你必须引入Require.js或者使用下面koorchik的绝妙解决方案,我觉得绝对值得。
2021-04-04 11:36:36
这种方法将丢失 ide 可用的任何语法突出显示、重新格式化和重构功能。虽然不投票。
2021-04-06 11:36:36
@TommiForsström 我同意。我已经摆脱了这种方法。哇!2011 年 12 月 4 日在 Backbone.js 开发世界中是很久以前的事了:)
2021-04-11 11:36:36

这种混入可以渲染使用外部模板下划线以非常简单的方式:_.templateFromUrl(url, [data], [settings])方法 API 几乎与Underscore _.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"});
非常酷的 D,这就是我正在寻找的解决方案。我认为可用于将一组模板保密。
2021-03-23 11:36:36
非常漂亮的小混音那里非常整洁!:) 欢呼分享
2021-03-24 11:36:36
@abhi,它适用于 jQuery 1.* 另请参阅此答案stackoverflow.com/a/11755262/541961
2021-03-31 11:36:36
@abhi 它在答案中提供。此外,您需要 jQuery 来加载模板,但您可以根据自己的喜好使用任何其他库重写通过 AJAX 加载模板的部分代码。
2021-04-05 11:36:36
@Dmitriy async:不推荐使用false,所以如果我不带异步参数调用它不起作用,我认为这是因为默认情况下async为true,这意味着调用syncronisilly,所以你有解决这个问题的方法
2021-04-12 11:36:36

我不想将 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 视图中。

谢谢你。我用过。一个建议:没有理由附加为脚本标签 - 可以继续转换为模板并将其保存在查找哈希中。这是一个(非功能性)小提琴示例: jsfiddle.net/PyzeF
2021-03-17 11:36:36
也使用它,它非常适合我尝试将 Jasmine 用于 TDD 并希望在我实现 requirejs 及其 textjs 插件之前测试模板的场景。干得好@Tramp
2021-04-04 11:36:36
对 $.ajax 的调用是异步的,任何取决于结果的调用都应该在返回的 promise 的 done 方法中执行。
2021-04-05 11:36:36
async: false 现在已弃用
2021-04-07 11:36:36
由于async: false已弃用,我通过添加complete回调改进了答案
2021-04-13 11:36:36

这可能有点偏离主题,但您可以使用 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 为您启动此任务,请键入:

grunt watch

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

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

设置并了解如何配置 grunt.js 文件需要一些时间,但是非常值得投入时间,而且我认为您永远不会回到 grunt 之前的工作方式

最喜欢的答案。这应该是公认的答案。(不是我的)
2021-03-16 11:36:36
咕噜声的好切入点。它适用于纯 HTML,但如果我有 <%= price%> 或类似的,我会得到:unexpected token = , failed to compile from grunt
2021-03-31 11:36:36
我喜欢这种方法(使用 JST),除了我在执行此操作时遇到问题: template: JST['test.html'](),它似乎没有从 JST 加载数据 :((请参阅我的问题:stackoverflow.com/questions/29723392/... )
2021-04-10 11:36:36