Ember component sendAction() not working

12,396

Solution 1

You should define where the action will be sent when defining a component in the template.

{{edit-item value=desc createItem='someactionoutside'}}

this is in case the action has a different name in different places (since this is a component, it could have different meanings in different locations). It also avoids clashing actions/triggered actions. Think of the idea of having two instances of a component, and each one should trigger a different action in the controller

{{edit-item value=desc createItem='createUser'}}
{{edit-item value=desc createItem='createShoppingCart'}}

in your case you can just write

{{edit-item value=desc createItem='createItem'}}

And inside your component you would call

this.sendAction('createItem', param1, param2, ....);

If you don't care about it being self contained like a component, you might want to just use a view and not a component. You can register it as a helper and it'd look just as pretty.

Em.Handlebars.helper('edit-item', Em.View.extend({
  templateName: 'some_template',

  actions: function(){
   // etc etc
  } 

})); 

{{edit-item}}

Solution 2

As an addition to the nice answer by @Kingpin2k you can also define your action's name within the component if it is always the same and you want to simplify the syntax of including your component. ie.

import Ember from 'ember';
export default Ember.Component.extend(SchoolPlayerProspectMixin, {

    //Here we define an attribute for a string that will always be the same
    transitionToRoute: "transitionToRoute",

    somethingChanged: function(){
        console.log( "OMG something changed, lets look at a post about it!" );

        //Here we are passing our constant-attribute to the sendAction.
        self.sendAction('transitionToRoute', "post.show", post );

    }.observes('changableThing'),
});

In this example the component uses the parent controllers transitionToRoute method to change routes, even though the component may not be a button/link. For example, navigating on change of a component containing several select inputs, or just changing route from within a component in general.

Share:
12,396
Wilhearts
Author by

Wilhearts

Updated on June 06, 2022

Comments

  • Wilhearts
    Wilhearts almost 2 years

    i have been struggling with this for the past few hours, i am making an ember application for creating an invoice. i am using ember component (textfield) to modify the fields using the keyboard, but since actions are not sending back to the relevant controller, i cannot save the records on focusOut or insertNewLine and nothing is happening. i am using :

    Ember      : 1.1.2 
    Ember Data : 1.0.0-beta.3 
    Handlebars : 1.0.0 
    jQuery     : 1.9.1
    

    this is supposed to look like this: https://dl.dropboxusercontent.com/u/7311507/embercomponent.png

    The problem seems to lie within either the controller or the component, it seems i am missing something.

    the console.log function gets called on the component, the sendAction call never works...

    Thanks for the help.

    ItemsRoute

    App.ItemsRoute = Ember.Route.extend({
        renderTemplate: function() {
              // Render default outlet   
              this.render();
              // render extra outlets
              this.render("client", { outlet: "client", into: "application"});
          },
          model: function() {
            return this.store.find('item');
          }
        });
    

    ItemsController

    App.ItemsController = Em.ArrayController.extend({
        actions: {
          createItem: function () { // NEVER GETS CALLED FROM COMPONENT
            var title = "Nouvel élément"
    
            // Create the new Todo model
            var item = this.store.createRecord('item', {
              desc: title,
              qty: 1,
              price: 0
            });
    
            // Save the new model
            item.save();
          }
        },
        totalCount: function(){
            var total = 0;
            this.get('model').forEach(function(item){
                total += item.get('totalprice');
            });
            return total;
        }.property('@each.qty', '@each.price')
    });
    

    ItemController

    App.ItemController = Em.ObjectController.extend({
        didInsertElement: function(){
            this.$().focus();
        },
        actions: {
            testAction: function(){ // NEVER GETS CALLED FROM COMPONENT
                console.log("controller recieved call for testAction");
            },
            saveItem: function(value) {
                this.get('model').save();
    
            },
            removeItem: function() {
                var item = this.get('model');
                item.deleteRecord();
                item.save();
              },
        },
        isHovering: false
    });
    

    Items Template

    <script type="text/x-handlebars" data-template-name="items">
          <!-- ...  -->
    
          <tbody>
          {{#each itemController="item"}}
            {{view App.ItemView }}
          {{/each}}
          </tbody>
    
          <!-- ... -->
      </script>
    

    ItemView template

    <script type="text/x-handlebars" data-template-name="item">
        <td class="desc">{{edit-item value=desc}}</td>
        <td class="qty">{{edit-item-number value=qty }}</td>
        <td class="">{{edit-item-number step="25" value=price}}</td>
        <td class="totalprice">
          {{ totalprice }}
          <div class="delete-item" {{bindAttr class="isHovering"}} {{action "removeItem" on="click"}}>
            <i class="icon-trash"></i>
          </div>
        </td>
      </script>
    

    Views / Components

    App.ItemView = Em.View.extend({
        templateName: "item",
        tagName: "tr",
    
        mouseEnter: function(event) {
            this.get('controller').set('isHovering', true);
        },
        mouseLeave: function(event) {
            this.get('controller').set('isHovering', false);
        }
    });
    
    App.EditItem = Em.TextField.extend({
        becomeFocused: function() {
            this.$().focus();
        }.on('didInsertElement'),
    
        insertNewline: function(){
            console.log('Tried to insert a new line'); // WORKS
            this.triggerAction('createItem'); // DOESN'T WORK
        },
    
        focusOut: function(){
            console.log('Focused the Field Out') // WORKS
            this.triggerAction('testAction', this); // DOESN'T WORK
        }
    
    });
    
    App.EditItemNumber = App.EditItem.extend({
        becomeFocused: null,
        attributeBindings: ["min", "max", "step"],
        type: "number",
        min: "0"
    });
    
    Ember.Handlebars.helper('edit-item', App.EditItem);
    Ember.Handlebars.helper('edit-item-number', App.EditItemNumber);
    
  • Wilhearts
    Wilhearts over 10 years
    Thanks a bunch, this solved my problem, i understand the concept behind this (the components being self-contained), but it my case this introduces code repetition.
  • Kingpin2k
    Kingpin2k over 10 years
    A view might be easier if you don't care about being self-contained
  • 0xcaff
    0xcaff almost 9 years
    Also note the quotes in the template are really important.