我该如何使用Meteor Spacebars模板动态渲染HTML?

8

假设我正在数据库中存储<div>{{name}}</div><div>{{age}}</div> 。然后我想在模板中呈现第一个HTML字符串- {{> template1}} ,它只使用其中包含{{name}} 句柄的第一个字符串进行呈现。 接下来,我希望将新生成的模板/ HTML数据提供给它,以便填充句柄中实际的 name,这样我们就会得到<div> John </div>。 我尝试了以下操作:

<template name="firstTemplate">
    {{#with dataGetter}}
        {{> template1}}
    {{/with}}
</template>

其中template1被定义为

<template name="template1">
    {{{templateInfo}}}
</template>

templateInfo是一个帮助程序,从数据库中返回包含handlebar的上述html字符串。

dataGetter只是这样的(仅为示例,我使用不同命名的集合)

Template.firstTemplate.dataGetter = function() {
    return Users.findOne({_id: Session.get("userID")});
}

我无法获取{{name}}的值。我已经尝试了几种不同的方法,但似乎Meteor不理解需要使用数据来评估字符串中的handlebars。由于正在使用其他软件包,它们目前还没有0.8+版本支持,所以我无法升级到Blaze 0.8+. 如果您有任何想法如何使其正常工作,那将非常感激。

6个回答

9
在1.0版本中,上述方法均无法使用。我通过在客户端代码中定义以下函数使其正常工作。关键是向编译函数传递选项 { isTemplate: true }。
var compileTemplate = function(name, html_text) {
  try {
    var compiled = SpacebarsCompiler.compile(html_text, { isTemplate:true });
      var renderer = eval(compiled);
      console.log('redered:',renderer);
      //Template[name] = new Template(name,renderer);
      UI.Template.__define__(name, renderer);
  } catch (err){
    console.log('Error compiling template:' + html_text);
    console.log(err.message);
  }
};

你可以在客户端使用类似以下方式进行调用:

compileTemplate('faceplate', '<span>你好!!!!{{_id}}</span>');

这将使你的html中的UI动态呈现。

{{> Template.dynamic template='faceplate'}}


3
请务必添加 spacebars-compiler 包 (meteor add spacebars-compiler),这样你就可以使用 SpacebarsCompiler。 - Alban Lecocq
1
我在控制台中收到“redered:function anonymous()”消息,但在我的情况下{{> UI.dynamic template='faceplate'}}没有任何作用。我正在使用Meteor 1.1.0.2。 - Rushikesh Gomekar
如何呈现面板子模板,即通常在面板中使用的{{> child }},但不随着动态添加{{> Template.dynamic template='faceplate'}}而呈现。 - Abdul Hameed

8

您实际上可以使用spacebars编译器将字符串编译为模板。 只需使用meteor add spacebars-compiler将其添加到您的项目中即可。

在使用0.8.x的项目中

var compiled = Spacebars.compile("<div>{{name}}</div> and <div>{{age}}</div>");
var rendered = eval(compiled);

Template["dynamicTemplate"] = UI.Component.extend({
  kind: "dynamicTemplate",
  render: rendered
});

对于使用 0.9.x 版本的项目

var compiled = SpacebarsCompiler.compile("<div>{{name}}</div> and <div>{{age}}</div>");
var renderer = eval(compiled);

Template["dynamicTemplate"] = Template.__create__("Template.dynamicTemplate", rendered);

哈,这是一个更好的解决方案,我会研究一下。不确定在0.7.0中是否有UI类可用。 - Danail Gabenski
这个包似乎不存在 - 在 Meteor 和 Meteorite 中都没有。我猜它是 0.7.0 之后的 Meteor 东西。 - Danail Gabenski
鉴于 spacebars 是 Meteor UI 的一部分,我认为你需要使用 Meteor > 0.8.0。 - Kelly Copley
我的错,我一直以为spacebars只是Meteor对handlebars的称呼,与Meteor的版本无关。我猜他们只是在0.8.x+中将它们重命名了。但我还在使用0.7.0。 - Danail Gabenski

3

以下是 @user3354036 的回答:

var compileTemplate = function(name, html_text) {
  try {
    var compiled = SpacebarsCompiler.compile(html_text, { isTemplate:true }),
        renderer = eval(compiled);

    console.log('redered:',renderer);
    //Template[name] = new Template(name,renderer);
    UI.Template.__define__(name, renderer);
  } catch (err) {
    console.log('Error compiling template:' + html_text);
    console.log(err.message);
  }
};

1) 在你的HTML中添加以下内容

 {{> Template.dynamic template=template}}

2) 调用compileTemplate方法。

compileTemplate('faceplate', '<span>Hello!!!!!!{{_id}}</span>');
Session.set('templateName','faceplate');

将模板名称保存在 Session 变量中。下一点会解释其重要性。

3) 编写一个帮助函数以返回模板名称。我已经使用 Session 变量来实现。如果您在单击事件上添加动态内容,或者父模板已经被渲染,则这很重要。否则,您将永远看不到动态模板被渲染。

'template' : function() {
  return Session.get('templateName');
}

4) 将以下内容写入父模板的渲染方法中。这是为了重置会话变量。

Session.set('templateName','');

这对我很有帮助。希望能对其他人有所帮助。

1
太好了!非常感激,救了我的一天 :) - Abdul Hameed
渲染后,我的之前的帮助函数停止工作了,你遇到过这种情况吗? - Abdul Hameed
不要忘记运行 meteor add spacebars-compiler 以便使用 SpacebarsCompiler - Harry Adel

1
一个非常简单的方法是在onRendered事件中调用全局Blaze对象。
Blaze.renderWithData(Template[template_name], data ,document.getElementById(template_id))

1
如果您需要动态编译复杂模板,我建议采用Kelly的答案。否则,您有两个选择:
  1. 创建所有模板变体,然后动态地选择正确的模板:

    例如,创建您的模板

    <template name="displayName">{{name}}</template>
    <template name="displayAge">{{age}}</template>
    

    然后使用以下方法动态包含它们

    {{> Template.dynamic template=templateName}}
    

    其中templateName是一个返回"age""name"的辅助函数

  2. 如果您的模板很简单,只需自己执行替换。您可以使用Spacebars.SafeString返回HTML。

    function simpleTemplate(template, values){
          return template.replace(/{{\w+}}/g, function(sub) {
              var p = sub.substr(2,sub.length-4);
              if(values[p] != null) { return _.escape(values[p]); }
              else { return ""; }
          })
    }
    Template.template1.helpers({
      templateInfo: function(){
          // 在此上下文中,this/self指代“user”数据
          var templateText = getTemplateString();
          return Spacebars.SafeString(
              simpleTemplate(templateText, this)
          );
      }
    

昨天我选择了第二个选项,希望Blaze和其后续版本能够让我们有机会在运行时将HTML编译成Handlebars。这仍然让我很生气,我必须自己重新实现Handlebars,唉。 - Danail Gabenski
JSYK eval在模板包内部用于创建模板。字符串空格编译器将解析您的字符串并正确转义它们,就像处理模板一样。 - Kelly Copley
就标签而言,您可能希望解析掉任何<script>标签。 - Kelly Copley

1
幸运的是,Meteor API 已经提供了解决这个问题和类似问题的方法,即 Blaze package。这是使响应式模板成为可能的核心 Meteor 包。如果您查看链接文档,Blaze 包提供了一长串函数,允许广泛的解决方案,用于编程创建、渲染和删除响应式和非响应式内容。
为了解决上述问题,您需要执行以下操作:
首先,预测应用程序需要动态呈现的不同 HTML 片段。在本例中,这些片段将是 <div>{{name}}</div><div>{{age}}</div>,但它们实际上可以是任何有效的 HTML(虽然它还不是公共 API 的一部分,在未来,开发人员将有更多选项以更动态的方式定义此内容,如文档中所述 here)。您将把它们放入小型模板定义中,如下所示:
<template name="nameDiv">
    <div>{{name}}</div>
</template>

"and" (并且)
<template name="ageDiv">
    <div>{{age}}</div>
</template>

其次,需要修改firstTemplate的定义,包含一个可以通过编程引用的HTML节点,如下所示:
<template name="firstTemplate">
    <div></div>
</template>

接下来,您需要为firstTemplate模板定义逻辑,并利用Blaze软件包提供的一些函数,即 Blaze.With, Blaze.render Blaze.remove(尽管您可以更改以下逻辑并改用 Blaze.renderWithData函数;这完全基于您个人对如何定义逻辑的偏好 - 我只为说明提供了一种可能的解决方案)。

Template.firstTemplate.onRendered(function() {
    var dataContext = Template.currentData();
    var unrenderedView = Blaze.With(dataContext, function() {
        // Define some logic to determine if name/age template should be rendered
        // Return either Template.nameDiv or Template.ageDiv
    });
    var currentTemplate = Template.instance();
    var renderedView = Blaze.render(unrenderedView, currentTemplate.firstNode);

    currentTemplate.renderedView = renderedView;
});

Template.firstTemplate.onDestroyed(function() {
    var renderedView = Template.instance().renderedView;
    Blaze.remove(renderedView);
});

在您的firstTemplate模板的onRendered函数中,我们正在动态确定要呈现在页面上的数据部分(在您的情况下为名称或年龄),并使用Blaze.With()函数使用firstTemplate模板的数据上下文创建未呈现视图。然后,我们选择要包含动态生成内容的firstTemplate模板元素节点,并将两个对象传递到Meteor.render()函数中,该函数使用指定的元素节点作为呈现内容的父节点将未呈现的视图呈现到页面上。
如果你阅读Blaze.render()函数的细节,你会发现这个渲染出来的内容会保持响应性,直到使用Blaze.remove()函数删除渲染视图或指定的父节点从DOM中删除为止。在我上面的例子中,我正在获取从调用Blaze.render()收到的渲染视图的引用,并将其直接保存在模板对象上。我这样做是为了当模板本身被销毁时,我可以在onDestroyed()回调函数中手动删除渲染视图,并确保它真正被销毁。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接