Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ember component send action to route

Tags:

ember.js

I have two component and one sitting on another. I need to send an event to main route from child component( both components use in same route)

enter image description here

Please let me know is there any standard way to do that.

like image 342
kamprasad Avatar asked Feb 17 '17 10:02

kamprasad


2 Answers

For short answer you can you can use ember-route-action-helper addon.

<button {{action (route-action 'onButtonClick')}}>ClickToCallRouteAction</button>

There are three way of actions communication,

1. Old style classic functions style ie., passing function name as string from top to bottom. and in all the places we need to define same function and provide. Use sendAction to bubble. and send method bubble from controller to route hierarchy.

This is not encouraged. Sample classic style actions twiddle

2. Closure actions Use action helper pass function instead of just string. so that you don't need to define it everywhere. sample twiddle for closure actions style

3. route-action-helper addon You can directly call route action from anywhere literally by just wrapping functions using route-action helper.

Sample twiddle

Comparision between Classic style and Closure style and Why Closure is preferrable ?

  • In classic style, You need to define actions at each level and use sendAction to trigger the action at each level until you got all the way out of your nesting.
  • You can return value in closure actions but not in classic actions.
  • You can curry values in closure actions but not in classic actions.
  • Closure actions fail immediately if the action is not found. but classic actions by design,would lazily raise errors only upon invocation values.
  • Coding complexity like who will handle actions and do business logic?.
  • In closure, you can combine action and mut helper to set a property with value. onclick=(action (mut title) value="titlevalue")
  • In closure, you can specify target object to invoke function. (action 'save' target=session) would look at the actions hash on the session object instead of the current context.

Some of the promising article regarding this,
- miguelcamba article ember-closure-actions-in-depth
- emberigniter article send-closure-actions-up-data-owner
- emberjs blog 1.13 release article
- dockyard - ember-best-practice-stop-bubbling-and-use-closure-actions
- blog from Ember map Why action helper?
- blog from Alisdair McDiarmid ember-closure-actions-have-return-values
- blog from alexdiliberto ember-closure-actions

like image 88
Ember Freak Avatar answered Sep 21 '22 07:09

Ember Freak


Starting with Ember 3.14, Octane, we can solve this problem in a modern, explicit, concise and clear way -- which we'll get to after this brief intermission:

I need to send an event to main route from child component

While this is possible, it's strongly recommended against, as Routes should not have actions and should be stateless. That said, we can solve the problem of action passing through deep components in a couple of ways:

  • "Data Down, Actions Up"
  • Use a Service

For the first, Data Down, Actions Up, you may pass arguments down as many component layers as you desire

// app/controllers/application.js:
@action
dance(){
  console.log('┏(-_-)┓┏(-_-)┛┗(-_- )┓')
}

// app/templates/application.hbs
<Foo @dance={{this.dance}} />

// app/components/foo.hbs
<Bar @dance={{@dance}} />

// app/components/bar.hbs
<button {{on 'click' @dance}}>Dance!</button>

This could be a slipperly slope. While only having two components to data down, and to action back up (after a click in this case), it may not seem like too much effort, but many UIs could be 10+ components deep, and would lend themselves to an anti-pattern known as Prop-Drilling.

To mitigate prop-drilling, we have another approach in our toolbox to reach for. Services!

// app/services/my-service.js
@action
dance(){
  console.log('┏(-_-)┓┏(-_-)┛┗(-_- )┓')
}

// app/components/bar.js
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';

export default class Bar extends Component {
  @service myService;
}

// app/components/bar.hbs
<button {{on 'click' this.myService.dance}}>Dance!</button>  

The deeply nested component can access the action directly, rather than needing to be passed through a few layers -- this leads to much more maintainable and clear code.

Resources

  • Classic to Octane Cheat Sheet
  • Octane Edition Documentation Page
like image 26
NullVoxPopuli Avatar answered Sep 20 '22 07:09

NullVoxPopuli