How to trigger an action on a controller from a template - ember.js

I have this jsbin. My problem is that I am trying to trigger an action:
<a {{action controllers.nodesIndex.destroyAllRecords this}}><i class="icon-remove-circle"></i><a/>
But I get an:
Uncaught Error: Nothing handled the event 'controllers.nodesIndex.destroyAllRecords'
(You can trigger that by pressing the little icon icon-remove-circle on the top-right, and checking the error on the js console)
But my controller is properly set-up:
App.NodesIndexController = Ember.ArrayController.extend({
destroyAllRecords: function () {
console.log('destroyAllRecords called');
},
});
What am I missing here?

Since the controller for the nodes/index template is the App.NodesIndexController, You need to mention it as controllers.nodesIndex.destroyAllRecords, the default target will be App.NodesIndexController, and so you can just say <a {{action destroyAllRecords}}> as #Thomas told.
Also for getting the length of records, just say {{this.length}} instead of {{controllers.nodesIndex.length}}.
I've updated your jsbin,
You'll need to say as 'controllers.controllername.methodname' only if you are referring to some other controller than the controller for the template, and you've to give the controller's name in the needs list,
say, if you want to call a method of your 'profile' route from your 'nodes/index' template,
then
App.NodesIndexController = Ember.ArrayController.extend({
needs: ['profile'],
});
and in your template,
<a {{action controllers.profile.methodname}}>
Hope it helps.
UPDATE: Refer the solution and the bin in the comment

Related

Ember2.8: Sending an action from a component to the controller

Reading up on the documentation for Ember, I was under the impression that when an action is triggered by a component, it will go up the hierarchy until it hits an action with that name. But here's what's happening right now. I have a game-card component written like so:
game-card.hbs
<div class="flipper">
<div class="front"></div>
<div class="back">
<img {{action "proveImAlive"}} src={{symbol}} />
</div>
</div>
game-card.js
import Ember from 'ember';
export default Ember.Component.extend({
classNames: ['flip-container'],
actions: {
//blank for now because testing for bubbling up
}
});
Now according to what I've read, since game-card.js does not have a 'proveImAlive' action, it will try to bubble up the hierarchy i.e. the controller for the particular route.
play.js (the route /play)
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
proveImAlive() {
console.log('Im aliiiiveeee');
}
}
});
But when I finally run my application, I get this error:
Uncaught Error: Assertion Failed: <testground#component:game-card::ember483> had no action handler for: proveImAlive
Now my question is twofold:
Why is this error happening?
I want some of my component's actions to bubble up to the route's controller. For example, when a game-card is clicked, i'd like to send the id value (to be implemented) of that card up to the controller so it can store it on an array.
game-card is clicked --> sends value of 1 --> arrayinController.push(1)
How can I achieve this?
First, I'd like to point out that you linked to the documentation of Ember v1.10.0. You should consult the documentation for the version of Ember you are utilizing, which you mention is v2.8.0.
Now according to what I've read, since game-card.js does not have a 'proveImAlive' action, it will try to bubble up the hierarchy i.e. the controller for the particular route.
This isn't quite what happens because components are isolated, so there is no implicit bubbling. When the Guides say "actions sent from components first go to the template's controller" and "it will bubble to the template's route, and then up the route hierarchy" they mean that you have to explicitly send an action up from the Component. If the component is nested inside another component, you have to do this for each layer, until you reach the Controller.
Why is this error happening?
You need to bind the action in the template: {{game-card proveImAlive="proveImAlive"}}
i'd like to send the id value (to be implemented) of that card up to the controller so it can store it on an array.
I am going to be using closure actions for this part of the answer. As mentioned by #kumkanillam, they have better ergonomics, and they are the current proposed way to use actions if you consult the Guides.
I have prepared a Twiddle for you.
a) Initialize array in the controller
export default Ember.Controller.extend({
appName: 'Ember Twiddle',
gameCards: null,
init() {
this.set('gameCards', []);
}
}
b) Implement the action that pushed to the array
export default Ember.Controller.extend({
appName: 'Ember Twiddle',
gameCards: null,
init() {
this.set('gameCards', []);
},
actions: {
proveImAlive(cardNo) {
this.get('gameCards').pushObject(cardNo);
console.log('Im aliiiiveeee - cardNo', cardNo);
}
}
});
c) Bind the closure action down
{{game-card proveImAlive=(action 'proveImAlive')}}
d) Trigger the action passing the arguments
<div class="flipper">
<div class="front"></div>
<div class="back">
<button {{action proveImAlive 1}}> ProveIamAlive</button>
</div>
</div>
You need to explicitly set the action handler:
{{component-name fooAction=fooHandler}}
This is required because it helps keep components modular and reusable. Implicit links could result in a component triggering unintended behavior.
Your code should work, only if you have included game-card component into play.hbs. I doubt the controller for the particular route is not play in your case.
Here is the working-twiddle
Instead of bubbling actions, use closure actions. For better understanding you can go through the below links,
https://dockyard.com/blog/2015/10/29/ember-best-practice-stop-bubbling-and-use-closure-actions
http://miguelcamba.com/blog/2016/01/24/ember-closure-actions-in-depth/
https://emberigniter.com/send-action-does-not-fire/

ember.js | How to bind an event of a sub-component to an action of an outer component

unfortunately i am not able to figure out, how to receive an event of a component i use from within a component.
What i mean actually sounds harder than it is, consider the following toy example, with a component my-outer and another component my-inner (a short explanation follows the code, at the end i link to jsbin).
The templates:
<script type='text/x-handlebars' id='components/my-outer'>
<div {{bind-attr class="isRed:red"}}>Buttons should toggle my background color</div>
<button {{action "toggleRed"}}>It works from my-outer</button>
{{my-inner action="toggleRed"}}
</script>
<script type='text/x-handlebars' id='components/my-inner'>
<button {{action "action"}}>It doesn't work from my-inner</button>
</script>
The javascript:
App.MyOuterComponent = Ember.Component.extend({
isRed: false,
actions: {
toggleRed: function() {
this.toggleProperty("isRed");
}
}
});
my-outer contains a short text, with a background-color, which can be toggled from and to red by invoking the toggleRed action. the first button demonstrates that this works in principle.
now i would like to bind the default action of the second component to this same toggleRed action, that's the point of the following line.
{{my-inner action="toggleRed"}}
But on clicking the second button (which is part of my-inner) an error is thrown and the action is not fired.
How do I fix this example?
http://emberjs.jsbin.com/cabasuru/2/edit?html,js,console,output
Thanks so much in advance
(and this is my first question on so, i am happy about any meta-critics)
Since Components work just like views, easiest way is to get the parentView and forward the action. You may have to handle the action in my-inner like following.
App.MyInnerComponent = Ember.Component.extend({
isRed: false,
actions: {
toggleRed: function() {
this.get('parentView').send('toggleRed');
}
}
});
You can see outer component can be accessed as parentView in inner component. Here is the working jsbin link
http://emberjs.jsbin.com/cabasuru/5/edit
My question actually missed the main point. What goes wrong in the example above, is that the action helper in the inner component
<button {{action "action"}}>It doesn't work from my-inner</button>
does not trigger the default action associated with the component. Instead it invokes a new event named action, which is not allowed to bubble (due to the component confinement).
It turns out, there are two ways to solve that:
Properly reroute the event in an actions block on the my-inner component
<button {{action "my-action"}}>...</button>
together with a definition of the my-action action for my-inner:
App.MyInnerComponent = Ember.Component.extend({
actions: {
myaction: function(){
this.sendAction();
}
}
});
This is basically, the idea #CodeJack proposes, with the difference,
that here we rely on the wiring, which is set-up in the template of my-outer.
http://emberjs.jsbin.com/cabasuru/3/edit
As #torazaburo hinted at, setting the target property on the my-inner component to the my-outer component allows the event triggered from the action helper to bypass the component isolation.
{{my-inner target=controller}} in the my-outer template and a <button {{action "toggleRed"}}>...</button> in the my-inner template.

link-to action parameters are not working in ember js

Hi i am very new to ember js. i pass action parameters(id ) on link-to action in template but i did not get the values in my controller.
My Template code as follows:
index.html:
<script type="text/x-handlebars" data-template-name="search">
{{#each model.results}}
// here i pass id value along with action
{{#link-to 'profile' id action="profileinfo"}}
</script>
app.js:
App.SearchController = Ember.ObjectController.extend({
id: '',
actions:{
profileinfo: function(id){
// Here i access id value like this
console.log(id);
var id = this.get('id');})
when i click on the link action goes to Searchcontroller, but i get id value is empty.I follow some solutions in stack overflow but unfortunately i did not get anything. Please provide some solution
I don't get why you're using the {{#link-to}} helper for triggering an action on your controller. Maybe you could simply use the {{action}} helper ?
If you try doing it that way, would it work ?
<button type="button" {{action "profileinfo" id}}>Click me !</button>
From there, your console.log(id); should get your value.
EDIT
Would also work for a <a> tag
<a href="#" {{action "profileinfo" id}}>Click me !</a>
I've created popular addon for doing just that:
{{#link-to 'profile' id invokeAction='profileinfo'}}
Simply install it:
ember installember-link-action
Please leave a star if you enjoy it or leave feedback if you feel anything is missing. :) It works with Ember 1.13 and 2.X (tested on Travis CI).

Ember View not finding the controller/action

I'm trying to call my controller's action from my view with Ember, but it says:
Uncaught TypeError: Cannot call method 'send' of null
I just can't find the right way to work with views in ember.
My view layout has a call like:
<button type="button" {{action modalConfirmation target="view"}} class="btn btn-primary">Save changes</button>
And my View class tries to call the controller in this fashion:
this.get('controller').modalConfirmation();
My Controller has something like this:
ProjEmber.BananasIndexController = Ember.ArrayController.extend({
actions: {
showModal: function() {
modalinaView.title = "My Title";
modalinaView.templateName = "any_template_you_wish";
modalinaView.append();
},
modalConfirmation: function() {
console.debug('Action modalConfirmation');
}
}
});
OBS: it works if I append my view using the helper like this:
{{#view ProjEmber.ModalinaView title='A title'}}
A not so good application of a modal view. Just for the sake of illustration.
{{/view}}
You can see the full source on Github, especifically this part of the commit:
https://github.com/lucaspottersky/ember-lab/commit/4862426b39adc0bbcce0b4cc3fd0099439f8dd55#commitcomment-4421854
There is a good likelihood it's failing to be appended within the body, or the scope of your ember app which would be why the events aren't propagating to your actions hash.
You might try appendTo('body')
You shouldn't access view like this
var modalinaView = this.container.lookup('view:modalina');
This PR can give you more insights.
You are doing the same as Stefanpenner has done in this commit.
And this is Wycats reply.
Alternatively, This answer may help you in instantiating modals

Ember Nested Routing

In my application.hbs want to:
{{#linkTo 'pieces/newPiece' }}New Piece{{/linkTo}}
So I set up the router:
App.Router.map(function() {
this.resource('pieces', function(){
this.route('newPiece');
});
});
Yet I get:
The route pieces/newPiece was not found
Any direction appreciated
UPDATE
I changed:
{{#linkTo 'pieces/newPiece' }}New Piece{{/linkTo}}
to
{{#linkTo 'pieces.newPiece' }}New Piece{{/linkTo}},
and that took care of the error, but what I want is to call the 'newPiece' function of the piecesController.
You want to use the {{action}} helper for that. From the guide:
You may want to trigger high level events in response to a simple user
event (like a click).
In general, these events will manipulate some property on the
controller, which will change the current template via bindings.
In your case, assuming that pieces is the current controller in context you would use:
<a href='#' {{action newPiece}}>New Piece</a>
JSBin example