I am trying to access one of two models in a controller that uses needs on a sibling controller. My router looks like the following:
App.Router.map(function() {
this.route('login');
this.route('mlb.lineups', {path: 'tools/mlb/lineups'})
this.resource('mlb.lineups.site', { path: 'tools/mlb/lineups/site/:site_id' });
});
The mlb.lineups route definition looks like the following:
App.MlbLineupsRoute = Ember.Route.extend({
model: function() {
var self = this;
return Ember.RSVP.hash({
sites: self.store.find('site')
})
},
setupController: function(controller, models) {
controller.set('model', models.get('sites'));
},
afterModel: function(models) {
var site = models.sites.get('firstObject');
this.transitionTo('mlb.lineups.site', site);
}
});
The reason I am using Ember.RSVP.hash({}) here is I plan on adding another model to be retrieved after I retrieve the site model.
Now in my MlbLineupsSiteController I am trying to access the sites model with the following:
App.MlbLineupsSiteController = Ember.ArrayController.extend({
needs: "mlb.lineups",
sites: Ember.computed.alias("controllers.models.sites")
});
This is the error I'm getting in my Ember console: needs must not specify dependencies with periods in their names (mlb.lineups)
What's the best way to make the sites model from the MlbLineups controller available in my MlbLineupsSiteController?
Note:
#NicholasJohn16's answer isn't valid anymore. It always gives an error that controller couldn't be found. Generally you should also never use needs property and always use Ember.inject.controller if you have to make your controllers dependent on each other. I'd also recommend using services instead of dependencies between controllers. It's easier to maintain code which contains communication between controllers through services, than controller directly accessing other controller's properties. You might not always be aware of such access, and using services gives you another layer of security.
Solution:
Tested in Ember.js 1.10.0-beta.4. Use following code in Controller to reference nested controller in needs:
needs: ['classic/about']
Then you can access it later using:
const aboutController = this.get('controllers.classic/about');
const aboutProperty = aboutController.get('customProperty');
Works as expected. Basically you need to replace dots with slashes.
It should be:
needs:" MlbLineupsSite "
Basically, the name of the controller you want to include, minus the word controller.
Everything else you posted should work.
Related
I need to load contents coming from my API when the application boots, then inject what I get into all routes and controllers to be able to access them whenever I want.
I was wondering where is the best place to do that in Ember?
In an initializer? I've heard that it's not a good practise to use the store from there...
In the application route? Then how can I access it from all routes and controllers? Using this.modelFor('application')? Is that a good practise?
Thanks.
The best place would be in the ApplicationRoute, you can do this in the beforeModel or afterModel/setupController hook as you like. Here's a beforeModel example:
ApplicationController = Ember.ObjectController.extend({
beforeModel:function() {
var self = this;
var rsvp = Ember.RSVP.hash({
fruits: self.store.find('fruit'),
candies: self.store.find('candy'),
meats: self.store.find('meats')
}};
rsvp.then(function(models) {
self.controllerFor('fruits').set('model',models.fruits);
self.controllerFor('candies').set('model',models.candies);
self.controllerFor('meats').set('model',models.meats);
});
}
});
The rsvp fetches all of the models together and waits for them to precede before continuing which happens on the then. We then assign all the found models to the model property on their matching controller.
To do this in the afterModel hook, it would look different.
I wanted to understand if the needs attribute could be used to inject arbitrary objects into controllers, routes and views.
I am developing an Ember.js application where I am writing a custom data-service layer that communicates with the backend to load and persist data. I define Ember Objects which represent the various backend services such as:
App.SessionServiceClient = Em.Object.extend({
// methods and attributes
});
App.UserServiceClient = Em.Object.extend({
// methods and attributes
});
I now register these objects with the application's container to make them available for DI:
App.register('service:session', App.SessionServiceClient, {singleton: false});
App.register('service:user', App.UserServiceClient, {singleton: false});
Now that the objects are available for injection, and if I have a controller that needs only the SessionServiceClient, can I do the following:
App.SignInController = Em.ObjectController.extend({
needs: ['service:user'], // using the needs to declare dependency
actions: {
// actions for the view
}
});
When i tried this, it did not work. Is this possible with Ember.js or am I doing it wrong?
Best practice is to use initializers to inject your service into the controllers you need it in. SEe http://ember.zone/ember-application-initializers/
Ember.Application.initializer({
name: "sessionLoader",
after: "store",
initialize: function(container, application) {
container.register('service:session', App.SessionServiceClient, {singleton: false});
container.injection('route', 'session', 'service:session');
container.injection('controller', 'session', 'service:session');
});
}
});
Also you should really try to switch to Ember-CLI or at least use an ES6 module structure. (i.e. use imports instead of globals.)
I am trying to work with Ember.js
Can I expose my data model as JSON through a route or controller?
I have an object like this saved in the store:
this.store.createRecord('Person', {
id: 1,
name: this.get('name'),
email: this.get('email')
});
I want to expose this data from a route or controller as JSON object. I don't want to use any view.
Is it possible to do this?
Thanks for help!
EDIT
My route is:
App.ResultRoute = Ember.Route.extend({
model: function() {
return this.store.find('person', 1);
}
});
There is '1' because I want only this record.
In this way It works and I see in the view the {{name}} and the {{email} of the Person object.
I want to see only the JSON, I tried to do how you suggest me :
App.ResultRoute = Ember.Route.extend({
afterModel: function (model) {
model.get('content').forEach(function (item) {
console.log(item.get('content'));
});
}
});
But I receive this error:
Uncaught Error: Assertion Failed: Error: More context objects were passed than there are dynamic segments for the route: error
What is my error?
The way I would do this would be, I would have an api in my model which would return a plain json object to whoever asked it. So the Person model would have a getPersonDetails method which will hide all the internal details, including the attributes and associations and whatever else, and return the state of the person object it is invoked upon.
So, for example, if you wanted to display a table of persons or something, you would do a createRecord, and just ask the newly created person object for it's details.
Start from the beginning of this guide. http://emberjs.com/guides/routing/specifying-a-routes-model/ It will show you how to specify a model for a route.
Then, read this entire guide on controllers: http://emberjs.com/guides/controllers/
In general, you would access that data from the route's model hook with:
this.store.find('person') // All records
If you wanted to access that first object as JSON, you could do:
var person_JSON = this.store.find('person').then(function (persons) {
//The persons records are now available so you can do whatever you want with them
console.log(persons.objectAt(0).get('content'));
});
You could also iterate over all records and strip out the content to produce raw json without the Ember wrapping... Just depends on what you need to really do.
Really the best place to put this would be the route's afterModel hook, though. You wouldn't be working with a promise, as Ember would have dealt with that for you:
afterModel: function (model) {
model.get('content').forEach(function (item) {
console.log(item.get('content'));
});
}
Hope that helps.
Edit: Since you have one record try this:
afterModel: function (model) {
console.log(model.get('content'));
}
I have a route that has two models associated with it as shown below:
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
sites: this.store.find('site'),
songs: this.store.find('song')
})
},
Now later on, I need to be able to retrieve the first object in the sites model in order to do a transition I'll show below. I figured I can set the models using setupController, but when dealing with multiple models as depicated above, I'm not sure how to fill this part in:
setupController: function(controller, ???) {
controller.set('model1', ???);
controller.set('model2', ???);
}
And finally, I'd like to be able to retrieve the first object in model1 (it's multiple instances of site as described above)
afterModel: function() {
firstRecord = this.('sites').objectAt(0);
this.transitionTo('site', firstRecord.id);
}
It's also possible that I'm not designing my application properly. sites in this case is a component I built that displays different sites within a few different controllers. The controllers are dependent on this component in that they need to know which site is selected in order to do their own thing. So in controllers that need access to the component, I do something like:
{{site-nav sites=sites}}
Where site-nav is my component. It needs its own model, as does the controller itself.
First, you're going to need to modify your model hook slightly, to make sure you stay in the right scope:
model:function(){
var self = this;
return Ember.RSVP.hash({
sites: self.store.find('site'),
songs: self.store.find('song')
})
}
To get the different models in setupController, you just access it from the second parameters, like this:
setupController:function(controller,models) {
controller.set('sites',models.sites);
controller.set('songs',models.songs);
}
afterModel provides two parameters, this first being the resolved model for your route, so you'd do it something like this:
afterModel:function(models){
var site = models.sites.get('firstObject');
this.transitionTo('site',site);
}
I'm working with Ember.Router, and one thing I can't figure out is how to bind objects to controllers that the Router is instantiating.
For instance, here is a controller class (extended) that the Router will instantiate for a specific route ('page'), as well as a controller object (created), say to handle user administration tasks on a part of the application outside of the Router:
// controller used by Router to render the "page" route
App.PageController = Em.ObjectController.extend({
content: Em.Object.extend({
foo: 'bar'
})
});
// global controller for users
App.usersController = Em.ObjectController.extend({
content: Em.Object.extend({
fooBinding: App.PageController.foo
// the above will not work since Em.Router
// instantiates the page controller dynamically
})
});
So when the router loads it will instantiate App.PageController into App.router.pageController, but that's after App.usersController is already created. So how can App.usersController access data in a controller that the Router is managing?
Any ideas?
There are a few mistakes in your sample.
First, you should never directly setup a property with an Object value at declaration time: this value would be shared across all instances of the class. Here, it does not really matter, but it's a bad practice. In this case, the good way of setting up the PageController content is to bind it in router, at connectOutlet call, like that:
connectOutlets: function (router) {
var theContainerController = router.get('theContainerController'),
objectWithFooBar = Ember.Object.create({
foo: 'bar'
});
theContainerController.connectOutlet('page', objectWithFooBar);
}
Second mistake is the naming of usersController: it should be UsersController, as it is a class, which will be injected in the router as usersController during initialize call. It seems also quite strange to have users pluralized & ObjectController. Certainly should be singularized...
Last, and certainly what will be the most interesting regarding the question, once you will have preceding remarks applied, you will be able to setup the binding using:
App.UserController = Em.ObjectController.extend({
fooBinding: 'App.router.pageController.foo'
});
App.router can be setup before your call to App.initialize. It is definitively a bad coupling to have UserController using a global symbol to directly access to PageController, but it does the job in your case.
A definitely yet better solution would also be to bind UserController's content in a connectOutlet call.