public
Description: JavaScript Application Framework - JS library only
Home | Edit | New

Keeping Control

SproutCore Controller Classes and Their Differences

  • SC.ObjectController: An ObjectController gives you a simple way to manage the editing state of an object. You can use an ObjectController instance as a “proxy” for your model objects. For example, if you create a Record object called “Albums.Vinyl” and plan on allowing your application to edit the properties of your objects, it would be a good idea to create an ObjectController and call it “Albums.vinylController” to be more precise. This object could become the receptacle for the properties of whichever Vinyl is currently selected on a web page, by setting the contentBinding property. For example, you could setup your object controller to show the selected vinyl by doing:

Albums.vinylController = SC.ObjectController.create({
  contentBinding: "Albums.vinylsListController.selection"
});

An ObjectController can control any class extending SC.Object.

  • SC.ArrayController: An Array Controller provides a way to project the contents of an array out to a view. You can use this object any place you might use an array. This will probably be the Controller that is most useful to you and gives you the most control over its objects, with the least “overhead.” When in doubt, use this one.

An ArrayController expects to work with an object that implements the SC.Array mixin. All built-in JavaScript Arrays implement SC.Array. Other classes that are not native Arrays but implement SC.Array are called “array-like” objects. Set the content property of your ArrayController to the array or array-like object you want to control. For example:


  var recordArray = Albums.Vinyl.findAll() ;
  Albums.masterController.set('content', recordArray) ;

To manage items in your array, you should bind views to the “arrangedObjects” property. This property represents the content array in a form usable by the CollectionViews in your UI. (i.e. SC.ListView, SC.GridView, etc.) ArrayControllers also implement a “selection” property which is an array of your currently selected items. Bind the selection property of your collection views to the selection property of your array controller.

  • SC.CollectionController: A CollectionController works just like an ArrayController except that it includes support for working with SC.Collection objects. An SC.Collection object is like a “smart group” in that it updates its contents automatically based on preset filters you have designated. In Database design terms, this might be thought of as a table-view, while an ArrayController would be the table itself. So for instance, when you want to delete a “row” in your db table, you would not try to delete it from the view, you would delete it from the actual table.

A CollectionController expects to be working with an SC.Collection object or something extending this class, as its “content” property. For example :


  var recordCollection = Albums.Vinyl.collection() ;
  Albums.masterController.set('content', recordCollection) ;
  recordCollection.refresh() ;
  • SC.Controller: The controller base class provides some common functions you will need for controllers in your applications, especially related to maintaining an editing context. In general you will not use this class, but you can use a subclass such as ObjectController, CollectionController, or ArrayController.

Basic example of CRUD application using Controller classes

Lets say you are creating a basic web application to track your vinyl collection. You would like to be able to manage a list of records, find individual albums by name, add, delete, and edit metadata about albums, such as artist name, year released, number of tracks, and condition of the vinyl. These basic tasks – (Create, Read, Update, Delete) – are basic to most web applications, so a lot of this is already done for you in SproutCore.

Your new application is going to be called: “Albums”
And it will track an unlimited amount of Record objects called “Vinyl”

To create your application, do:

sc-init albums

Then, to create your model, do:

sc-gen model albums/vinyl

This creates a file for your model clients/albums/models/vinyl.js, a file to create fixtures (instances of your model that will load initially without needing a database and that are helpful for experimentation purposes) clients/albums/fixtures/vinyl.js, and a file to define tests for your model clients/albums/tests/models/vinyl.rhtml.

To make sure all of your objects that you will create are distinct from anyone else’s objects, the sc-gen model command creates a namespace. (See example code in core.js: server: SC.Server.create({ prefix: ['Albums'] }).)

And since the Vinyl class is part of the Albums application, the syntax generated for you to create the namespace is:


Albums.Vinyl = SC.Record.extend(
/** @scope Albums.Vinyl.prototype */ {

When viewing your albums, it would be reasonable to want to sort them by various criteria, such as title, artist, year, etc. So this need suggests that a Collection object would be the best way to store them, and a CollectionController would be the best way to manage this Collection object.

Your main controller will be called vinylController, and you can create it with:

sc-gen controller albums/vinylController SC.CollectionController

This creates a file for your controller clients/albums/controllers/vinyl.js and a file to define for your controller clients/albums/tests/controllers/vinyl_controller.rhtml.

In vinyl.js, you should see the line:
Albums.vinylController = SC.CollectionController.create(

Seeing What You Have

Once these classes are created, you could create a custom view object (Creating Custom Views) to see them, or just use one of the many SC.View derived classes already created for you. Either way, you will call these classes into action on your body.rhtml page by using view helper syntax.

Example:


<div class="recordList">
<% scroll_view :record_list_scroll_view, :height => 200 do %>
  <%= list_view :record_list,
    :content_value_key => 'album_name',
    :content_value_editable => true,
    :can_reorder_content => true,
    :can_delete_content => true,
    :bind => {
      :content => "Albums.vinylController.arrangedObjects",
      :selection => "Albums.vinylController.selection"
    } %>

<% end %>
</div>

One way to think about SC.Collection is that it’s like a SQL query. But unlike a SQL query, it’s more like a “smart” query (using iTunes
lingo) that will update whenever the contents of SC.Store change.

Related Links

Comments

In general, this is good, and is what makes it so easy to bind your model layer to your controllers, and your controllers to your views.

However, removing a record from a collection without doing anything else doesn’t make much sense. A collection is a smart query, not some arbitrary list, so the only way to change what is in the collection is to change the objects the query is working against. Because of this, some of you have discovered that you need to “remove” the record from the store to remove it from the collection. Hopefully now, it should make sense why that works, and why it’s not a good solution.

The real solution is easy. If you need to present an arbitrary list of records to your controller (and from there on up to your views), be easy on yourself and do not use a smart query to hold them (e.g. do not use SC.Collection).

Instead, use a normal, plain jane, vanilla JavaScript array. It’ll stay “in order” automatically. You can add (and remove) records, of any type, at any time, and as long as you use the methods in SC.Array to do so, your controllers and views will update as expected. -onitunes

Last edited by mattdipasquale, Wed Aug 20 13:41:52 -0700 2008
Home | Edit | New
Versions: