How can I get an ApplicationController inside my HomeController?
HomeController=Ember.Controller.Extend({
init:function {
// Here I want to get My application controller. Is there any way to get??
}
})
How can I get an ApplicationController inside my HomeController?
To specify dependencies (or needs) between controllers, use the needs property:
HomeController=Ember.Controller.Extend({
needs: ['application']
})
Now Ember will make the application controller accessible from inside your HomeController as controllers.application. You can use it like any other HomeController, it is even accessible from your templates:
<!-- inside `home` template -->
{{controllers.application}}
See http://darthdeus.github.com/blog/2013/01/27/controllers-needs-explained/ for more detail on controller-needs.
BTW: It's pretty rare for an ember controller to use a custom init fx. Consider moving any initialization logic to setupController hook on the route instead.
App.HomeController = Ember.Controller.extend({
init: function () {
var applicationController = this.controllerFor('application');
}
});
Related
I am working with Ember Octane version, I want to invoke an action in the Route from the child component. The pseudo code is as follows.
**Route**
export default class SomeRouter extends Route {
model() {
return data;
}
#action
refreshRoute() {
this.refresh();
}
}
**SomerRouter.hbs**
<ChildComponent> //Using child component here
**ChildComponent**
export default class ChildComponent extends Component {
#action
revert() {
//How do I invoke the "refreshRoute" on the SomeRouter from here?
}
}
In the revert method of the above child component, "this" refers to the component itself but in the previous version of the ember "this" refers to the router where I could simply call this.refresh(). So how do I achieve this in Ember Octane. Would really appreciate any help.
You dont. This is actually one of the things that are still a bit inconsistent even with octane. Because the bound context of the route template is the Controller, not the route. So you can not access the action with {{this.refreshRoute}}.
To call an action on the Route your best way is to uzilize send. But to do this you need a Controller and define a different action on the Controller:
controllers/some.js:
export default class SomeController extends Controller {
#action
refreshRouteFromController() {
this.send('refreshRoute');
}
}
Now this function you can use from your template:
<ChildComponent #refresh={{this.refreshRouteFromController}}>
And then use it from your component:
revert() {
this.args.refresh();
}
Or directly from a button:
<button {{on "click #refresh}}>...</button>
I need to generate the url for given route from a helper.
How to generate url for a route in Ember.js pointed me to use the generate function. And yes it works as i need (Checked the functionality by making application route global). But i am not sure how to call it from inside a helper.
You were in a good direction, so you mainly solved this problem. :) There are two type of helper in Ember a simple function helper and the Class Based Helpers. We will use a Class Based Helper in this case.
As you have seen in your linked example, we need access to the main Router. We can do this with Ember.getOwner(this).lookup('router:main'). (Ember.getOwner() exists from v2.3, before v2.3 use this.container.lookup('router:main'))
For example, you have this map in your router.js:
Router.map(function() {
this.route('about');
this.route('posts', function() {
this.route('post', {path: '/:post_id'});
});
});
And if you create a helper for example with the name of url-for your template could contain these lines:
{{url-for 'about'}}
{{url-for 'posts'}}
{{url-for 'posts.post' 2}}
And your Class Based Helper could be the following:
// app/helpers/url-for.js
import Ember from 'ember';
export default Ember.Helper.extend({
router: Ember.computed(function() {
return Ember.getOwner(this).lookup('router:main');
}),
compute([routeName, ...routeParams]) {
let router = this.get('router');
return Ember.isEmpty(routeParams) ?
router.generate(routeName) : router.generate(routeName, routeParams[0]);
}
});
Demo on Ember Twiddle
In my Ember application, I wanted to have a controller wrapping a collection of models, that I could inject into other controllers.
I've set it up like this:
app/controllers/zones.js:
export default Ember.Controller.extend({
model: function () {
return this.store.find('zone');
}
});
app/controllers/zones/index.js:
export default Ember.Controller.extend({
needs: ['zones'],
zones: Ember.computed.alias('controllers.zones.model')
});
This seems like it ought to work, but unfortunately, it doesn't. I get this error in my JavaScript console (in the browser):
Error: Assertion Failed: The value that #each loops over must be an Array. You passed function () {
"use strict";
return this.store.find('zone');
}
I've tried moving stuff around, or using ArrayController rather than just Controller, but I still get this error.
This makes very little sense to me, any ideas?
Here is the thing, model is the function need to resolve the model for route not controller. That model then automatically injected to controllers model property.
Ember way
In ember way I would suggest move this model definition to the route for controller. Something like this.
export default Ember.Route.extend({
model: function (param) {
return this.store.find('zone');
}
});
This is the Ember way of doing thing. Resolve model in route then have controller to filter / decorate it.
I would also suggest using ArrayController instead of Controller since you are handling number of models.
The other way
Again if you want to have model resolved in controller. I warn you its not the Ember way but you can do it something like this -
export default Ember.ArrayController.extend({
//dont override the model property
mydata: function () {
return this.store.find('zone');
}.property('model'),
});
I figured out the problem – I just needed to set my overridden model implementation to be a property, like this:
app/controllers/zones.js (injected controller):
export default Ember.Controller.extend({
model: function () {
return this.store.find('zone');
}.property() // `.property()` turns the function into an iterable object for use in templates and the like.
});
The main controller is still the same.
app/controllers/zones/index.js (active route controller):
export default Ember.Controller.extend({
needs: ['zones'],
zones: Ember.computed.alias('controllers.zones.model')
});
Here is possibly an edge case for how ember adds the 'active' class on a link to helper.
I have my current router set up like so:
import Ember from 'ember';
var Router = Ember.Router.extend({
location: PortalDevENV.locationType
});
Router.map(function() {
this.resource('portal', function() {
this.route('admin');
this.resource('placements', function() {
this.route('import-debtors');
this.resource('add-debtor', function() {
this.route('debtor-form');
});
this.route('view-debtors');
});
this.resource('debtor', {path: 'placements/view-debtors/debtor/:debtor_id'}, function() {
this.route('agent-notes');
this.route('transactions');
});
});
});
export default Router;
notice how I have a resource called "debtor" that- while it is being rendering into the portal template- i still need it to appear (in terms of the URL) to be a child of the "view-debtors" route... which, in reality, is nested deeper within a separate set of templates.
This structure seems to be working fine, but it is breaking my breadcrumb-style navigation.
When moving into the "debtor" page.. i still want "view-debtors" {{link-to}} helper to get the 'active' class from ember... along with the {{link-to}}'s that lead up to the "view-debtors".
Is this possible to do by calling some functions in my routes... or some other way?
It doesn't seem to be a common ember convention... but then again perhaps Ember actually does work in this way and I did something else that broke it? Take a look and see if my set up is correct.
You should be able to bind the active class to a computed property. Assuming the {{link-to}} you are referring to is in your application.hbs template, you could do something like this:
// templates/applictaion.hbs
{{#link-to "view-debtors" class="isDebtorsRoute:active"}}View Debtors{{/link-to}}
// controllers/application.js
export default Ember.Controller.extend({
isDebtorsRoute: function() {
// use this.get('currentRouteName') or this.get('currentPath')
}.property('currentPath')
})
EDIT: Here is a jsbin example http://emberjs.jsbin.com/wuhor/1/edit?html,css,js,output
Uncaught Error: Assertion Failed: `<(subclass of Ember.ObjectController):ember947> specifies `needs`, but does not have a container. Please ensure this controller was instantiated with a container.
If for some reason a controller doesn't have a container, how can I provide it with one? Context is below, but that is essentially the question being asked.
The context is that there apparently is not a straightforward way of providing controllers for the individual items in Ember.CollectionView, a problem which is outlined at ember.js/issues/4137.
It seems the only way to get item controllers is to declare the them inline in the init method for an inline itemViewClass declaration of the CollectionView (as confirmed by the originator of that ticket):
var someCollectionView = Ember.CollectionView.extend({
itemViewClass: Ember.ListItemView.extend({
templateName: "foo-item",
init: function(){
var content = this.get('content');
var controller = Ember.ObjectController.extend({
// controller for individual items in the collection
actions: {
// actions specific to those items
}
}
}).create({
content: content,
});
this.set('controller', controller);
this._super();
}
})
});
So this works, however if you add a "needs" property to this controller, it gives the error about no container. These item controllers will be observing a property on an external controller, so I need the "needs". So how do I instantiate the controller with the container... or hack it in after instantiation?
Accessing App.__container__ is generally adviced against. All core objects like views, controllers, routes, should have been instantiated by the container. In that case they will also have a container property (plain JS property, not an Ember property), that you can use to instantiate other objects, which in turn will have access to the container.
So instead of
Ember.ObjectController.create(...)
try
this.container.lookupFactory('controller:object').create(...)
If container is undefined you'll have to go up the chain, and make sure whatever object you're calling this from is also coming out of the container.
It looks like you can do
...
}).create({
content: content,
container: App.__container__
});
this.set('controller', controller);
this._super();
}
})
});