Using components instead controllers in EmberJS v2


#1

I am beginner in EmberJS. I have made todomvc for ember v2 for learning Ember.

My implementation works without controller. I use components instead controllers. Because in documentation I see

Controllers are very much like components, so much so that in future versions of Ember, controllers will be replaced entirely with components.

But I think it is some ugly solution. Now I will explain why.

This is template for todo item component for my todolist app/templates/components/todo-item.hbs:

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

It has acceptChanges and removeTodo events. And in js part of component I have handlers:

//app/components/todo-item.js
import Ember from 'ember';

export default Ember.Component.extend({

    updateTodoAction: 'updateTodo',
    deleteTodoAction: 'deleteTodo',

    isEditing: false,
    
    // some lines removed for brevity

    actions: {
      editTodo: function() {
        this.set('isEditing', true);
      },

      acceptChanges: function() {
        this.set('isEditing', false);
        this.sendAction('updateTodoAction', this.get('item'), this.get('item.title'));
      },

      removeTodo: function () {
        this.sendAction('deleteTodoAction', this.get('item'));
      }
    }
});

In js code of component I don’t work with storage. I send this.sendAction(), and my route will handle it. And todos-route works with storage. Please look at todos route:

// app/routes/todos.js
import Ember from 'ember';

export default Ember.Route.extend({
    model() {
        return this.store.findAll('todo');
    },

    actions: {
        // some lines removed from here for brevity

        updateTodo: function(todo, title) {
            if (Ember.isEmpty(title)) {
                this.send('deleteTodo', todo);
            } else {
                todo.save();
            }
        },

        deleteTodo: function(todo) {
            todo.deleteRecord();
            todo.save();
        },

       // some lines removed from here for brevity
    }
});

When user clicks on remove button, it generates removeTodo action and it will handled in removeTodo() in js part of component. And then removeTodo() function calls this.sendAction(‘deleteTodoAction’ … ), which generates deleteTodo action. This action will handled in todos route in deleteTodo() method. I have todo.save() in this method.

The acceptChanges action works much like removeTodo, but it sets property isEditing = false before calling this.sendAction.

I have following chain for actions

component template -> component js -> route

I think for deleteTodo() will be better to exclude component js part. But how? In updateTodo() I need component js part, because I set value for isEditing property. But for calling route I need updateTodoAction property. And it is ugly for me. Can it work without updateTodoAction property? How?

Also I’d like to see any your comments about my solution (components instead controllers) or any part of my code.

Thank you, friends!


#2

For learning components and more I changed my Bloggr example to Ember 2.2.x with components. See

Hope it will help solve some of your questions.


#3

Hi Broerse!

I see in your example you use controllers. But in my example I don’t use it, because in future controllers will removed from Ember.

Can you give me more details about problem?

Thank you


#4

Until we have routeable components we still need controllers. I also still use controllers for query parameters but it is on my list to move query parameters to the router.


#5

But I need not routeable components… An I need not query parameters… I think controller will be one more unnecessary element in chain. ArrayControler is deprecated in EmberJs 1.13. And in Ember 2 ArrayController removed. I use components instead it. I just need have bubbling events of components up to route.


#6

#7

hi jasonmit! Very interesting information. I see this plugin calls route’s action handler for event of component. But in my example I need and handlers in component and handlers in route. Handler in component for changing GUI state. Handler in route for storing a model. My be it is not bubbling …


#8

You don’t need bubbling in this case. A DOM event is triggering the action on the component. Then from within the component’s action you set state and invoke the action on the route which returns a promise.

An example of the flow: http://jsbin.com/kovejinexe/1/edit?html,js,output (use latest of Chrome or Chromium to run this since I’m using ES6 syntax)


#9

Thank you for example, jasonmit! What do you think, way with ember-route-action-helper is better than my way (example is in my first post)? And why?

I see for acceptChanges() my way is better. But for removeTodo() way with ember-route-action-helper (with bubbling) will be better. Because for removeTodo() we need not change GUI.

Am I right?

Up