7
votes

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>
1
Will you show your template, it's likely you aren't using the itemController.Kingpin2k
Check in the Ember Chrome debugger and look at the view hierarchy. You will see what controller is being used and find out why it isn't working. My guess is it is looking for the TodoIndexController, if you're using such a route in your template. Also, remember you can put actions in Routes if it suits your needs.Steve H.
Thanks guys, added the templates, sure it's something simple I'm not doing in the correct ember way.brownie3003
Thanks @steveH. I looked at the view hierarchy. It is application -> todos -> todos.index and I saw that the todos.index was using a generated todos.index controller, so I copied my controller into controllers/todos/index.js and it worked. Now just need to figure out how to get it to work with active and complete routes, surely don't need 3 identical controllers in todos folder.brownie3003

1 Answers

9
votes

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.