How to send actions to parent controllers? - ember.js

I have an action that may be triggered from different routes/templates, but ultimately should be sent to an action in the application controller. How do I do this? I've only seen examples of the needs property being used for sending actions to child controllers.
So how can actions sent from /posts/post and / (application) both be sent to the application controller?

You usually define the action handler in the ApplicationRoute as:
App.ApplicationRoute = Em.Route.extend({
actions: {
print: function() {
console.log('hello');
}
}
});
Then, if your action is not defined either on your controller or specific route, the action will bubble up to any parent routes until the ApplicationRoute.
If you want to handle the action in your route and at the application level, you must return true in your action handler in order the action can bubble up.
App.IndexRoute = Em.Route.extend({
actions: {
print: function() {
console.log('hello');
return true;
}
}
});
Check the guide for a detailed description.

Related

How to get next route after the current route deactivate.?

I have some code that uses route 'deactivate' method as follows.
deactivate: function() {
this._super();
//some code here
}
Is there a way to identify where is user trying to go(route of URL) after this route deactivates?
I would encourage you to use willTransition actions method hook for this purpose since that will be called before deactivate hook method.
transition.targetName will provide the target route.
actions: {
willTransition(transition) {
this._super(...arguments);
console.log('willTransition user ', transition.targetName);
}
}

ember.js - call base action from overriden action

I want to call a base action from an overriden action like in this jsbin.
I know if I return true then it will propagate to the action, the problem is that I want the action to execute and then do something in the overriden action. Is this possible?
As #fanta noted you aren't exactly overriding the action because you'd have to extend from your parent route, but aside from that:
If you return true from the action handler the action will bubble up to any parent routes. Simple as that you can specify to call the parent's route action handler or just let the action die in your handler
App.IndexRoute = Ember.Route.extend({
actions:{
doSomething: function(){
//do things
if (something) {
//I got this, no need for my parent
return true;
} else if (otherThing) {
//I can't do this, let my parent route handle it
return false;
}
return defaultOption;
}
}
});
I can't think of a scenario in which using action bubbling you couldn't (implicitly) call your parent's route action handler. Do you?
EDIT
If you were to wait for a promise in the remove method of the object you want to remove you can use something like this:
App.IndexRoute = Ember.Route.extend({
actions:{
removeConfirmed: function(){
// do some handling
console.log("I may transitionTo");
self.transitionToRoute('otherRoute', 'someParam');
}
}
});
App.IndexController = Ember.Controller.extend({
actions:{
remove: function(){
var self = this;
this.get('content').remove().then(function() {
// you could transition from the controller
self.transitionToRoute('otherRoute', 'someParam');
// or if you need some handling in the route
self.send('removeConfirmed');
});
// you could let the action remove action bubble up
return true;
}
}
});
If there's no need to wait for a promise then I don't see why returning true and letting the action bubble wouldn't work, since the action in the route would be called.
The important thing to note is that the controller can send actions to itself. If they are not handled by controller itself the action will bubble up to its route and any subsequent parent routes
I hope this helps you!

Ember: Get route instance from the controller

I have a multi-step flow that the user can go through sequentially or jump straight to a section (if the sections in between are completed). I think this logic should be in the Route object. However, from within the controller, how do I access the route instance. For example, it would be ideal to be able to do something like this in the controller:
App.Flow = Em.ObjectController.extend({
submit: function(){
// Validation and XHR requests
// ...
// Go to the next step
route.goToNextStep();
}
}
From within a controller, you can access the router via this.get('target'). So this.get('target').send('goToNextStep') should work.
Like so:
App.Flow = Em.ObjectController.extend({
submit: function(){
// ...
this.get('target').send('gotoNextStep');
}
}
App.FlowRoute = Ember.Route.extend({
events: {
gotoNextStep: function(){
// ...
this.transitionTo(routeName);
}
}
}
You need to get the route for such conditions,
so from the controller just say,
App.Flow = Em.ObjectController.extend({
submit: function(){
var self =this;
// Validation and XHR requests
// ...
// Go to the next step
self.send('goToNextStep');
}
}
and define your goToNextStep event in your route's event hash
'this' is what points to the router, but you shouldn't add any methods to that prototype. Instead, make some sort of event that triggers the transition to the next step.
In addition to target, another way to do this in Ember now is with getOwner.
For example, to send an action to your application route:
import Component from '#ember/component';
import { action } from '#ember/object'; // https://github.com/pzuraq/ember-decorators-polyfill
import { getOwner } from '#ember/application';
export default class MyTopLevelComponent extends Component {
#action
closeModal() {
getOwner(this).lookup('route:application').send('closeModal');
}
});

How do I make my items in my ArrayController listen to an event from a third party API?

I want my items in my ArrayController to listen to
FB.Event.subscribe('edge.create', function(response){
Ember.Instrumentation.instrument("facebook.like", response);
})
I'm making use of the a seperate itemController.
Like2win.ContestsController = Ember.ArrayController.extend({
itemController: "contest",
});
Like2win.ContestController = Ember.ObjectController.extend({
init: function() {
this._super();
instance = this;
Ember.subscribe("facebook.like", {
before: function(name, timestamp, payload) {
instance.send('onLike', payload);
},
after: function(name, timestamp, payload) {
//
}
})
},
For some reason only the last item in my array ends up listening to the event. I'm just starting out with Emberjs so I expect the answer to be simple.
Ember.Instrumentation is a simple software instrumentation api. It's purpose is performance profiling, tracing, not application level event dispatching.
You can see this api in action by setting Ember.STRUCTURED_PROFILE to true. This will start logging the render times for all templates rendered to the DOM by ember.
The specific issue you are having deals with how the Ember runloop works. The after hooks are only fired once with the last context given. This is done to ensure that multiple property changes of the same property do not result in re-rendering the DOM that many times. So the last property change on the runloop wins and the DOM updates with that property value.
What you really need to do is just translate the FB.Event of type edge.create into an application event that your app understands, something like `facebookLike', similar to what you have done above.
I would do this in the enter hook of your ContestRoute. Further exiting from the ContestRoute should probably unsubscribe from this event. So you probably need an unsubscribe in the exit hook.
enter: function() {
var self = this;
FB.Event.subscribe('edge.create', function(response) {
self.get('controller').send('facebookLike', response);
});
}
exit: function() {
// unsubscribe from edge.create events here
}
Then you can handle this event in your ContestController like so,
facebookLike: function(response) {
}

Ember js save data before window unload

I want to save user progress, before user leaves a page. What is the best way to do this in Ember.js (v 1.0.0-pre.4)?
In pure JQuery it will look like:
$(window).unload(function() {
ajaxSaveUserProgress();
return true;
});
In Ember I am trying to do something like this:
Exam.TestView = Ember.View.extend({
unload: function(event){
controller.ajaxSaveUserProgress(); // call controller method
console.log('UNLOADED'+get(this, 'controller.test'));
}
});
Personally I'd put this code in the ApplicationRoute, as I believe the ApplicationRoute's setupController is only executed the once when the application is first initialised. You'll have to double-check this, but that's my understanding of it.
I've commented out the code you'll want because I've also demonstrated how the AJAX request needs to be set to synchronous, otherwise the window will close and your AJAX request won't have finished. We naturally need to wait for it to finish before the window is closed.
App.ApplicationRoute = Ember.Route.extend({
setupController: function() {
// var controller = this.controllerFor('foo');
// controller.ajaxSaveUserProgress();
jQuery(window).on('unload', function() {
jQuery.ajax({ type: 'post', async: false, url: 'foo/bar.json' });
});
}
});
Please ignore my jQuery instead of $ (Personal preference!)
Ember's got a standard way of handling this now. From the docs:
App.FormRoute = Ember.Route.extend({
actions: {
willTransition: function(transition) {
if (this.controller.get('userHasEnteredData') &&
!confirm("Are you sure you want to abandon progress?")) {
transition.abort();
} else {
// Bubble the `willTransition` action so that
// parent routes can decide whether or not to abort.
return true;
}
}
}
});