Todos 04-Hook Up Your Data

Now we want to show the tasks in your data. To do this, you are going to need a controller. Controllers are used for data binding, filtering bound data, event handling, and acting as a delegate. They help to shuffle data between your models object and your views, and help to manage committing these changes back to the server when necessary.


Creating a Controller


In our case, we are going to need one controller for now called the tasksController. This will be a ArrayController since it helps to display a collection of records held in an array. The SproutCore generator will help you create one of these. On the command line, type the following:


sc-gen controller Todos.tasksController SC.ArrayController


This will create a file in apps/todos/controllers/tasks.js. Open the file and take a look at it. It should now read:


Todos.tasksController = SC.ArrayController.create( 

/** @scope Todos.tasksController.prototype */ {    


  // TODO: Add your own code here.  


}) ; 


IMPORTANT: SC.ArrayController and the related SC.ObjectController behave as a proxies for the object you set as their "content" property.  This means that when you work with controllers, instead of accessing properties on the controller content you will access the controller itself.   For example, you can normally bind views in your UI directly to the ArrayController and they will work as if you bound directly to the content array.  Later, you can change the content of the controller to point to another array and UI will update appropriately without needing you to reconfigure each UI item individually.


In the Todos app, this ArrayController is going to represent our list of todos.  We will eventually set the content property of this array to point to the list of todos.  For now though, we need to connect our ListView to the controller so that it can display that content.


Hooking Up Your View


Back in your main_page, you created an SC.ListView.  We want this list view to display the contents of the collection controller we just created.  We do this using binding.


Bindings are like wires that connect parts of your application. They automatically relay changes from one part of your app to the other. An ArrayController in particular has two properties that we need to bind to the list view:



To bind your ListView to these new properties, you simply need to add the bindings to your design. Open up your main page design again (in apps/todos/resources/main_page.js) and edit the SC.ListView design like so:


in apps/todos/resources/main_page.js:


  contentBinding: 'Todos.tasksController.arrangedObjects',

  selectionBinding: 'Todos.tasksController.selection' 



Go ahead and reload your page now. You will notice that you task list is still empty. Your list view is now getting some content, but it is an empty array! The controller still doesn’t have any content. Let’s fix that.


Creating an array of tasks.


When you load your SproutCore application in your web browser, SproutCore loads your views and all of your classes and then finally calls your main() function. This is defined in apps/todos/main.js. The last thing you will do in this function is get the initial set of data you want to display and plug it into a controller. Let’s do that now. Open your main.js file and add the following to the bottom of the Todos.main() function in main.js:


in apps/todos/main.js:

var tasks =;

Todos.tasksController.set('content', tasks);


In your fully developed application, this code would reach out to your server and return an array of tasks from the server. Since we are still using fixtures, this method returns an SC.RecordArray with the task objects found in your fixtures. It then sets that record array as the content of your tasksController.


Remember that a controller will “proxy” its content property to any view that is bound to it. In this case, since you set the set of tasks as the content of the Todos.tasksController, the controller will proxy those records to the list view that is bound to it.  Go ahead and refresh now to see what happens:




Eureka! We have data! All of your tasks are listed there. You can even click on them to select.


Configuring Your List Items


So far so good, but the tasks do look pretty ugly. That’s because we haven’t told the list view how it should interpret the task object to display it yet. Since we haven’t given it any clues, it just converts the task to a string and you get what you see here.


As you might guess, you can configure how the ListView should display this by adding options in your main page again. Edit your SC.ListView in the main page like so:


in apps/todos/resources/main_page.js:


  contentBinding: 'Todos.tasksController.arrangedObjects',     

  selectionBinding: 'Todos.tasksController.selection',     

  contentValueKey: "description",     

  contentCheckboxKey: "isDone",


  rowHeight: 21 



And refresh. Now we’re cooking! You can select your items, and check the boxes.




The contentFOOKey syntax here basically tells the ListView which properties on your content object map to the properties needed by its list items. In this case, the “value” property on a list item is set to show the description and the “checkbox” property is set to show the isDone state.



Create a calculated Property for the item count


Now that we have some data loaded we can have some calculated properties based on the data, like the number of records loaded or the number of selected items in the list. To do this we will create a computed property called "summary" that will compose the appropriate string to be set at the bottom view.


A computed property is a value that is generated dynamically whenever you try to access it.  Other languages have you implement computed properties by writing accessor methods for every property on your object, even those that you don't want to compute.  This often leads to a lot of boiler plate code like this Java-like example:


 getName: function() {

    return this._summary;



 setName: function(newValue) {

    this._summary = newValue;

    return this;



Rather than force you to write accessor methods for every property on your object, SproutCore implements two generic accessor methods called get() and set().  You can use get() and set() to work with any property on any SproutCore object, even if you've never explicitly defined that property before.


Normally, when you call object.get('foo'), SproutCore will simply lookup the value of "foo" on your object and return it.  Likewise, when you call object.set('foo', newValue), it will simply update the value of foo.  This is the equivalent of the boiler-plate accessors shown above.


If you want to override this behavior and compute the property value instead, you can do so easily by defining a computed property method.  A computed property method is simply a method that ends with a special notation "property()".  This tells SproutCore that when you call get() on that property, it should run this function and return the result instead.


We are going to use the computed property facility now to implement the "summary" property on our tasksController.  The summary property will return a string that we can show in the UI indicating the number of items in the UI.  Here is the code you want to add:



in apps/todos/controllers/tasks.js:

Todos.tasksController = SC.ArrayController.create({


  summary: function() {

    var len = this.get('length'), ret ;


    if (len && len > 0) {

      ret = len === 1 ? "1 task" : "%@ tasks".fmt(len);

    } else ret = "No tasks";


    return ret;




First we get the number of items in the array.  After that it goes through some logic to build the final string.


At the end of the function we call .property() , converting this function into a computed property. This property is recalculated if the 'length' changes. It is also cacheable, meaning that it won't be calculated everytime the property is called.


Note: Here 'length' refers to SC.ArrayController length.  A computed property can be calculated with one or more controller native properties and will be recalculated whenever native properties state changes.


To reflect the changes in your UI we have to bind the text in your summary view to the calculated property.   Find the "summaryView in your page design and replace the "value" property with a binding:


in apps/todos/resources/main_page.js:


  layout: { centerY: 0, height: 18, left: 20, right: 20 },

  textAlign: SC.ALIGN_CENTER,  

  valueBinding: "Todos.tasksController.summary"



Reload your app, you should see at the bottom of your screen the total of loaded records.


Now that you have your basic data setup, next we’ll work on wiring up the remaining bits of the UI.


Continue to next step: Step 5: Finishing the UI »