How to get reference to current route from controller? - ember.js

I have a controller (KbRelatedGroupController) that is loaded via a {{render}} helper in a template.
In the controller's action, if I do this.get('target'), it returns a reference to the parent controller for the page (KbShowController).
If I call .target on that, I get a reference to Discourse.Router, which is no good to me.
What I want is a reference to a KbShowRoute, and that is what I expected since .target is supposed to produce the Route when called from a controller is it not?
Really confused here. Why is it so hard to get a reference to the current route from a controller?

The way I see it, you're not supposed to. You can let the action bubble up to the route:
App.KbShowRoute = Ember.Route.extend({
...
actions: {
something: function() {
console.log('called second');
}
}
});
App.KbShowController = Ember.Controller.extend({
...
actions: {
something: function() {
console.log('called first');
}
}
});
see docs
You could:
Handle one part of the action in the controller, and let it bubble to the route by not returning anything in the controller's action handler
Let the route handle the action (by not adding the action to your controller's action hash) and from the route use this.controllerFor(this.routeName).sendAction('..', ...) to call a different action (or part of the action) in the controller.
I hope this helps you!

Related

Emberjs: How to get to a route parameter passed in via url in controller

Consider routes:
this.resource('things', {path: '/things/:id'}, function () {
this.route('edit');
});
Inside controller of the edit, how do I access the :id?
You can use the paramsFor method in the route to get parent route parameters.
So in your case you can also use the setupController hook of the edit route,
setupController(controller,model){
this._super(...arguments);
let { id } = this.paramsFor(this.routeName);
controller.set('thingsId',id);
}

Binding component actions to route and getting the returned result

Say I have the following component
{{my-component onSaved=(action 'save')}}
I want to define the "save" action on the route not the controller.
actions: {
save(model): {
return model.save();
}
}
The returned promise is important as I need it in the component code.
const pendingPromise = this.attrs.onSave(model);
This does not work and says it can't find the action. Is this possible somehow? Even if I provide an action in the controller to simply call the one in the route there is still no way to get hold of the returned promise.
actions: {
saveIntermediary(model) {
this.send('save', model); // can't get hold of the response!!!!
}
}
{{my-component onSave=(action 'saveIntermediary')}}
The route isn't connected to the template, that's why you can't normally bind a closure action from a route. There's two solutions at the moment:
Move the action to the controller. The controller is the context of the rendered template, so they're connected.
Use the route action addon and keep the action in the route.

Parent Route Not Asked to Handle Event?

The guide says that when an action is triggered, Ember first looks for a handler in the current controller, then if it can't find it in the controller it looks in the current route, then the parent route, etc. I'm not seeing that happen.
My routes:
App.Router.map(function() {
// Creates 'products' and 'products.index' routes
this.resource('products', function(){
// ...
});
});
My super trivial products.index template;
<span {{action fooBar}}>Run fooBar</span>
To test this, I'm currently at /#/products in the browser, and Ember logs "Transitioned into 'products.index'" saying I'm currently in the products.index route, as I expect. Now if click on the action, Ember should look for a handler in:
ProductsIndexController
ProductsIndexRoute
ProductsRoute
My observations:
If I put the handler in ProductsIndexController, it works.
If I put the handler in ProductsIndexRoute, it works.
However, if I put the handler in ProductsRoute, it's never called:
.
App.ProductsRoute = Ember.Route.extend({
events: {
fooBar: function(){
alert("alarm!");
}
}
});
Instead I see the error:
*Error: Nothing handled the event 'fooBar'.*
What am I missing?
One of my other javascript files was also setting/creating App.ProductsRoute (and doing nothing with it), which was causing a conflict. Silly mistake.

propertyBinding Controller - view

So, I am trying to get a simple propertyBinding to work with emberjs. Specifically, I have a controller with a content property, that gets updated under certain circumstances and a view, which needs that content array to draw some chart.
I have made the most basic example and it doesn't seem to work. My simple example is the following:
Appname.IndexController = Ember.Controller.extend({
value: 'bla'
});
Appname.IndexView = Ember.View.extend({
templateName: 'Index',
propertyBinding: 'Appname.IndexController.value',
didInsertElement: function() {
console.log('Indexview');
console.log(this.get('property'));
}
});
It is as simple as that, and it just does not work. What is really odd though, if I create another testcontroller (rather then extending it) e.g.
Appname.TestController = Ember.Controller.create({
value: 'jpopo'
});
the property binding works all of the sudden. But I just can not get it to work with the IndexController
(And in case the information is necessary, in the Applicaton.hbs I have an outlet)
Thanks for any help
Bindings work for instantiated objects, not for object definitions.
Appname.IndexController is the controller definition, not an instance. It is not what you want to bind to. The Ember.js app will create an instance of IndexController, and it's that created instance that you want to bind to:
To access the actual controller instance from its view, use controller.
Appname.IndexView = Ember.View.extend({
templateName: 'index',
propertyBinding: 'controller.value',
didInsertElement: function() {
console.log(this.get('property'));
}
});
Of course, that is if you follow Ember.js conventions.

Can `insertNewline` invoke a transitionTo?

Sample code for my question is here.
It's a simple Ember app that displays the SearchView containing a TextField by default.
When the user enters some text and hits Enter, I want to transition to another state (displayUserProfile) passing the value entered in the textbox.
At first, in the Textbox's insertNewline callback, I called the transitionTo method of the application's router, passing the value as part of the parameter object:
App.SearchTextFieldView = Em.TextField.extend({
insertNewline: function() {
App.router.transitionTo('displayUserProfile', {
username: this.get('value')
});
}
});
That works fine, but then I noticed that pangratz's answer on a question about infinite scrolling, uses a different approach. Instead he invokes a method on the view's controller, which in turn calls a method on the controller's target (which is the router).
This changes my code to:
App.SearchTextFieldView = Em.TextField.extend({
insertNewline: function() {
Em.tryInvoke(this.get('controller'), 'displayUserProfile', this.get('value').w());
}
});
App.SearchController = Em.Object.extend({
displayUserProfile: function(username) {
this.get('target').transitionTo('displayUserProfile', {
username: username
});
}
});
My question is: which approach is better?
Calling transitionTo directly from the view or delegating it to the view's controller?
I would recommend a different approach. insertNewLine should trigger an action that is handled by the router, which will then transition its state.
App.SearchTextFieldView = Em.TextField.extend({
insertNewline: function() {
this.get('controller.target').send('showUser', {username: this.get('value')});
}
});
App.Router = Ember.Router.extend({
...
foo: Ember.Router.extend({
showUser: function(router, evt) {
router.transitionTo('displayUserProfile', evt);
});
}
});
You should put the showUser handler at the top-most route where it is valid in your app.
This approach follows the general pattern of events in Ember apps that views handle DOM-level events and where appropriate, turn them into semantic actions that are handled by the router.
Personally I think the second approach is better.
The first thing is that it's a bad idea to access the router statically. Then for me, you have to keep the views logic-less, so delegating to controller seems a good choice.
In your case this is only a call to the router, but you can imagine processing some algorithms on the textfield value. If you do this proccessing in you view, this will lead to a view, mixing UI code, and logic code. View should handle only UI code.