我如何让Ember.js handlebars #each遍历对象?

jfewjypa  于 2022-11-05  发布在  其他
关注(0)|答案(3)|浏览(149)

我试图让{{#each}}帮助器迭代一个对象,就像在vanilla handlebar中一样。不幸的是,如果我在一个对象上使用#each,Ember.js版本会给我这个错误:
Assertion failed: The value that #each loops over must be an Array. You passed [object Object]
我写这个助手试图补救这一点:

Ember.Handlebars.helper('every', function (context, options) {
  var oArray = [];
  for (var k in context) {
    oArray.push({
      key   : k,
      value : context[k]
    })
  }
  return Ember.Handlebars.helpers.each(oArray, options);
});

现在,当我尝试使用{{#every}}时,我得到以下错误:
Assertion failed: registerBoundHelper-generated helpers do not support use with Handlebars blocks.
这似乎是一个基本的功能,我知道我可能错过了一些明显的东西。有人能帮助吗?

编辑

这里有一把小提琴:http://jsfiddle.net/CbV8X/

cgyqldqp

cgyqldqp1#

使用{{each-in}}助手。您可以像{{each}}助手一样使用它。
示例:

{{#each-in modelWhichIsObject as |key value|}}
  `{{key}}`:`{{value}}`
{{/each-in}}

JS Bin demo.

sqougxex

sqougxex2#

在摆弄了几个小时之后,我想出了这个古怪的办法:

Ember.Handlebars.registerHelper('every', function(context, options) {
  var oArray = [], actualData = this.get(context);

  for (var k in actualData) {
    oArray.push({
      key: k,
      value: actualData[k]
    })
  }

  this.set(context, oArray);
  return Ember.Handlebars.helpers.each.apply(this, 
      Array.prototype.slice.call(arguments));
});

我不知道this.set有什么影响,但这似乎是工作!
这里有一把小提琴:http://jsfiddle.net/CbV8X/1/

omtl5h9j

omtl5h9j3#

我一直在寻找类似的功能,既然我们分享了我们的黑客方式,这里是我的小提琴为不耐烦:http://jsfiddle.net/L6axcob8/1/
这个小提琴是基于@lxe提供的一个,由@Kingpin2k更新,然后是我自己。
Ember:1.9.1,车把:2.0.0版,jQuery 2.1.3版
这里我们添加了一个名为every的助手,它可以迭代对象和数组。
例如此模型:

model: function() {
    return {
        properties: {
            foo: 'bar',
            zoo: 'zar'
        }
    };
}

可以使用以下把手模板进行迭代:

<ul class="properties">
    {{#every p in properties}}
    <li>{{p.key}} : {{p.value}}</li>
    {{/every}}
</ul>

every helper的工作原理是从对象的键创建一个数组,然后通过ArrayController协调对Ember的更改。是的,很有技巧。不过,这确实可以,让我们在对象中添加/删除属性,前提是该对象支持观察[]属性。
在我的用例中,我有一个Ember.Object派生类,当属性被添加/删除时,它会通知[]。我建议看看Ember.Set的这个功能,尽管我看到Set最近被弃用了。因为这稍微超出了这个问题的范围,我将把它留给读者作为练习。这里有一个提示:设置未知属性
为了得到属性更改的通知,我们将非对象值 Package 在一个我称为 DataValueObserver 的东西中,这个东西建立了(目前是单向的)绑定,这些绑定在我们内部的ArrayController所保存的值和我们正在观察的对象之间提供了一个桥梁。
处理对象时;我们把它们 Package 在ObjectProxy中,这样我们就可以引入一个'key'成员而不需要修改对象本身。为什么是的,这确实意味着你可以递归地使用#every。这是给读者的另一个练习;- )
我建议你的模型基于Ember.Object,这样可以和Ember的其他部分保持一致,允许你通过get和set处理程序来操作你的模型。或者,就像在小提琴上演示的那样,你可以使用Em.Get/Em.set来访问模型,只要你这样做是一致的。如果你直接接触你的模型(没有get/set),那么every不会得到你的改变的通知。

Em.set(model.properties, 'foo', 'asdfsdf');

为了完整起见,下面是我的every助手:

var DataValueObserver = Ember.Object.extend({
    init: function() {
        this._super();
        // one way binding (for now)
        Em.addObserver(this.parent, this.key, this, 'valueChanged');
    },
    value: function() {
        return Em.get(this.parent, this.key);
    }.property(),
    valueChanged: function() {
        this.notifyPropertyChange('value');
    }
});

Handlebars.registerHelper("every", function() {
    var args = [].slice.call(arguments);
    var options = args.pop();
    var context = (options.contexts && options.contexts[0]) || this;

    Ember.assert("Must be in the form #every foo in bar ", 3 == args.length && args[1] === "in");
    options.hash.keyword = args[0];
    var property = args[2];

    // if we're dealing with an array we can just forward onto the collection helper directly
    var p = this.get(property);
    if (Ember.Array.detect(p)) {
        options.hash.dataSource = p;
        return Ember.Handlebars.helpers.collection.call(this, Ember.Handlebars.EachView, options);
    }

    // create an array that we will manage with content
    var array = Em.ArrayController.create();
    options.hash.dataSource = array;
    Ember.Handlebars.helpers.collection.call(this, Ember.Handlebars.EachView, options);

    //
    var update_array = function(result) {
        if (!result) {
            array.clear();
            return;
        }

        // check for proxy object
        var result = (result.isProxy && result.content) ? result.content : result;
        var items = result;

        var keys = Ember.keys(items).sort();

        // iterate through sorted array, inserting & removing any mismatches
        var i = 0;
        for ( ; i < keys.length; ++i) {
            var key = keys[i];
            var value = items[key];
            while (true) {
                var old_obj = array.objectAt(i);
                if (old_obj) {
                    Ember.assert("Assume that all objects in our array have a key", undefined !== old_obj.key);
                    var c = key.localeCompare(old_obj.key);
                    if (0 === c) break; // already exists
                    if (c < 0) {
                        array.removeAt(i); // remove as no longer exists
                        continue;
                    }
                }

                // insert
                if (typeof value === 'object') {
                    // wrap object so we can give it a key
                    value = Ember.ObjectProxy.create({
                        content: value,
                        isProxy: true,
                        key: key
                    });
                    array.insertAt(i, value);
                } else {
                    // wrap raw value so we can give it a key and observe when it changes
                    value = DataValueObserver.create({
                        parent: result,
                        key: key,
                    });
                    array.insertAt(i, value);
                }
                break;
            }
        }
        // remove any trailing items
        while (array.objectAt(i)) array.removeAt(i);
    };

    var should_display = function() {
        return true;
    };

    // use bind helper to call update_array if the contents of property changes
    var child_properties = ["[]"];
    var preserve_context = true;
    return Ember.Handlebars.bind.call(context, property, options, preserve_context, should_display, update_array, child_properties);
});

灵感来源:

如果你错过了,这把小提琴又来了:

相关问题