Since the ember-guides explains how to load mutliple models on a route like that
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
songs: this.get('store').findAll('song'),
albums: this.get('store').findAll('album')
});
}
});
Im wondering how to load only the related model-entries from a second one, like loading ALL songs but only the albums which are indexed in the songs if we assume that the song model containing this
...
albums: hasMany('album'),
...
How can I do that?
Assuming your adapter and JSON API backend support it, you can simply say:
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
songs: this.get('store').findAll('song', { include: 'albums' }),
});
}
});
Typically, this will generate a GET to /songs?include=albums, which tells the JSON API backend to include the related album resources, according to http://jsonapi.org/format/#fetching-includes.
On the Ember side of things, this feature is documented at http://emberjs.com/blog/2016/05/03/ember-data-2-5-released.html#toc_code-ds-finder-include-code.
If the above isn't an option, then there's no way to load everything in one request without building a custom endpoint and using store.pushPayload.
Here is one way to do it
export default Ember.Route.extend({
model() {
var promise = new Ember.RSVP.Promise(function(resolve,reject){
this.store.findAll('song').then(function(songs){
var albumPromises = songs.map(fuction(s){return s.get('album')});
Em.RSVP.all(albumPromises).then(function(){
resolve(songs);
});
});
});
return promise;
}
});
So Basically you are waiting till everything is resolved.
Hope it helps!
Related
Am I able to nest Ember Data adapters?
For example, say if I have a model //models/site.js with a template //templates/site.hbs and I want to make a custom query() request to ${ENV.APP.API_HOST}/customers/${org}/sites - as per the docs I can simply customise the query() function at //adapters/site.js.
But what if I have a second template at //templates/sites/show.hbs and I need to query a second distinctly different endpoint such as ${ENV.APP.API_HOST}/customers/${org}/sites/${id} (or any other deeply nested data endpoint) can I setup an adapter under //adapters/sites/show.js? I can't seem to achieve that with Ember Data currently.
As far as I know, Ember doesn't support nested endpoints at the moment. Related discussions: 1, 2.
So I was able to customise and fix this by using an ember plugin - https://github.com/amiel/ember-data-url-templates/. It has good documentation and allows you to customise URL segments.
My site adapter
// adapters/site.js
export default ApplicationAdapter.extend({
urlTemplate: '{+host}/api/{apiVersion}v1/customers{/org}/sites{/site}',
queryUrlTemplate: '{+host}/api/{apiVersion}v1/customers{/org}/sites'
});
And my service adapter
// adapters/service.js
export default ApplicationAdapter.extend({
urlTemplate: '{+host}/api/{apiVersion}v1/customers{/org}/services{/service}',
});
Then in my routes I loaded params that were picked up by the URL segments in my adapters thanks to ember-data-url-templates. Using seperate queryRecord() calls with Ember store allowed me to specify the correct endpoints as required.
// routes/sites.js
export default Ember.Route.extend({
model: function(params) {
let siteQuery = this.modelFor('sites');
let org = siteQuery.customer_orgCode;
return RSVP.hash({
site: this.get('store').queryRecord('site', { org: org, site: params.site_id })
});
}
});
// routes/sites/show.js
export default Ember.Route.extend({
model: function(params) {
let siteQuery = this.modelFor('sites');
let org = siteQuery.customer_orgCode;
return Ember.RSVP.hash({
service: this.get('store').queryRecord('service', { org: org, service: params.instance_id })
});
}
});
NB // I've use an RSVP hash as there's likely to be multiple calls for the same model, but you can just return the this.get query as necessary directly to model: as well.
I want to reload my route model after save/update action in the route.js file. Following is my route model.
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
employeeList : this.store.findAll('employee'),
employee : this.store.createRecord("employee"),
});
}
I tried following and it doesn't worked.
this.refresh(); // I saw this in many posts.
Can anyone suggest how to reload a model after save/update operation?
Try to use store.push method.
"push is used to notify Ember Data's store of new or updated records that exist in the backend. This will return a record in the loaded.saved state"
store.push(store.normalize('employee', savedEmployee));
More: http://emberjs.com/api/data/classes/DS.Store.html#method_push
I'm wondering if it's a timing thing. New records show up in the data store for me great when I use a subroute...i.e. routes/employees.js returns this.store.findAll('employee') while routes/employees/new.js returns this.store.createRecord('employee').
If a new route isn't an option, perhaps a promise will help?
export default Ember.Route.extend({
model() {
// forces a createRecord after the store is already loaded with all employees
var newEmployeePromise = new Ember.RSVP.Promise((resolve, reject) => {
let allEmployees = this.store.findAll('employee');
allEmployees.then(() => {
resolve(this.store.createRecord('employee'));
}, (error) => {
reject(error);
});
});
return Ember.RSVP.hash({
employeeList : this.store.findAll('employee'),
employee : newEmployeePromise,
});
}
}
you could use setupController hook to fix the issue.
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
employeeList : this.store.findAll('employee'),
employee : this.store.createRecord("employee"),
})
setupController(controller, model) {
controller.set('employeeList', model.employee);
}
});
Hope this help you !!!
Creating new record through this.store.createRecord() ,will update the store automatically, so you don't need to refresh the page, since it will update ui automatically.
If you still you need to update the page, like you said you can call this.refresh().. You please have a look at twiddle
If you provide ember-twiddle for non working.it would be good.
Cross-posting from discuss.ember. I am using Ember 2.0.1 with Ember-data 2.0 and default the default RESTSerializer generated by ember-cli. I know this question has been asked to many places before (which none have real answers) but no solutions have been working for me yet.
I have this model hook for a user model :
export default Ember.Route.extend({
model() {
return this.store.findAll('user');
}
});
Router is the following :
Router.map(function() {
this.route('users', { path: '/' }, function() {
this.route('user', { path: '/:user_id' }, function(){
this.route('conversations', { path: '/'}, function(){
this.route('conversation', { path: '/:conversation_id' });
});
});
});
});
For example, going to /conversations/4 transitions to users.user.conversations. My relations are defined in my models. In the user model I have a DS.hasMany('conversation') conversations attribute set with { embedded: 'always' }. Returned JSON looks like this :
{"conversations":[
{
"id":183,
"status":"opened",
"readStatus":"read",
"timeAgoElement":"2015-08-20T16:58:20.000-04:00",
"createdAt":"June 16th, 2015 20:00",
"user":
{
"id":4
}
}
]}
The problem I get is that Ember-data is able to add my data to the store but I get this error :
Passing classes to store methods has been removed. Please pass a dasherized string instead of undefined
I have read these posts : #272 and #261
Is it a problem with the JSON response?
Thank you. I have been using ember-data for quite a bit of time and never encountered this error before switching to ember 2.0.1 and ember-data 2.0.0
EDIT : I am now sure it is related to the embedded conversations because in ember inspector, if I try to see the conversations of a user (and the conversations are loaded into the store), it returns me a promiseArray which isn't resolved.
Try not to push objects to store directly. Possible use-case of .push() :
For example, imagine we want to preload some data into the store when
the application boots for the first time.
Otherwise createRecord and accessing attributes of parent model will load objects to the store automatically.
In your case UserController from backend should return JSON:
{"users" : [ {"id":1,"conversations":[183,184]} ]}
Ember route for conversation may look like:
export default Ember.Route.extend({
model: function(params) {
return this.store.find('conversation', params.conversation_id);
}
}
User model:
export default DS.Model.extend({
conversations: DS.hasMany('conversation', {async: true})
});
You don't have to always completely reload model or add child record to store. For example you can add new conversation to user model:
this.store.createRecord('conversation', {user: model})
.save()
.then(function(conversation) {
model.get('conversations').addObject(conversation);
});
P.S. Try to follow Ember conventions instead of fighting against framework. It will save you a lot of efforts and nervous.
Your conversation route has URL /:user_id/:conversation_id. If you want it to be /:user_id/conversations/:conversation_id, you should change this.route('conversations', { path: '/'}, function(){ to this.route('conversations', function(){ or this.route('conversations', { path: '/conversations'}, function(){
I wanted to make a searchQuery that needs to go to the server and fetch new data (the query has to be in the server Filter is not an option)
So I figured out that I must have a different route for search
(using ember_cli)
I have a hbs/controller/route named sessions
And now I added a route search-sessions.js
The search function in the sessions controller calls: this.transitionToRoute("search-sessions", query);
I wanted to not DRY so I tried to make search-sessions.js work with sessions controller/hbs (they are exactly the same other than the fact that they have a query passed to the server)
I tried adding the following code in search-sessions.js route:
export default Ember.Route.extend({
model: function(params) {
return this.store.findQuery('session', params.filters);
},
controllerName: 'sessions',
renderTemplate: function() {
this.render('sessions');
}
});
The thing is - that the model/view doesn't get updated unless I refresh the page
If I duplicate the code (separate hbs/controller for search-sessions it will work but will miss the point of not duplicating code)
The following seems to refresh the model:
App.SearchRoute = Ember.Route.extend({
model: function(params) {
return ['pink', 'orange', 'green'];
},
controllerName: 'common',
renderTemplate: function() {
this.render('common');
}
});
What are you doing different? Can you reproduce your issue in the following jsbin?
http://emberjs.jsbin.com/yukaxe/2/edit
I am developing a website using Ember JS.
I have created a nested route like this:
//router
this.resource('store/checkout', {path: '/store/checkout/:order_id'}, function(){
this.resource('store/checkout-lines', {path: ''});
});
This results in the route /store/checkout/:order_id calling both routes and corresponding tempaltes.
The template for store/checkout has an {{outlet}} for the template store/checkout-lines.
In the routes I have this code:
//store/chekout
export default Ember.Route.extend({
model: function(params) {
return this.store.find('order', params.order_id);
}
});
//store/checkout-lines
export default Ember.Route.extend({
model: function(params) {
var order_id = params.order_id; //this does not work
return this.store.find('order-item', {orderId: order_id});
}
});
But my problem is that in the route for store/checkout-lines, I cannot get the orderId.
How can I achieve this? Or am I at the wrong track and should be doing this in another way?
My goal is that the route /store/checkout/:order_id should call the server to fetch both order and orderItems.
What some people seem to miss is that even if you are visiting a nested route, the model for the parent route is loaded. In your nested route, you can easily fetch the model from the parent route using modelFor(type)and then get your information from there. In your case it would be like this.
//store/checkout-lines
export default Ember.Route.extend({
model: function(params) {
var order_id = this.modelFor('checkout').get('id');
return this.store.find('order-item', { orderId: order_id });
}
});
This might seem like an extra step but when you get around to it it really makes a lot of sense and works very well.