Skip to content

Step 5: Finishing the UI

juanpin edited this page Sep 12, 2010 · 16 revisions

NOTE: NOT UPDATED FOR SPROUTCORE 1.0 API. THE REST IS OUT OF DATE AND A WORK IN PROGRESS

Now that we can see our data, we need to finish out a few bits of the UI. We want to allow users to:

  • Edit tasks
  • Reorder tasks
  • Delete tasks
  • Add new tasks

The first three are easy to add before they are managed for you by the ListView automatically. Just open your body.rhtml and edit the list_view helper to include the following options:

<%= list_view :outlet => true,
    :content_value_key => 'title',
    :content_checkbox_key => 'isDone',
    :content_value_editable => true,
    :can_reorder_content => true,
    :can_delete_content => true,
    :bind => {
      :content => "Todos.tasksController.arrangedObjects",
      :selection => "Todos.tasksController.selection"
    } %>

Now refresh. Double clicking on a title will allow you to edit. Click and drag will reorder. Selecting and pressing the Backspace or Delete keys will delete it, kind of. Actually tasks are deleted, they are just removed from the array that is begin this controller. That won’t really do the trick. We need to turn on a feature in the ArrayController to help us. Add this property to your tasksController:


destroyOnRemoval: YES

This tells the controller you want the member records to be destroyed when they are removed from the array in addition to just removing them.

Last thing we need to do is to hook up the Add and Delete buttons.

Set an action to the Add Button

Update the add button javascript in main_page.js to trigger the “addTask” method on the controller when it is pressed.

    addButton: SC.ButtonView.design({
        layout: { centerY: 0, height: 21, right: 116, width: 100 },
       title:  "Add Task",
       target: "Todos.tasksController",
       action: "addTask"
      }),

Now add the addTask method. This method needs to do three things:

  1. Create a new task record. This record will be added to the store automatically, but since we are keeping our tasks in an ordered array, it also needs to be added to the array.
  2. Add the task to the array managed by this controller. This will cause the task to be displayed in the collection UI because we use observable array methods.
  3. Get the itemView for the last from the list view and begin editing on it.

Here is the code. Add this to the tasksController:

  addTask: function() {
    
    // Create a new task, with a default title.  
    // We use newRecord() here because it will add it to the store by default.
    var task = Todos.Task.newRecord({
      title: 'Untitled'
    }) ;
    
    // Add the task to the end of the current array.  Note that we use the
    // SC.Array method, since this is observable.
    this.pushObject(task) ;

    // Now, we need to get the item view for the new task from the list view.
    // Since the the task list has not yet had a chance to update with the new
    // content, we do this the next runloop.

    // Find the list view from the page.
    var listView = SC.page.getPath('todos.taskListScrollView.taskList') ;
    var itemView = listView.itemViewForContent(task) ;

    // Begin editing on the found itemView.
    itemView.beginEditing() ;
  }

Note the use of getPath() to find the view. All views are stored in an object called SC.page. You can follow their hierarchy as defined in your view helpers. getPath() will split your path apart and call get on each thing. It’s the same as writing SC.page.get(‘todos’).get(‘taskListScrollView’).get(‘taskList’).

Add the Delete Button

Deleting is similarly easy. Add in main_page.js the delete button right after the addButton

deleteButton: SC.ButtonView.design({
        layout: { centerY: 0, height: 21, right: 8, width: 100 },
       title:  "Delete Task",
       target: "Todos.tasksController",
       action: "deleteTask"
      })

The deleteTask method needs to do the following:

  1. Get the currently selected tasks
  2. remove the object from the array and
  3. call destroy() on each item in the record.

And here is the delete code. Add this after the addTask method in your controller. Don’t forget to put a comma after the addTask method:

  
  deleteTask: function() {
  
  	//get the selected tasks
  	var sel= this.get('selection');
  	var store=Todos.get('store');
	
	//pass the guids to be destroyed
  	store.destroyRecords(Todos.Task, sel.get('guid'));
  	//commit the operation to send the request to the server
  	store.commitRecords();
  }

Disabling the Delete Button

This is all well and good, but we have another problem. The delete button is always enabled. If you don’t have any tasks selected, you can’t delete them.

Solve this with a computed property:

  canDeleteTask: function() {
    var sel = this.get('selection') ;
    return (sel != null) && (sel.get('length') > 0) ;
  }.property('selection') 

This property will change whenever the selection changes. It becomes true whenever the selection length is greater than 0. Now, we need to add a binding to the delete button:

    <%= button_view :title => 'Delete', 
        :action => 'Todos.tasksController.deleteTask',
        :bind => { :enabled => 'Todos.tasksController.canDeleteTask' } %>

OK. try your handiwork. This is looking pretty good. You can add tasks, delete tasks, reorder them, rename them, and, of course, check them off as done or not done.

Continue to next step: Step 6: Building the Backend »

Related Links

Comments