Ember.JS A BEFORE() function for every controller action - ember.js

Is it possible to create some sort of before() function for every action inside a controller?
I know I can do an init() for every action, but what if I have a some code I want to run for every thing? It would be way too redundant to create the same init() function for each action.

No, there is no way to run code before every action without explicitly calling it yourself. At one point there was an idea to lookup actions using get(), which would have allowed this, but it never got implemented. Not sure what your use case is, but if you really do need to call functionality before every action call, just call it yourself. It'll be much more readable than any clever hacks we could come up with.

You could possibly do something like this:
First make sure your controller extends the `Ember.TargetActionSupport' mixin.
Ember.Controller.extend(Ember.TargetActionSupport,{
...
});
This will allow you to trigger actions programmatically. See more about this: Ember.TargetActionSupport
Then you would make an entry point action that contains your common code and a call to trigger the proper action:
Ember.Controller.extend(Ember.TargetActionSupport,{
actions: {
common: function (actionName) {
//Do common logic here
this.set('foo', 'bar');
//And then trigger the intended action
this.triggerAction({
action: actionName,
target: this
});
},
otherAction: function () {
//Do some more logic here
this.set('bar', 'baz');
}
}
});
You would call this action from your template like this:
<button {{action 'common' 'otherAction'}}>Press Me</button>

Related

Closure actions in Ember

I'm rewriting some old Ember code to use closure actions, but I'm new to Ember and I can't seem to figure out how to change a sendAction that hasn't got parameters.
This is the code in the component row.js:
click: function() {
this.sendAction();
}
The row.hbs as well as the parent templates are just
{{yield}}
so I can't add anything there it seems. Does anyone know how to solve this?
If it's just {{yield}} all the way up is it possible that the click didn't actually do anything?
Usually the calling template would have something like {{row action="doSomething"}} Ember Docs
It's also possible there was some behavior here that kept sending the action up when you have a {{yield}} in which case you would want to just keep going up the tree until you found (somewhere) an action="doSomething" declaration. That is where you will start sending the action back down.
e.g.
{{top action="doSomething"}}
{{row}}
{{/top}}
Would become
{{top}}
{{row doSomething=(action "doSomething")}}
{{/top}}
Then row.js
click() {
this.doSomething();
}

EmberJS 2.7 - using a closure action to call a function vs calling that function as an action

In Ember with closure actions you can pass a function, like this:
1
<div class="dropzone text-center" {{action (action openFilePicker)}}>
<!-- this calls a function (no quotes!) -->
</div>
instead of calling an action, like the following.
2
<div class="dropzone text-center" {{action (action 'openFilePicker')}}>
<!-- this calls an action (quotes!) -->
</div>
In (1) the code backing this component looks like this:
openFilePicker: function () {
let child = this.$('div:first-child');
child.removeClass('is-dragging');
child.html('<h2>Upload GPX Route</h2><div class="dropzone-body-text"><h5>Drag and Drop here</h5>- or -<h5>click anywhere here</h5></div>');
filepicker.pick({
container: 'modal',
maxSize: (5 * 1024 * 1024),
services: ['COMPUTER']
});
}
This uses JQuery to remove some elements from the DOM, then calls another function (filepicker.pick) which is in an externally loaded JS file.
In (2) the same code is inside an action:
actions: {
openFilePicker: function () {
let child = this.$('div:first-child');
child.removeClass('is-dragging');
child.html('<h2>Upload GPX Route</h2><div class="dropzone-body-text"><h5>Drag and Drop here</h5>- or -<h5>click anywhere here</h5></div>');
filepicker.pick({
container: 'modal',
maxSize: (5 * 1024 * 1024),
services: ['COMPUTER']
});
}
}
What are the pros and cons of doing this? I understand that (1) is not the recommended way of doing this, but surely (1) makes it easier to access other code in the component (like properties of the component, for example). As usual the Ember documentation doesn't seem to shed any light on this (not that I can find anyway). All I can find is this blog post which makes the point to discourage calling arbitrary functions (otherwise why have actions in the first place?)
So although this question can result in opinions, that's not what I am after. I want to understand what is the SCOPING difference between these two approaches? For example, when you are inside the function INSIDE the action, what is the scope difference from the point of view of the JS runtime? Is 'this' the same thing in both cases, for example?
There is not really a difference. Checkout this twiddle.
The only big difference is the action lookup time. While {{action 'foo'}} requires foo to exist on bound time, this is not a required for ={{action 'foo'}} (a closure action).
Action calls are bound by ember to the current this context. This is basically what the {{action}} helper for closure actions does. It creates a new function that calls the original function with this being the context when the {{action}} helper was evaluated.
If you call another function outside your current function scope this will always be different.
Its important to understand that this is always bound to the function scope.
If you call a function like myfunction() then this inside that function will be null. If you call a function like foo.myfunction() then this inside myfunction will be foo.
Make sure that you understand this completely. In a nutshell, this is no way different in Ember. It's standard Javascript.

call another controller method from an action method in emberjs

I am trying to call a second controller method from an action method.
I am able to call second controller method using
this.controller.get('controllers.secondController').method();
How can i call method of another controller from action method?
There is work around i know of like call the controller of the current action method which in turn calls the second controller method.
this.callFirstControllerMethod();
and then use
this.controller.get('controllers.secondController').method();
But is there a way to call the second controller method directly?
Generally this kind of logic should not be on the controller, a controller should really only have actions that relate to the UI or the display of your model.
I would suggest to add an action in the route, that can access the controllers and invoke any methods you need.
// route.js
actions: {
doSomething: function() {
var controllerA = this.controllerFor('firstController');
var controllerB = this.controllerFor('secondController');
controllerA.doSomething();
controllerB.doSomethingElse();
}
}

Catch All Action Handler

Now, this is really a question that's evolved from this. There is a whole lot more info there, but I guess, it's better to ask a direct specific question. So here it goes:
We typically define actions in a controller like so:
var FooController = Ember.Controller.extend({
actions: {
login: function() {
}
}
});
Is there a way to define a catch all action handler, like so (hypothetically):
actions: {
login: function() {
},
*: function(actionName, paramArray) {
}
}
This would be analogous to embers catch all route which I believe has been implemented, though I haven't tried it.
I need this because my Ember.Component renders a user supplied partial template using the {{partial}} helper. This partial might have {{action}}'s specified in them. These actions don't bubble up to the calling controller or route and are lost inside the component. This fact is mentioned in the docs in para 4.
If a catch all action was possible, my component could implement it and send the action back to the caller using something like this:
actions: {
*: function(actionName, paramArray) {
this.sendAction(actionName, paramArray)
}
}
The functionality around the actions hash on controllers, views and routes is provided by the action handler mixin [1]. As you can see, it injects the send(actionName) method and makes enables an object to receive actions. You can now easily override this function and catch all actions instead of looking for it in the actions hash -- which is what the implementation does.
[1] https://github.com/emberjs/ember.js/blob/v1.3.0/packages/ember-runtime/lib/mixins/action_handler.js#L8

Is there a way to get a callback when Ember.js has finished loading everything?

I am building an Ember.js app and I need to do some additional setup after everything is rendered/loaded.
Is there a way to get such a callback? Thanks!
There are also several functions defined on Views that can be overloaded and which will be called automatically. These include willInsertElement(), didInsertElement(), afterRender(), etc.
In particular I find didInsertElement() a useful time to run code that in a regular object-oriented system would be run in the constructor.
You can use the ready property of Ember.Application.
example from http://awardwinningfjords.com/2011/12/27/emberjs-collections.html:
// Setup a global namespace for our code.
Twitter = Em.Application.create({
// When everything is loaded.
ready: function() {
// Start polling Twitter
setInterval(function() {
Twitter.searchResults.refresh();
}, 2000);
// The default search is empty, let's find some cats.
Twitter.searchResults.set("query", "cats");
// Call the superclass's `ready` method.
this._super();
}
});
LazyBoy's answer is what you want to do, but it will work differently than you think. The phrasing of your question highlights an interesting point about Ember.
In your question you specified that you wanted a callback after the views were rendered. However, for good 'Ember' style, you should use the 'ready' callback which fires after the application is initialized, but before the views are rendered.
The important conceptual point is that after the callback updates the data-model you should then let Ember update the views.
Letting ember update the view is mostly straightforward. There are some edge cases where it's necessary to use 'didFoo' callbacks to avoid state-transition flickers in the view. (E.g., avoid showing "no items found" for 0.2 seconds.)
If that doesn't work for you, you might also investigate the 'onLoad' callback.
You can use jQuery ajax callbacks for this:
$(document).ajaxStart(function(){ console.log("ajax started")})
$(document).ajaxStop(function(){ console.log("ajax stopped")})
This will work for all ajax requests.
I simply put this into the Application Route
actions: {
loading: function(transition, route) {
this.controllerFor('application').set('isLoading', true);
this.router.on('didTransition', this, function(){
this.controllerFor('application').set('isLoading', false);
});
}
}
And then anywhere in my template I can enable and disable loading stuff using {{#if isLoading}} or I can add special jQuery events inside the actual loading action.
Very simple but effective.