I'm new to Ember and getting very stuck! I have a child route that needs to wait for the parent route to return it's record set. The child route then needs to do a findByIds for all the keys returned in the parent route (parent returns many keys). My child route looks like this:
export default Ember.Route.extend({
model: function(params, transition) {
var allPermissions = Ember.ArrayProxy.create({ content: this.modelFor('permissions') });
return this.store.findByIds('outlet', allPermissions.map(function(item, index, enumerable){
console.log('in map');
return item.get('howya_system_key');
}));
}
});
What I'm trying to do here is get all the parent route records, then return the result of of findByIds on each of the parent id's returned.
When I transition into the route it does not error and there is no output from the console.log. I think my problem is that the new ArrayProxy has not been created by the point that findByIds is executed so allPermissions.map has nothing to iterate over.
I'm guessing that I need to implement this via a Promise though I can't see how I'd do it. Can anybody point me in the right direction?
Much appreciated.
UPDATE:
I think I've worked out how to do this but I'm not sure I'm doing it the 'Ember' way. I've added a promise and my route now looks like this:
export default Ember.Route.extend({
model: function(params, transition) {
var _this = this;
return new Ember.RSVP.Promise(function(resolve) {
resolve( _this.store.findByIds('outlet', _this.store.peekAll('permission').map(function(item, index, enumerable){
console.log(item.get('howya_system_key'));
return item.get('howya_system_key');
})))
});
}
});
Question is, is there a better way and is 'peekAll' the best thing to be using or am I going to end up looking at all store records for a model, rather than just store records for a model related to a parent route query?
UPDATE
OK, solved this. Can't use relationships as suggested below as the parent keys can point at different tables depending on type.
The solution was to use the promise as defined above, but use:
_this.modelFor('ROUTE_NAME').map
Instead of:
_this.store.peekAll('MODEL_NAME').map
I'll also need to filter the ID's to search for depending on Type and present different types in separate routes - but that's for another day.
For the final outcome of this where I was shown how to use the promise correctly, please see my other question
Related
I'm using this.store.push to push a record into the store from with the application controller (this action is being called from a socket service that is initialized in the application controller), using ember 2.2.1 I am achieving this like
var newStoreRecord = this.store.push({
data: {
id: id,
type: 'cart',
attributes: newCartItem
}
});
This adds this new item into the store but the template doesn't update to show the new item, I also tried adding something like this
this.get('cart.model').pushObject(newStoreRecord); assuming that I had something like cart: Ember.inject.controller(), at the top of the controller, might have had that one wrong anyway.
In the cart route I have my model being defined as so
model(params) {
this.set('routeParams',params.event_url);
return Ember.RSVP.hash({
event: null,
items: null
});
},
actions: {
didTransition() {
this.store.findRecord('event',this.get('routeParams')).then((result)=>{
this.controller.set('model.event',result);
});
this.controller.set('noItems',false);
this.store.query('cart',{auction_id:this.get('routeParams'),user:this.get('user.user.user_id'),combine:true}).then((result)=>{
if(!result.get('length')){
this.controller.set('noItems',true);
return null;
}else{
this.controller.set('model.items',result);
}
});
},
}
Not sure if I'm having troubles with getting the template to update because I'm not use the model hook? (btw, we're not using the model hook because of the bad performance on android we'd rather load an empty template with a loader and THEN load data rather than the other way around.
I have several thoughts here:
To answer your question specifically, when you set a variable from the store, like you're doing, it will only reference what was in the store at that time. It will not update automatically.
Your best bet is to add two new computed properties to your controller:
items: Ember.computed(function() {
return this.store.peekAll('cart');
}),
// You'll need to flesh this one out further
filteredItems: Ember.computed('items.#each.auction_id', function() {
return this.get('items').filter(...);
})
Reference filteredItems in your template and it should work.
Sidenote, I'd highly recommend refactoring a couple things.
I would use the setupController hook instead of didTransition. It runs after the model hook is complete so will be similar to what you're looking for
You can access the params at any time in the route, so you don't need to save them in the model hook
You don't need to return an a promise in the model hook if you're not doing any async data. Just return the object. You may need even need to do that.
Hope this helps.
Is it possible to access route model inside route action?
I am passing multiple objects inside a route model to template,
model: function() {
return {
employeeList : this.store.findAll("employee"),
employee : Ember.Object.create()
}
}
From the route action I am want to modify the route model.employee. I tried the following, but I am not getting the object.
actions:{
editAction : function(id) {
var emp = this.get("model");
console.log(emp.employee);
}
}
Can anyone give a solution to get and modify model object(employee)?
First problem is that you should return a promise from the model hook. That way, the transition will wait for the promise to Resolve. return { /*...*/}; returns an object and not a promise, even if the object itself contains promises.
The solution is to use Ember.RSVP.hash like:
model() {
return Ember.RSVP.hash({
employeeList: this.store.findAll('employee'),
employee: Ember.Object.create()
});
}
This will return a promise that resolves when all inner promises resolve.
The second problem is that you can't use this.get('model') in a route. If you think about it the model property is the hook itself and not the resolved model. Solutions:
That action is sent from the controller/template. Can't you pass the model as a parameter? That way you could access the model through the function arguments.
If you absolutely need to, this.modelFor(this.routeName); returns the model for the current route.
You can access the model in route through the controller like this.controller.get('model').
You could also implement the setupController hook and access there the model. You can then do things like this.set('employeeModel', model); for later access.
this.get('context')
gives you access to the model in the route action.
In a simple, standard ember.js route ...
this.resource('foo', function () {
this.route('show');
this.route('new');
});
I have a foo template that shows a new button (link to foo/new route) and the foo.bar data of the foo objects (with links to foo/show in a sorted way using the foo controller)
import Ember from 'ember';
export default Ember.Controller.extend({
sortProperties: [ 'bar:desc' ],
sortedList: Ember.computed.sort('model', 'sortProperties')
});
The routes foo/show and foo/new do the obvious, nothing special.
I don't need or want a "real" foo/index route, since all my navigation needs are catered for in the foo route and template. Therefore I want to transitionTo from foo/index whenever I enter that route to the first (as in given by the sorting above) foo/show route (or foo\new if there is nothing to show).
I've learned that "the ember way" is to transitionTo out of the route, but never ever out of the controller.
Now, my problem is, that I get access to the unsorted data very easily (it's the model of my parent route, that I can access via modelFor) and I can sort this "manually", but I'd rather use the already sorted data from my parent's controller. Now, I'm at a bit of a loss which hook to use in the route to be sure that my parent route's controller's computed properties are ready - and it doesn't feel right. So, what would be the ember way to do this?
Edit: Non-working example at http://emberjs.jsbin.com/hujoxigami/2/edit?html,js,output ...
Use the afterModel hook as you mention in one of your comments.
App.FooIndexRoute = Ember.Route.extend({
afterModel: function(posts, transition) {
if (posts.get('length') > 0) {
this.transitionTo('foo.show', posts.get('firstObject'));
}
}
And there is nothing wrong with sorting your model before you return it from the FooRoute. (Even better if you can receive it already sorted from your API/data storage)
App.FooRoute = Ember.Route.extend({
model: function() {
// not fully functional, just illustrative
return [{id:1, name:"Hans", detail:"german"},
{id:3, name:"Henri", detail:"french"},
{id:2, name:"Harry", detail:"english"}].sortBy('name');
}
});
The sorting properties in the controller remain relevant if you want to provide sorting controls for the user.
I'm trying to add new record to Ember Data store, and it expected to be displayed immediately. Spent few hours trying to figure out why it isn't displayed, and found, that passing parameters to Ember Data store.find prevents it.
So this code works:
App.FlagsRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('flag');
}
});
App.FlagsController = Ember.ArrayController.extend({
init: function() {
this.store.createRecord('flag');
}
});
JSBin: http://jsbin.com/OxIDiVU/409/edit
There is one new record in the bottom of the table being displayed, as expected.
And when I'm adding only one thing, the params to 'find' query, it stops working.
So this code breaks new records diplaying:
App.FlagsRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('flag', params);
}
});
JSBin: http://jsbin.com/OxIDiVU/407/edit
New record actually added to Ember Data store, but not shown in table.
Your problem is that you're creating a record in the init function of your controller. Your route's model hook is called before your controller is instantiated for the first time, so it's not finding the record because it doesn't exist. The first one works because the find method called with no parameters returns an array that updates as new records are added. Calling it with parameters doesn't do that.
Your init function is a terrible place to create records anyway. You shouldn't be dealing with any kind of persistent state in that function. Ideally, records should be created in controllers, at runtime, as the result of user interaction. If you give us a little more information about your use case and how you want to create a new flag, I can help you a bit more.
Is there a way to reload the whole store in ember-data? Or to sort items inside?
It should go like that:
user clicks "add" button
new record is added
store is commited
user is transmited to the state where items are listed
his new entry is there in correct order
To do this I have to either transmit in model.didCreate which shouldn't be (it's not the role of a model!) or refresh store after transmiting.
Anyone had similar issues?
I am using ember-data revision 12
EDIT
I used:
Ember.run.later(this, function(){
this.transitionToRoute('list');
}, 500);
To give store some time. But then again new entry is always at the bottom of the list. I still need to reload whole store or sort it somehow.
EDIT#2
So I did little rework here. In my template I use:
{{#each arrangedContent}}
To use sorted data and in the route I have:
App.AdsRoute = Ember.Route.extend({
model: function(){
return App.Ad.find();
},
sortProperties: ['id']
});
It's not working though. Looks like Route is not ArrayController. And if I make additional AdsController extending from ArrayController it is not working neither.
Ember ArrayControllers have a few options for sorting the content:
http://emberjs.com/api/classes/Ember.ArrayController.html#property_sortAscending
You can set which fields to sort by and which order you want to use. When you use these you will want to bind views to arrangedContent and not just plain old content.
In my applications I typically return to the list state after the record has been persisted:
var observer, _this = this;
observer = function(target, path) {
if (target.get(path) === 'saved') {
target.removeObserver(path, null, observer);
return _this.get('target.router').transitionTo('apps.index');
}
};
I'm not sure if the store has a reload mechanism (individual records do), but I can update this response if I find an answer.