Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to declare the item controller in an array controller - ember.js

Tags:

ember.js

I'm trying to write the Todo app (using ember-cli). When I added active and complete routes underneath my todos resource my item controller stopped working. Before I was using itemController in my Array controller to set my Object controller.

router.js

import Ember from 'ember';

var Router = Ember.Router.extend({
    location: TodoMVCENV.locationType });

Router.map(function() {
    this.resource('todos', { path: '/' }, function() {
        this.route('active');
        this.route('complete');
    }); 
});

export default Router;

controllers/todos.js

import Ember from 'ember';

var TodosController = Ember.ArrayController.extend({
    actions: {
        createTodo: function() {
            // Get the todo title set by the "New Todo" text field
            var title = this.get('newTitle');

            // Create the new Todo model
            var todo = this.store.createRecord('todo', {
                title: title,
                isCompleted: false
            });

            // Clear the "New Todo" text field
            this.set('newTitle', '');

            // Save the new model
            todo.save();
        }   
    },

    itemController: 'todo',

    allAreDone: function(key, value) {
        if (value === undefined) {
            return this.get('length') > 0 && this.everyProperty('isCompleted', true);
        }
        else {
            this.setEach('isCompleted', value);
            this.invoke('save');
            return value;
        }
    }.property('@each.isCompleted'),

    hasCompleted: function() {
        return this.get('completed') > 0;
    }.property('completed'),

    completed: function() {
        return this.filterBy('isCompleted', true).get('length');
    }.property('@each.isCompleted'),

    remaining: function() {
        return this.filterBy('isCompleted', false).get('length');
    }.property('@each.isCompleted'),

    inflection: function() {
        var remaining = this.get('remaining');
        return (remaining === 1) ? 'item' : 'items';
    }.property('remaining')
});

export default TodosController;

controllers/todo.js

import Ember from 'ember';

var TodoController = Ember.ObjectController.extend({
    actions: {
        editTodo: function() {
            this.set('isEditing', true);
        },
        acceptChanges: function() {
            // Remove is editing property
            this.set('isEditing', false);

            // If the todo is empty, delete it
            // otherwise save it with the new title
            if(Ember.isEmpty(this.get('model.title'))) {
                this.send('removeTodo');
            } else {
                this.get('model').save();
            }
        },
        removeTodo: function() {
            var todo = this.get('model');
            todo.deleteRecord();
            todo.save();
        }
    }
});

export default TodoController;

Before I added my nested routes, the actions in todo.js worked, now I when I try any of the actions in todo.js I get the following in the console:

Uncaught Error: Nothing handled the action 'editTodo'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble.

Adding the templates below from comments....

templates/todos.hbs

{{input type="text" id="new-todo" placeholder="What needs to be done?" 
    value=newTitle action="createTodo"}}

{{outlet}}

<footer id="footer">
    <span id="todo-count">
        <strong>{{remaining}}</strong> {{inflection}} left
    </span>
    <ul id="filters">
        <li>
            {{#link-to "todos.index" activeClass="selected"}}All{{/link-to}}
        </li>
        <li>
            {{#link-to "todos.active" activeClass="selected"}}Active{{/link-to}}
        </li>
        <li>
            {{#link-to "todos.complete" activeClass="selected"}}Active{{/link-to}}
        </li>
    </ul>

    <button id="clear-completed">
        Clear completed (1)
    </button>
</footer>

templates/todos/index.hbs

<section id="main">
    <ul id="todo-list">
        {{#each}}
            <li {{bind-attr class="isCompleted:completed isEditing:editing"}}>
                {{#if isEditing}}
                    {{input type="text" class="edit" value=title focus-out="acceptChanges"
                        insert-newline="acceptChanges"}}
                {{else}}
                    {{input type="checkbox" checked=isCompleted class="toggle"}}
                    <label {{action "editTodo" on="doubleClick"}}>{{title}}</label>
                    <button {{action "removeTodo"}} class="destroy"></button>
                {{/if}}
            </li>
        {{/each}}
    </ul>
</section>
like image 390
brownie3003 Avatar asked Jul 24 '14 23:07

brownie3003


1 Answers

Change your template to:

{{#each todo in content itemController="todo"}}
    {{#with todo}}
        ...
    {{/with}}
{{/each}}

I added the {{with}} block due to the updates in Ember 1.6.0 and the change in scope referenced here.

I like to add more markup to the templates like this for other devs to be able to quickly recognize whats going on in the javascript without having to open the Route's controller.

You could also replace "content" with "arrangedContent" if you decide to set some #sortProperties on the array controller.

like image 179
Chris Honniball Avatar answered Sep 22 '22 02:09

Chris Honniball