Search code examples
sproutcore

Why doesn't this work? (SproutCore & Todos Example)


I'm trying to modify the Todos Examples to get a better understanding of the framework.

I'm trying to modify the todosController to add a 'completed' computed property that returns all the completed todos. On top of this, I'm trying to get the 'areAllCompleted' property to update.

I have this code, which does not update 'areAllCompleted' when 'completed' has changed.

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')
});

However, if I change the code slightly to add a binding, it works:

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')
});

Why does this subtle difference suddenly make it work?

Thanks.


Solution

  • When you are using .property() method, the parameters are expected to be direct properties of the object; so, it doesn't expand upon the property path that you are passing (.*completed.length). When you setup the binding, you are basically telling SproutCore that you want the path (.*completed.length) bound to a property of the object, which is why the second one works; since it has become a simple property.

    Since you are setting both of these based off of the completion, another way that you could do it is by using a single function with .observes() which does follow property paths, but that is a bit complex. Following is how I would probably handle this:

    /*
     * 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