Deletion of records only works the first time. Uncaught TypeError: view.destroy is not a function


#1

I’m trying to write a todo app with Ember. I’m following some guides, but I’m not doing things exactly as the guide and deletion of records is not working.

This is what I’ve got: router

Router.map(function() {
  this.route('home', {'path': '/'});
  this.resource('todos', {'path': '/todos' }, function() {
    this.resource('todo', {'path':  ':/todo_id' })
    this.route('new', {'path': '/new'} )
  });
});

todo-details component

<li>
  {{#if isEditing}}
    {{input type="text" class="edit" value=todo.title focus-out="acceptChanges" insert-newline="acceptChanges"}}
  {{else}}
    {{input type="checkbox" class="toggle" checked=todo.isCompleted}}
    <label {{action 'editTodo' on 'doubleClick'}}>{{todo.title}}</label><button class="destroy" {{action 'destroyTodo'}}></button>
  {{/if}}
</li>

{{yield}}


import Ember from 'ember';

export default Ember.Component.extend({
  tagName: 'li',
  classNameBindings: ['completed'],
  completed: function() {
    return this.get('todo.isCompleted');
  }.property('todo.isCompleted'),
  actions: {
    editTodo: function() {
      this.set('isEditing', true);
    },
    acceptChanges: function() {
      this.set('isEditing', false);
      this.sendAction('edit', this.get('todo'));
    },
    destroyTodo: function() {
      this.sendAction('destroy', this.get('todo'));
    }
  }
});

todos.index template

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

<section id="main">
    <ul id="todo-list">
        {{#each model as |todo|}}
            {{todo-item todo=todo edit="editTodo" destroy="destroyTodo"}}
        {{/each}}
    </ul>

    <input type="checkbox" id="toggle-all">
</section>

<footer id="footer">
    <span id="todo-count">
        <strong>2</strong> todos left
    </span>
    <ul id="filters">
        <li>
            <a href="all" class="selected">All</a>
        </li>
        <li>
            <a href="active">Active</a>
        </li>
        <li>
            <a href="completed">Completed</a>
        </li>
    </ul>

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

todos.index controller

import Ember from 'ember';

export default Ember.Controller.extend({
  actions: {
    createTodo: function() {
      this.get('newValue');
      var newTodo = this.store.createRecord('todo', {title: this.get('newValue'), isCompleted: false});
      this.set('newValue', '');
    },
    editTodo: function(todo) {
      if (Ember.isEmpty(todo.get('title'))) {
        this.send('destroyTodo', todo);
      }
      else {
        todo.save();
      }
    },
    destroyTodo: function(todo) {
      debugger;
      todo.deleteRecord();
    }
  }
});

The first time I want to delete a todo record, it successfully does it, but it logs to the console Uncaught TypeError: view.destroy is not a function. From there on, it stops deleting todos.

In case it’s useful: https://github.com/FranGoitia/todo

Thanks.


#2

It seems like you named a handler “destroyTodo”, but sending an action named “destroy”.


#3

Yes, because when I call the component from the index controller I’m passing destroy as being equal to destroyTodo.

<ul id="todo-list">
        {{#each model as |todo|}}
            {{todo-item todo=todo edit="editTodo" destroy="destroyTodo"}}
        {{/each}}
</ul>

#4

I am not sure, but it seems you just pass parameter to a component named “destroy”. Does this also affect action name?

Can you try to send action named “destroyTodo”?


#5

That was my first setup. From the template I was calling a component action and inside there I was sendting the action destroyTodo to the route/controller. It didn’t work. After re-reading the documentation on components I found out that when you are going to use the sendAction method inside the component, the value you pass to this function needs to be a variable that you setup when calling the component. Summary:

- component is being called from todos.index template and destroy=destroyTodo is passed as well
- component template calls its destroyTodo action when button is clicked. 
- destroyTodo action in todos.index controller is callled by sending destroy action from destroyTodo action in component.