1

so I have nested components in EmberJS and can't manage to properly handle their actions.

I have route Create and in its template Component pixel-grid:

<div class="row">
    <div class="col-sm-7">
        {{pixel-grid}}
    </div>
    <div class="col-sm-5">

    </div>
</div>

In pixel-grid template i have component named pixel-cell:

<table class="mx-auto">
    {{#each (range 0 panelWidth) as |row|}}
    <tr class="table-row">
        {{#each (range 0 panelHeight) as |cell|}}
        <th class="table-cell">
            {{pixel-cell onClick=(action 'changeColor')}}
        </th>
        {{/each}}
    </tr>
    {{/each}}
</table>

Component pixel-cell have an empty template since I don't need it for now. In pixel-cell component js file I have:

import Component from '@ember/component';
export default Component.extend({
    classNames: ['cell', 'clickable'],
    tagName: 'div',

    init() {
        this._super(...arguments);
    },
});

This code obviously do not compile since I did not handle this action. But..

I tried to set action in pixel-cell but then Ember told me that pixel-grid should have that action. So I did put this changeColor action in pixel-grid -> that doesn't work.

So I tried to handle this by something like this in pixel-cell js:

click() {
    this.sendAction('changeColor');
},

-> that doesn't work.

I have no idea how it should work ;/ I tried to read guides but still can't manage that. Please help.

bmrki
  • 371
  • 3
  • 15
  • `Component.sendAction()` has been deprecated in favor of closure actions: emberjs.com/deprecations/v3.x/#toc_ember-component-send-action – jelhan Jan 09 '19 at 08:15

4 Answers4

1

https://ember-twiddle.com/e04e318489bcc8e9e921e849c9fb9e9e?openFiles=templates.components.pixel-cell.hbs%2Ctemplates.components.pixel-grid.hbs

I have create a twiddle to show you a sample action passing from parent component to child component. You can refer the above url to understand it easier.

Instead of sendAction, I have used a concept called closure actions which is the norm in Ember going forward.

Neovire
  • 97
  • 1
  • 8
1

I would recommend against handling actions on component elements. Instead always use closure actions!

If you do {{some-component onClick=(action 'changeColor')}} you need the action changeColor on the corresponding, not inside some-component! However you probably want to use it inside some-component like this:

<button onclick={{@changeColor}}>...</button>

In your case I would set tagName: '' for the pixel-cell component and add this template:

<div onclick={{@changeColor}}></div>
Lux
  • 17,835
  • 5
  • 43
  • 73
0

Ok, so I did manage to get this to work. It looks like this:

pixel-grid template:

<table class="mx-auto">
    {{#each (range 0 panelWidth) as |row|}}
    <tr class="table-row">
        {{#each (range 0 panelHeight) as |cell|}}
        <th class="table-cell">
            {{pixel-cell}}
        </th>
        {{/each}}
    </tr>
    {{/each}}
</table>

pixel-cell component js file:

import Component from '@ember/component';
export default Component.extend({
    classNames: ['cell', 'clickable'],
    tagName: 'div',

    click: function () {
        this.sendAction('changeColor', this);
    },

    changeColor(cell) {
        console.log("CELL ACTION");
        console.log(cell);
        cell.element.style.backgroundColor = 'red';
    }
});

Others files are empty (only panelHeight and panel Width in pixel-grid js file). Is this a good way of doing this?

bmrki
  • 371
  • 3
  • 15
0

I personally never used sendAction. Instead, you should pass actions as attributes from parents to child components, then invoke them within child components. This approach is much easier to reason about as well as other benefits which https://emberigniter.com/send-closure-actions-up-data-owner/ explains in great details.

In your example:

pixel-grid.js:

export default Component.extend({
  actions: {
    changeColor(cell) {
      console.log("CELL ACTION");
      console.log(cell);
      cell.style.backgroundColor = 'red';
    }
  }
});

pixel-grid.hbs:

{{pixel-cell handleColorChange=(action 'changeColor')}}

pixel-cell.js:

export default Component.extend({
    classNames: ['cell', 'clickable'],
    tagName: 'div',

    click: function () {
        // Invoke parent action and pass this element as the argument
        this.get("handleColorChange")(this.element);
    }
});
kasperite
  • 2,460
  • 1
  • 15
  • 17