EmberJS: retrieving model from route when multiple models are retrieved - javascript

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);
}

Related

Ember not calling setupController of router

So, I have two paths in my route. I created the two routes as the doc recommends.
My router is the following:
// router.js
Router.map(function() {
this.route('photos');
this.route('photo', { path: '/photo/:photo_id' });
});
If I visit firstly the route /photo/ID and then go to /photos, it will only show one object on the latter. (wrong)
If I visit /photos first it shows all the objects and I can go to /photo/ID later on and it will be fine. (right)
I want to make it work both ways. How to do this? You can see my code for each route down below:
// photo.js
export default Ember.Route.extend({
model(params) {
return this.get('store').findRecord('photo', params.photo_id);
}
});
// photos.js
export default Ember.Route.extend({
setupController(controller, model) {
let photos = this.get('store').findAll('photo');
console.log('add all...');
// convert to an array so I can modify it later
photos.then(()=> {
controller.set('photos', photos.toArray());
});
},
});
I can always call the findAll() function regardless where the user goes, but I don't think this is smart.
The way I am dealing with the page transitions:
To go to photos I use:
{{#link-to 'photos'}}All{{/link-to}}
To go to /photo/ID I inject the service '-routing' and I use in one event click like this:
routing: Ember.inject.service('-routing'),
actions() {
selectRow(row) {
this.get("routing").transitionTo('photo', [row.get('id')]);
}
}
findAll will get it from a store and return immediately and later on it will request the server and update the store. but in your case, as you are not using route model hook, so this live array will not be updated so it will not reflect it in the template.
If I visit firstly the route /photo/ID and then go to /photos, it will
only show one object on the latter.
In the above case, store will contain only one reocrd, so when you ask for store data using findAll it will return the existing single record.
Another option is,
avoiding this photos.toArray() - It will break live-array update, I am not sure why do you need it here. since photos is DS.RecordArray.
Note: It's important to note that DS.RecordArray is not a JavaScript
array, it's an object that implements Ember.Enumerable. This is
important because, for example, if you want to retrieve records by
index, the [] notation will not work--you'll have to use
objectAt(index) instead.

Ember.js - Rendering additional data for a model

I have an app model and apps have an id and name.
this.resource("apps", function() {
this.route('show', { path: ':app_id' });
});
I'd like to make the show view show metrics about the app, but the query is pretty intense, so I don't want to include it in the call to the index view (let's say this is a table).
I'm not sure if this is possible with ember-data because the app would already be in the store with the simplified payload and not be re-requested for the show view to get the metrics.
Then my head went to making metrics a completely different model accessible from apps/1/metrics and then making it another model and everything.
But if I sideload the data, i have to provide ID references to the metrics for a particular app. And it's hasOne so there's not really IDs as there would be for a database backed model.
What's the best way to load in additional data about a model or expand the information supplied in the show view?
The backend is Rails and this is an ember-cli project.
The easiest way is to retrieve the data in the Route's afterModel handler:
var ShowRoute = Ember.Route.extend({
model: function(params) {
// Load the model and return it.
// This will only fire if the model isn't passed.
},
afterModel: function(model, transition) {
// Load the rest of the data based on the model and return it.
// This fires every time the route re-loads (wether passed in or via model method).
}
});

Ember route or controller expose data model as json

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'));
}

Ember.js: dependencies between two controllers failing

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.

Ember.js does not auto bind model RESTful changes

Correct me if I'm wrong, but I thought Ember should most of the model - view binding for you?
What would be the case when you have to manually track model changes and update/refresh the view accordingly?
The app I'm working have nested routes and models associated with them.
App.Router.map(function() {
this.resource('exams', {path: "/exams"}, function() {
this.resource('exam', {path: ":exam_id"}, function(){
this.resource('questions', {path: "/questions"}, function() {
this.route("question", {path: ":question_id" });
this.route("new");
})
})
});
});
Everything works fine and I'm able to get exams and questions separately from the rest server.
For each model I have appropriate Ember.ArrayController and Ember.ObjectController to deal with list and single model items in the view. Basically for both models the way I handle things is IDENTICAL except for the fact that one is nested within the other. One more difference is that to display the nested route data I'm using another {{outlet}} - the one that is inside the first template.
Now the problem is that the top level model binding to the views is handled automatically by Ember without any special observers, bindings etc.. - e.g. When I add new item it is saved and the list view is refreshed to reflect the change or when the item is deleted it is auto removed from the view. "It just works (c)"
For second model (question), on the other hand, I'm able to reproduce all the crud behaviour and it works fine, but the UI is not updated automatically to reflect the changes.
For instance I had to something like this when adding new entry (the line in question has a comment):
App.QuestionsController = Ember.ArrayController.extend({
needs: ['exam'],
actions: {
create: function () {
var exam_id = this.get('controllers.exam.id')
var title = this.get('newQuestion');
if (!title.trim()) { return; }
var item = this.store.createRecord('question', {
title: title,
exam_id: exam_id
});
item.save();
this.set('newQuestion', '');
this.get('content').pushObject(item); // <-- this somehow important to update the UI
}
}
});
Whereas it was handled for me for (exam model)
What am I doing wrong? How do I get Ember.js to track and bind model and change the UI for me?
With
this.get('content').pushObject(item);
you push your new question to questions controller array. I think it would be better if you push the new question directly to the exam has_many relation.
exam = this.modelFor('exam');
exam.get('questions').pushObject(item);

Categories