【问题标题】:How do I call a controller function from a template in Ember?如何从 Ember 中的模板调用控制器函数?
【发布时间】:2015-04-09 16:54:33
【问题描述】:

假设我有一个迭代项目集合的模板,我想为每个项目调用一个函数,该函数特定于控制器,而不是模型级问题:

{{#each people as |person|}}
  icon name: {{findIconFor(person)}}
{{/each}}

我想在控制器中定义findIconFor,因为这是特定于这个特定视图的东西。

export default Ember.Controller.extend({
  findIconFor: function(person) {
    // figure out which icon to use
  }
);

但这不起作用。模板无法编译。解析错误:期望 'STRING'、'NUMBER'、'ID'、'DATA',得到 'INVALID'

这样做的“余烬方式”是什么?

【问题讨论】:

  • 好吧,你不能那样做,因为你不能那样做。您可以在person 上放置一个计算属性,然后只需执行person.icon

标签: ember.js


【解决方案1】:

因为我几乎一整天都在解决类似的问题,所以这是我的解决方案。

因为 Ember 出于某种原因不允许您直接从模板运行控制器功能(这很荒谬,并且以一些非常愚蠢的方式束缚了您的手,我不知道到底是谁认为这是一个好的想法...)对我来说最有意义的事情是创建一个通用自定义助手,它允许您从模板运行函数:)这里的问题是您应该始终传递当前范围(“this”变量) 给那个助手。

所以助手可能是这样的:

export default Ember.Helper.helper(function([scope, fn]) {
    let args = arguments[0].slice(2);
    let res = fn.apply(scope, args);
    return res;
});

然后,您可以在控制器中创建一个要运行的函数,例如:

testFn: function(element){
    return element.get('name');
}

然后在您的模板中,您只需使用自定义助手调用它:

{{#each items as |element|}}
    {{{custom-helper this testFn element}}}
{{/each}}

帮助程序的前两个参数应该始终是“this”和您要运行的函数的名称,然后您可以传递任意数量的额外参数。


编辑:无论如何,每次当你认为你需要这样做时,你应该考虑创建一个新组件是否会更好(这将在 90% 的情况下)

【讨论】:

  • 只是错字,但我认为 {{{custom-helper this testFn element}}} 每边都有一个括号太多
  • 其实不是错字,“triple-stash”的意思是“不要转义 HTML 标签”,但你也可以用 2 个括号来做
  • “这太荒谬了,而且以一些非常愚蠢的方式束缚了你的双手......” 我的沮丧之声。看起来像一个框架应该处理的一件简单的事情。
  • 兄弟也有同样的感受 :( .
【解决方案2】:

我会在控制器中使用计算属性:

iconPeople: Ember.computed('people.@each', function(){
  var that = this;
  return this.get('people').map(function(person){
    return {
      'person': person,
      'icon': that.findIconFor(person)
    };
  });
})

现在您可以从{{person.icon}} 获取图标,从{{person.person.name}} 获取名称。您可能希望对此进行改进(并且代码未经测试),但这是一般的想法。

【讨论】:

  • 哦,对不起,是的。我已经相应地改变了它。
  • 为什么是“people.@each”而不是“people”?
  • 您不仅想听到 people 数组本身的变化(即不同的数组),而且还想听到数组内容的变化。
【解决方案3】:

如果图标与人相关联,那么由于人由模型表示,最好将其实现为人模型上的计算属性。您尝试将其放入控制器的意图是什么?

// person.js
export default DS.Model.extend({
  icon: function() { return "person-icon-" + this.get('name'); }.property('name')
  ..
};

那么假设peopleperson的数组:

{{#each people as |person|}}
  icon name: {{person.icon}}
{{/each}}

@jnfingerle 提供的替代方案有效(我假设您发现他建议您循环遍历iconPeople),但似乎要创建一个包含对象的新数组需要做很多额外的工作。图标是否依赖于只有控制器知道的任何东西?如果不是,正如我所说,为什么计算它的逻辑应该在控制器中?

把东西放在哪里是一个哲学和偏好的问题。有些人喜欢只包含来自服务器的字段的准系统模型;其他人计算模型中的状态和中间结果。有些人在控制器中放了很多东西,而另一些人则更喜欢在“服务”中具有更多逻辑的轻量级控制器。就个人而言,我支持更重的模型、更轻的控制器和服务。当然,我并不是说业务逻辑、繁重的数据转换或视图准备都应该包含在模型中。但请记住,模型代表一个对象。如果对象有一些有趣的方面,无论它来自服务器还是以某种方式计算,对我来说,将其放入模型中是很有意义的。

还要记住,控制器是紧密耦合的路由/控制器/视图关系的一部分。如果您在一个控制器中计算了一些特定于模型的东西,那么您可能必须将其添加到恰好处理相同模型的其他控制器中。然后,在不知不觉中,您正在编写控制器 mixin,它们在控制器之间共享逻辑,而这些逻辑原本不应该包含在其中。

无论如何,您说您的图标来自“不相关的数据存储”。这听起来是异步的。对我来说,这暗示它可能是一个名为PersonIcon 的子模型,它是person 模型中的belongsTo。您可以使用适用于该模型的适配器和序列化程序的正确组合来实现这一点。这种方法的好处是,在创建 person 模型时,或者当您真正需要图标时(如果您说 async: true),检索图标的所有异步性都将得到半魔法处理。

但也许您没有使用 Ember Data,或者不想麻烦您。在这种情况下,您可以考虑在路由的模型钩子中使用图标来装饰人,利用 Ember 处理异步模型解析的能力,通过执行以下操作:

model: function() {
  return this.store.find('person') .
    then(function(people) {
      return Ember.RSVP.Promise.all(people.map(getIcon)) .
        then(function(icons) {
          people.forEach(function(person, i) {
            person.set('icon') = icons[i];
          });
          return people;
        })
      ;
    })
  ;
}

getIcon 类似于

function getIcon(person) {
  return new Ember.RSVP.Promise(function(resolve, reject) {
    $.ajax('http://icon-maker.com?' + person.get('name'), resolve);
  });
}

或者,如果它更简洁,您可以将图标内容拆分为 afterModel 挂钩:

model: function() { return this.store.find('person'); },

afterModel: function(model) {
  return Ember.RSVP.Promise.all(model.map(getIcon)) .
    then(function(icons) {
      model.forEach(function(person, i) {
        person.set('icon') = icons[i];
      });
    })
  ;
}

现在 Ember 将等待整个承诺解决,包括获取人物及其图标并将图标粘贴到人物身上,然后再继续。

HTH。

【讨论】:

  • 目的是使用控制器中的一些其他模型数据来确定动态结果。上面的这个例子是一个简单的例子,但即便如此,我真的不喜欢将视图特定的关注点放在模型上。但在这种情况下,它需要从一个不相关的数据存储中获取信息,所以它当然不应该在模型中。
  • 我明白你在说什么,但我的例子很简单。在我的真实案例中,需要与项目相关的数据是完全不相关的。为它建立关系是没有意义的。控制器拥有进行关联所需的信息,而模型没有也不应该拥有这些信息。 @jnfingerle 提出的属性方法对我的需求是正确的。我真希望我可以在控制器中调用一个函数。似乎是一件合理的事情。但这就是 Ember 的方式……稍微偏离“轨道”,事情就会变得艰难。
  • 这不是“ember”方式,而是“handlebars”方式,不将诸如函数调用之类的逻辑放入控制器中,放入模板中。在任何情况下,从视图/模板调用控制器都是对关注点分离的根本违反。如果你想这样做,你也可以放弃 MVC。模板显示控制器提供的内容。顺便问一下,你能提供getIconFor的示例代码吗?是不是异步的?
  • "从视图/模板调用控制器是对关注点分离的根本违反" 那么什么是动作?控制器向视图提供数据。视图调用控制器来委托行为。控制器和视图总是通过数据契约和交互契约耦合。为项目提供数据的功能似乎是一个合理的规定。它与关联数据的控制器没有太大区别。项目控制器也会做我需要的。但是项目控制器已被弃用。
  • 哈哈“你可能会放弃 MVC”,怎么样?这意味着 .Net 和 RoR 已经放弃了 mvc。 ?理想情况下,它应该提供与最先进技术相同的行为。我知道它们是不同的技术和领域,但这里的想法是相同的(MVC)。我应该可以做model.somemethod.apply,this.somemethod.apply,结果应该显示在hbs中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-08
  • 1970-01-01
相关资源
最近更新 更多