JavaScript 关于 ember 中如何通过变量使用不同的 view

zhex · 2013年06月22日 · 最后由 zhex 回复于 2013年06月23日 · 4181 次阅读

我有很多种的卡片,在数据库中为每种卡片保存了一个类型。由于每种卡片的展示效果非常不同,所以我想在 ember 中不同类型的卡片创建不同的 view,然后通过循环读取卡片的同时调用不同的 view。

期望表达式: {{ render 'card' card }}

可惜 render 的第一个参数只能是 view 的名字字符串,而不能变量代替。有谁知道有什么其他方法实现?

楼主我帮你分析下,你的需求可以分解一下:

  1. 传入的对象是同一类的,只是某个属性会有差别;
  2. 根据属性的差别所渲染的模板要不一样

这样看起来,使用 {{ render }} 其实不太合适呢,因为 {{ render }} 最大的特点是模型、视图、控制器全部可以自定义,但是你的情况实际上模型和控制器应该都是固定使用当前的只有视图需要变化,确切地说是视图需要的模板需要变化,所以 {{ view }} 应该就够用了。

关于 {{ partial }}{{ view }}{{ render }}{{ control }} 这几个渲染助手方法,可以参见http://emberjs.com/guides/templates/rendering-with-helpers/,描述的非常清楚。

接下来的问题就是,我如何知道这些对象的某个属性不同?

这个首先要看你的数据结构是怎么设计的了,因为你没有给出足够的信息,所以我就编个例子吧。比如说你的 card 会有一个属性 type,这个值不一样,渲染 card 用的模板就不一样。

这里要插一句,如果 Handlebars 支持这样的语法就简单了:

{{#if card.type === 1}}
  ...
{{/if}}

可惜它不能,Handlebars 只会判断逻辑真假。那么,假如你的 type 的种类是固定的,你可以用最简陋的方法,也就是把 type 分成 cards 表里的独立字段,像 type_1, type_2, type_3... 等等,这样在 Handlebars 里面直接判断这个属性有或没有就行了。

当然这是不推荐的做法,通常我们还是用一个 type 字段,用不同的值去区分的,所以我们应该使用 computed properties,就像这样:

MyApp.CardsController = Ember.ArrayController.extend({
  // 给对象加几个 computed properties
  isType1: false,
  isType2: false,
  isType3: false,

  // 根据 type 的值来改变上述属性的赋值
  changeType: function() {
    switch (this.get('type')) {
      case 1:
        this.toggleProperty(this.get('isType1'));
        break;
      case 2:
        this.toggleProperty(this.get('isType2'));
        break;
      case 3:
        this.toggleProperty(this.get('isType3'));
        break;
      default:
        return;
    }
  }.property(this.get('type'));
});

OK,现在 Handlebars 能处理了

{{#if card.isType1}}
  {{view MyApp.CardView templateName="card_type1" cardBinding=card}}
{{/if}}
...

一般性的处理到这里就可以了,除非 type 种类很多,搞得 Handlebars 看起来很乱的话,那我会考虑自定义 helper,传 type 值进去,在 helper 里面判断逻辑。不过通常来说,没有可重用价值的不太需要做成 helper。

这只是一种比较简单直接的思路,其实完全不考虑使用 Handlebars helpers,而是在视图类里动态去改变 templateName 属性,或者根据情况手动创造视图实例的时候去指明 templateName 属性也是可以的,一下子脑子里就可以迸出好多种方案。抛砖引玉一下吧,GL

@nightire 非常感谢你的认真回复。我希望 type 的名字是可以和 view 的名字对应的,所以理论上是无限多种 card type, 这个也是我需要的结果。你上面提供的方法貌似不适合我的需求,自定义 helper 应该是个可行的方案。

在自定义 helper 中我没有成功,是否可以帮我一起看下问题:

{{#each card in cards}}
  {{renderCard this card.type card class="card"}}
{{/each}}
Ember.Handlebars.helper('renderCard', function(context, type, card, options) {
    if (type && card)
        return Ember.Handlebars.helpers.render.call(context, type, card, options);
});

我在新创建的 helper 里调用 render helper,不过最后还是报:Uncaught TypeError: Object [object Object] has no method 'match' 的错误。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号