0

我正在尝试修改 Todos 示例以更好地理解框架。

我正在尝试修改 todosController 以添加一个“已完成”计算属性,该属性返回所有已完成的 todos。最重要的是,我正在尝试更新“areAllCompleted”属性。

我有这段代码,当“完成”发生变化时,它不会更新“areAllCompleted”。

TodosThree.todosController = SC.ArrayController.create({
    completed: function(){
        if(this.get('content')){
            return this.get('content').find(
                SC.Query.local(
                    TodosThree.Todo,
                    'isCompleted = true'
                )
            );
        }
        else {
            return [];
        }
    }.property('content').cacheable(),
    areAllCompleted: function (k, v) {
        console.log('get');
        if (v !== undefined) {
            this.setEach('isCompleted', v);
        }

        return this.getPath('completed.length') === this.get('length');
            # This .property definition doesn't work with .*completed.length .. ?
    }.property('length','.*completed.length')
});

但是,如果我稍微更改代码以添加绑定,它会起作用:

TodosThree.todosController = SC.ArrayController.create({
    completed: function(){
        if(this.get('content')){
            return this.get('content').find(
                SC.Query.local(
                    TodosThree.Todo,
                    'isCompleted = true'
                )
            );
        }
        else {
            return [];
        }
    }.property('content').cacheable(),
                                    # For some reason, this binding works ...
    completedLengthBinding: SC.Binding.oneWay('.*completed.length'),
    areAllCompleted: function (k, v) {
        console.log('get');
        if (v !== undefined) {
            this.setEach('isCompleted', v);
        }

        return this.getPath('completed.length') === this.get('length');
            # If I reference the binding that references completed, this now works ...
    }.property('length','completedLength')
});

为什么这种微妙的差异会突然使它起作用?

谢谢。

4

1 回答 1

1

当你使用.property()方法时,参数应该是对象的直接属性;因此,它不会扩展您传递的属性路径 ( .*completed.length)。当您设置绑定时,您基本上是在告诉 SproutCore 您希望将路径 ( .*completed.length) 绑定到对象的属性,这就是第二个有效的原因;因为它已成为一个简单的属性。

由于您是根据完成来设置这两个,因此您可以这样做的另一种方法是使用单个函数,.observes()该函数确实遵循属性路径,但这有点复杂。以下是我可能会如何处理这个问题:

/*
 * Note that using a store.find will auto-update when new todos are pushed
 * into the store, so there is no need to reset this every time.
 */
completed: TodosThree.store.find(
  SC.Query.local('TodosThree.Todo', 'isCompleted = true')
),


/*
 * Go ahead and grab the length so we can use it as a dependent property below
 */
completedLengthBinding: '*completed.length',


/*
 * Create the property function
 */
allAreCompleted: function(key, value) {
  if (value !== undefined) {
    this.setEach('isCompleted', value);
  }

  return this.everyProperty('isCompleted');
}.property('completed', 'completedLength')

A couple of things to note: since you're wanting to call allAreCompleted() and pass a value, you DO want this as a property, not just an observer. You could technically do it with a function that acts as both an observer and a property updater, but I think this is more clear.

Also, note the use of the everyProperty() method, which will iterate over each todo and ensure that the passed property is true for all todos.

Hope this helps! Please ask if you need clarification on anything :-D

于 2012-08-20T21:51:49.300 回答