Reloading just one model using RSVP in Ember - ember.js

To load many data models into a route on the model hook you need to use RSVP so I'm doing this:
export default Ember.Route.extend( {
queryParams: {
color: {
refreshModel: true,
},
},
model( params ) {
return Ember.RSVP.hash( {
cars: this.store.query( "car", params ),
stats: this.store.findRecord( "shop", params.shopId ),
} );
},
actions: {
filterByColor( color ) {
if ( color !== -1 ) {
this.transitionTo( { queryParams: { color } } );
}
else {
this.transitionTo( { queryParams: { color: undefined } } );
}
},
},
} );
The problem comes in that stats is always going to be the same but using queryParams I will change the cars list.
The action (triggered from a button) will transition to the same page changing the queryParams, which will call the model hook (as intended) but it will reload both models, cars and stats, when I'm only interested in refreshing cars.
Is there a way, with or without RSVP, to load both models on entry but only update one of them when queryParams change?

You can use setupcontroller hook in you route file. setupController hook always called when you transistion to route. Whenever you change queryParams, model hook called.
you can pass "stats" model into controller by using setupController hook. like that
Modelhook should be like that
model( params ) {
return Ember.RSVP.hash( {
cars: this.store.query( "car", params ),
stats:params.shopId
} );
},
setupController: function(controller, model) {
controller.set('cars', model.cars );
controller.set('stats',this.store.findRecord( "shop", model.shopId ));
}
after these changes you have to use linkto transition with .id and also use cars stats model in controller or template. you cannot use model.cars or model.stats in controller or template. because we are passing cars and stats model in controller by setupcontroller hook.

Related

How to carry some data between page using model in Ember?

I am trying to carry some data by updating the ( adding ) some properties to existing model. But it's not carried to next page. so i tried like this:
combineArray:Ember.computed(function(){
let trans = Ember.A();
let index = 0;
var that = this;
this.get('model.ccList').map(function(card,num){
let name = card.get('cardName');
let numb = card.get('cardNum');
card.get('cardtransactions').map(function(transaction){
console.log( 'transaction', JSON.stringify(transaction) )
let temp = {};
temp = JSON.parse(JSON.stringify( transaction ));
temp.cardName = name;
temp.cardNum = numb;
temp.selected = false;
temp.handlingFee = that.setHandlingFee( transaction.get('localCurrencyAmount') );
trans.push( Ember.Object.create().setProperties(temp) );
})
});
this.model.set('processedTrans', trans );
console.log( this.model.get('processedTrans').get('firstObject'));
return this.model.get('processedTrans');
}),
Using the above approach I am finding that, some of data's are missing. so what is the correct way to carry some data between page?
Using service not sets for me. because I have no.of pages. I am looking to update a model with required datas.
I would recommend that you begin thinking in terms of route rather than page. Ember Routes are a hierarchy, so you would have parent routes and child routes.
As an example, consider the following in your router configuration:
this.route('post', {
path: '/post/:post_id'
}, function() {
this.route('edit');
});
In this case, there is a parent (post) and a child (post.edit).
In your Route for post, you would have:
export default Route.extend({
model(params) {
return this.store.findRecord('post', params.post_id);
}
});
For the Route for post.edit, you would have:
export default Route.extend({
model(params) {
return this.modelFor('post');
}
});
So here, the child and the parent are using the same model. If you need additional stuff in your child route, you can do that in the model hook (again, this is the child route):
export default Route.extend({
model(params) {
let post= this.modelFor('post');
return post.get('someOtherProperty');
}
});
In this case, the child's model will be the value of the parent's model's someOtherProperty attribute.
Hope this helps you think about your problem in a different way.

Ember: create a DS.belongsTo *outside* of a Model

I'm wanting to ideally create a DS.belongsTo / BelongsToRelationship in my own class (which is an Ember.Object, but not a DS.Model), or alternatively recreate the functionality to let me hold a reference to a record in my own class. I don't know if it's possible to use a DS.belongsTo outside of a DS.Model, or if so, how to set it up.
Background:
I have an ember-cli app using ember-data + ember-fire + firebase. One of my models has an attribute which is an object holding "type specific" information for the record. I transform this object into my own class based on the type it describes, and some times that type will have references to other records in the database. In these cases I would like to have a DS.belongsTo property set up in my typeSpecific class that I could link to in the same way as linking to a relationship in a regular model.
Alternative:
After a lot of searching and not finding any information on how to do this I made my own class which got me most of the way there. I've just noticed that although I can change the record it references on the client side and have it update, if I change it on the server-side I don't get updates coming through, so it's back to the drawing board.
If anyone is able to tell me how to make this alternative approach work that would serve the purpose too. The idea with this class is that I pass it a model name and id, and it should create the model reference and then keep model and id in sync if either side changes, and also pass through updates if anything on the model it's connected to gets changed just like a regular relationship would.
export default Ember.Object.extend({
id: null,
table: undefined,
model: undefined,
store: undefined,
init: function() {
this._super();
if(this.id && !this.model) {
this.updateModel();
}
else if(this.model && !this.id) {
this.updateId();
}
},
updateModel: function() {
var self = this;
if( this.get('id') ) {
this.store.find(this.get('table'), this.get('id')).then( function(model) {
self.set('model', model);
});
}
else {
self.set('model', undefined);
}
}.observes('id','table'),
updateId: function() {
if(this.get('model')) {
this.set('id', this.get('model.id'));
}
else {
this.set('id', null);
}
}.observes('model'),
});
Edit: code to manipulate the object above:
//Creating a reference:
this.set('target', ModelPointer.create({store:this.get('store'), table:this.get('targetTable'), id:targetId}));
//or:
this.set('target', ModelPointer.create({store:store, table:'myTable'}));
...
this.set('target.id', '42');
I believe that at the moment if I change either the id or model on the client the other will update automatically, eg:
//either:
this.set('target.id', '43');
//or:
this.store.find('myTable','43').then( function(newModel) {
self.set('target.model', newModel);
});
The problem is that if I log into Firebase and change myTable['42'].name='Fred' then the value showing on my web page which is linked to target.model.name doesn't update to 'Fred'. I suspect that if I set target.model.name to 'Fred' on the client side and save it wouldn't update the value on the server either(?)
The cleanest solution I've come up is to not store the id separately (left to the model itself). I've verified that changes I make in Firebase propagate to the displayed entry just fine.
With this solution setting the referenced model can be done with either its id or simply the model instance itself. See the controller code for examples.
Firstly, for reference, some test data for Firebase:
{
"testModels": {
"1": {
"name": "Model one"
},
"2": {
"name": "The second model"
},
"3": {
"name": "Third is the charm"
}
}
}
Thus its model app/models/test-model.js just needs the name in there.
Here is my belongsTo-like proxy class, I put mine under app/utils/proxy-class.js but it should probably be a Mixin:
import Ember from 'ember';
export default Ember.Object.extend({
remote: null, // reference to the remote DS.Model
store: null, // reference to the actual store
storeModel: null, // name of the model in the store
_watchRemote: function() {
var self = this;
if ( typeof self.get('remote') === 'object' ) {
// do nothing, already an object
if ( ! Ember.isPresent( self.get('store') ) ) {
// but set the store from the model
self.set( 'store', self.get('remote.store') );
}
} else if ( typeof self.get('remote') === 'string' ||
typeof self.get('remote') === 'number' ) {
// it's an id, so fetch the model
self._fetchModel( self.get('remote') );
}
}.observes('remote').on('init'), // on change, and during object init
_fetchModel: function( id ) {
var self = this;
self.store.find( self.get('storeModel'), id ).then(
function( model ) {
self.set( 'remote', model );
}, function ( err ) {
console.error( "couldn't read from the store:", err );
});
},
});
I created this controller, and used the browser console to change the model on the fly to test that model changes are picked up:
import Ember from 'ember';
import proxyClass from '../utils/proxy-class';
export default Ember.Controller.extend({
model: {
remoteFromId: null,
remoteFromModel: null,
},
init: function() {
var self = this;
self.set( 'model.remoteFromId',
proxyClass.create({
remote: 1,
store: self.get('store'),
storeModel: 'test-model',
})
);
self.get('store').find( 'test-model', 2 )
.then( function( model ) {
self.set( 'model.remoteFromModel',
proxyClass.create({
remote: model,
storeModel: 'test-model',
// no store provided here: set from the model
})
);
});
}
});
And the template for the controller:
<p>remoteFromId: {{model.remoteFromId.remote.id}}
{{model.remoteFromId.remote.name}}</p>
<p>remoteFromModel: {{model.remoteFromModel.remote.id}}
{{model.remoteFromModel.remote.name}}</p>

ember data reload() undefined

I am trying to reload a model that has changed on the server. My code is as follows:
App.CustomersController = Ember.ArrayController.extend({
intervalId: undefined,
startRefreshing: function() {
var self = this;
if ( self.get( 'intervalId' ) ) {
return;
}
self.set( 'intervalId', setInterval( function() {
//self.get('model').update();
self.get('model').reload();
}, 30000 ) );
}
});
App.CustomersRoute = Ember.Route.extend({
model: function() {
return this.store.find('customer');
},
setupController: function( controller, model ){
this._super( controller, model );
controller.startRefreshing();
},
actions: {
reload: function() {
this.get('model' ).reload();
}
}
});
You can see that I have two mechanisms for reloading the data - one a timer, and also an action triggered by a button in the UI. The latter is exactly what is shown in the ember-data documentation here: http://emberjs.com/api/data/classes/DS.Model.html#method_reload
Neither works. I get undefined in both cases i.e. the model returned does not have a reload() method. update() sort of works, except it does not remove deleted records and it is not what is recommended in the documentation. What am I doing wrong here in trying to use reload?
My stack:
DEBUG: -------------------------------
DEBUG: Ember : 1.5.1+pre.07fafb84
DEBUG: Ember Data : 1.0.0-beta.7.f87cba88
DEBUG: Handlebars : 1.3.0
DEBUG: jQuery : 1.11.0
DEBUG: -------------------------------
and I am using the following adapter in case that makes any difference:
App.Store = DS.Store.extend({
// Override the default adapter with the `DS.ActiveModelAdapter` which
// is built to work nicely with the ActiveModel::Serializers gem.
adapter: '-active-model'
});
reload exists on a record, not a collection.
You would need to iterate the collection and call reload on each record.
self.get('model').forEach(function(record){
record.reload();
});
But I'm guessing you don't want to waste the callbacks to the server. In this case I'd recommend returning a filter as your model, then make another call to the server for all records.
App.CustomersRoute = Ember.Route.extend({
model: function() {
this.store.find('customer');
return this.store.all('customer');
},
setupController: function( controller, model ){
this._super( controller, model );
controller.startRefreshing();
},
actions: {
reload: function() {
this.get('model' ).reload();
}
}
});
App.CustomersController = Ember.ArrayController.extend({
intervalId: undefined,
startRefreshing: function() {
var self = this;
if ( self.get( 'intervalId' ) ) {
return;
}
self.set( 'intervalId', setInterval( function() {
self.store.find('customer'); // get all customers again, updating the ones we have
}, 30000 ) );
}
});

confused about asyc loading from direct url emberjs

Below are my fixture adapters.
My understanding is model hook is not called on transitionTo or link-to when a model is provided.
But when same route is reached direactly by sharing or copy pasting url model hook is called.
Now when I transition by passing a model I see the city 1 and city 2 properly.
But when I copy past url the cities are not displayed. I tried .then and then get cities still I cannot see it. I have commented that line. I know I am doing something stupid. I did googled but couldnt figure out.
here is my jsbin: BIN BIN BIN
Though this is similar to THiS question. the ans is use modelFor and then findBy. but modelFor gives model for the parent route. but in my case since its not nested routes. this.modelFor('countries') gives undefines and hence i cannot apply findBy on it.
model: function(params) {
this.store.find('country', params.countryCode).then(function(country) {
console.log(country);
//country.get('cities');
});
}
Q.Country.FIXTURES = [{
id: 1,
countryCode: "CO",
countryName: "Country",
cities: [1, 2]
}];
Q.City.FIXTURES = [{
id: 1,
cityName: "city 1",
country: 1
}, {
id: 2,
cityName: "city 2",
country: 1
}];
Q.CountryRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('country', params.countryCode);
},
serialize: function(country) {
return {
country_id: country.get("countryCode")
};
},
afterModel: function(model) {
console.log("after model was called");
//this.transitionTo('cities',model);
}
});
Q.Router.map(function() {
this.resource("countries");
this.resource('country', {
path: ':country_id'
});
});
You have to return something from your model() hook. No need to use then() because in the model hook Ember will automatically wait for the promise to resolve.
model: function(params) {
return this.store.find('country', params.country_id);
}
If you want to use a slug, something like this could work:
Q.Router.map(function() {
this.resource("countries");
this.resource('country', {
path: ':country_code'
});
});
model: function(params) {
return this.store.findQuery('country', { code: params.country_code });
}

why am i seeing "error while loading route" with ember?

ember rc1, ember-data rev 12. all my other routes load correctly, unsure why i'm seeing this error. it happens when i try to access the show route i.e. /files/groups/5. the index route renders fine.
i've pasted the stack trace below but it's not very informative. is there something basically wrong i'm doing here?
my routes/controllers are set up as follows:
this.resource('files', { path : '/files' }, function() {
this.resource('groups', { path : '/groups' }, function() {
this.route('show', { path : '/:asset_link_group_id' });
});
});
AssetLinksApp.GroupsShowController = Ember.ArrayController.extend({
content : Ember.A(),
assetLinkGroup : null
});
AssetLinksApp.GroupsShowRoute = AssetLinksApp.AuthRequiredRoute.extend({
setupController : function(controller,model) {
controller.set('content',model.get('asset_links'));
controller.set('assetLinkGroup',model);
},
model : function(params) {
return AssetLinksApp.AssetLinkGroup.find(params.asset_link_group_id);
}
});
the stack trace:
Error while loading route: TypeError {} exchange_vendor.js:12078
(anonymous function) exchange_vendor.js:12078
Ember.Router.reopenClass.defaultFailureHandler.setup
exchange_vendor.js:35011 failure exchange_vendor.js:34448
objects.concat.context exchange_vendor.js:34497 invokeCallback
exchange_vendor.js:17846 Promise.then exchange_vendor.js:17893
EventTarget.trigger exchange_vendor.js:17822 results
exchange_vendor.js:17924 RunLoop._prev exchange_vendor.js:15911
Ember.handleErrors exchange_vendor.js:12140 invoke
exchange_vendor.js:15909 iter exchange_vendor.js:15981 RunLoop.flush
exchange_vendor.js:16035 RunLoop.end exchange_vendor.js:15940 tryable
exchange_vendor.js:16143 Ember.tryFinally exchange_vendor.js:12831
Ember.run.end exchange_vendor.js:16146 Ember.tryFinally
exchange_vendor.js:12833 Ember.run exchange_vendor.js:16102
Ember.HashLocation.Ember.Object.extend.onUpdateURL
exchange_vendor.js:36690 jQuery.event.dispatch exchange_vendor.js:3144
jQuery.event.add.elemData.handle.eventHandle
The model returns a single record. However you have defined an ArrayController.
Ember.js automatically places the model in the controller's content property, which will cause an error since it will be putting a single record in an array controller.
Even though you overrode the setupController, before it fires, Ember.js will place the model in the controller anyway. There's currently no way of stopping that.
Update 3-June-2014 (Ember > 1.0) :
If you now override setupController Ember no longer sets the model property.
The only solution I can think of is to add a resource to your routes:
this.resource('files', { path : '/files' }, function() {
this.resource('groups', { path : '/groups' }, function() {
this.resource('group', { path : '/:asset_link_group_id' }, function() {
this.route('index');
});
});
});
This means that you have an object controller (GroupController) containing the group,
and an array controller (GroupIndexController) containing the asset links array.
AssetLinksApp.GroupIndexController = Ember.ArrayController.extend({
assetLinkGroup : null
});
AssetLinksApp.GroupIndexRoute = AssetLinksApp.AuthRequiredRoute.extend({
setupController : function(controller,model) {
controller.set('content',model);
controller.set('assetLinkGroup', this.modelFor('group'));
},
model : function(params) {
return this.modelFor('group').get('asset_links');
}
});
Now your template should be named group/index instead of groups/show. As for group template, it can be an empty template containing {{outlet}}.
Most important thing to note from this is: If your controller is an object controller, you have to return an object from the model hook, and if your controller is an array controller, then you have to return an array from the model hook.
The bright side is, this pushes you to follow a certain design that I and probably the core team think is better.