Accessing store in another route - ember.js

I have this model:
App.Game = DS.Model.extend({
name: attr(),
uri: attr()
});
and this route:
App.GamesRoute = Ember.Route.extend({
model: function() {
return this.store.find('game');
}
});
This works fine, calls the backend server, and stores elements in the store (I've checked with Ember inspector). This is the json I return:
{"games":[{"id":"TicTacToe","name":"TicTacToe","uri":"http://localhost:10000/games/TicTacToe"}]}
Now I have this template for 'games' (snipped):
{{#each game in model}}
{{#link-to 'games.matchlist' game.id}}{{game.uri}}{{/link-to}}
This shows the URI for each game. Now in the games.matchlist route what I would like to do is to search in the store by the game_id received param and get the game URI. The reason is that the server doesn't follow RESTAdapter conventions, and I would like to make a custom AJAX query to that URI myself.
This doesn't work:
App.GamesMatchlistRoute = Ember.Route.extend({model: function(params) {
var store = this.store;
var game = store.find('game', params.game_id)
console.log(game);
console.log("URI: " + game.uri);
at this point, game is an object but it's not an instance of my model. It doesn't have a uri attribute. What am I doing wrong? I'm feeling that I'm missing something obvious.

If you want to get records without hitting the server and you know you already have it in the store, use this.store.getById('game', ID).

I'm on my mobile, but you need to create a GameAdapter and customize I believe the fetch function. Checkout the docs for adapters on the ember site and you should have your answer.
Your other option is to fetch the data from your server and use this.store.pushPayload(data).
Docs here: http://emberjs.com/api/data/classes/DS.Store.html#method_pushPayload
And the adapter docs here: http://emberjs.com/guides/models/customizing-adapters/

Related

Temporary Non-Persistent Record with Ember-Data 1.0.0-beta

I'm new to Ember and Ember-data and am deciding whether to use Ember-Data or one of the other persistence libraries. In order to evaluate, I'm experimenting with writing a small Rails-backed app.
One of my routes can be considered similar to the Todo MVC app that is frequently used in examples.
In my template, I have a number of input fields that represent attributes within the model. Furthermore, I also have one element in the model that represents a hasMany relationship.
Models:
App.CompanyModel = DS.Model.extend
company: DS.attr()
desc: DS.attr()
contacts: DS.hasMany('company_contact')
App.CompanyContactModel = DS.Model.extend
firstname: DS.attr()
lastname: DS.attr()
...
Within my controller, I want to be able to create a new CompanyModel record (and by virtue, add one or more contacts models to it), but not have it appear within the controller's instance of the CompanyModel until I'm ready to do so.
Currently, when a user wants to add a new record, I have a component that calls an action in my controller as follows:
#set('new_company',
#store.createRecord('company')
)
This actually works fine, except for one thing. My view has to populate the individual attributes within "new_company", which it does, however, the record is immediately added to the controller's model instance and appears in the list of records; I only want the newly created record to be visible in the table once a particular action has taken place.
Instead of instantiating new_company with createRecord, I could do something like this:
#set('new_company',
Ember.Object.create
companyname: ''
desc: ''
contacts: [
firstname: ''
lastname: ''
]
)
And then do a #store.createRecord('company', #get('new_company')), however, given I've already defined my attributes in the model, it doesn't feel very DRY to me.
I'm using Ember 1.5.0 and Ember-Data 1.0.0-beta.7.
It appears I'm not the first person to have this issue (create temporarty non persistent object in Ember-Data), but it appears that Ember-Data has sufficiently changed to make all of these solutions inoperable.
Thanks for your help!
You're real issue is you're using what's considered a live collection. I'm going to assume in your route you've done something like this:
App.FooRoute = Em.Route.extend({
model: function(){
return this.store.find('company');
}
});
find with no parameters says, hey Ember Data, find me all the records that are company. Well Ember Data shoots off a request to your back-end, then returns store.all('company'). all is a live collection that will always have all the records of that type currently in the store. In your case, you are saying I want to avoid any record that is new. There are a couple of ways to handle this.
Create a static list. (You'll need to manually add/remove objects to/from this list).
App.FooRoute = Em.Route.extend({
model: function(){
return this.store.find('company').then(function(companies){
return companies.toArray();
});
}
});
Example: http://emberjs.jsbin.com/OxIDiVU/641/edit
Create a computed property that only shows records that aren't new
App.FooRoute = Em.Route.extend({
model: function(){
return this.store.find('company');
}
});
App.FooController = Em.ArrayController.extend({
savedRecords: function(){
return this.get('model').filterBy('isNew', false);
}.property('model.#each.isNew')
// shorthand this could be written like this
// savedRecords: Ember.computed.filterBy('model', 'isNew', false)
});
Then in your template you would iterate over the computed property
{{#each item in savedRecords}}
{{/each}}
Example: http://emberjs.jsbin.com/OxIDiVU/640/edit

Ember-Data parent template not updating when model is changed

I am using EAK with a simple nested routing structure, but changing the parent model in the child controller does not change the top-level view. For example if I have the following router.js file:
this.resource('similar', function() {
this.resource('list', { path: '/list/:phone_id' });
this.resource('upload');
this.resource('new');
});
For the 'similar' route model I am using ember-data together with the RESTAdapter which is backed Flask.
export default Ember.Route.extend({
model: function() {
return this.store.find('phone');
}
});
If I manipulate the model inside the 'upload' controller then the changes are not reflected in the template, e.g.
var record = this.store.createRecord('phone', {
numbers: [1,2,3,4]
});
record.save();
will not change "{{#each list in model}} {{list.numbers}} {{/each}}".
If I reload the page it works fine. What am I doing wrong?
Instead of store.find which hits the server and stores that exact list, try using store.filter. store.filter "remains up to date as new records are loaded into the store or created locally"
http://emberjs.com/api/data/classes/DS.Store.html#method_filter
The problem here was that the REST endpoint did not return the new record (with the id attribute set).

ember-data - loading data for a menu - best approach

I have a list of categories that are loaded on the application route:
ApplicationRoute = Em.Route.extend({
model: function() {
return Em.RSVP.hash({
collections: this.store.find('collection'),
categories: this.store.find('category'),
links: this.store.find('link')
});
}
});
This triggers the categories index action on the server, as expected. I am doing this because I need to load a list of categories for a menu.
I also have a category route:
Router.map(function() {
this.route('category', { path: '/categories/:category_id' });
});
CategoryRoute = Em.Route.extend({
model: function(params) {
this.store.find('category', params.category_id);
}
});
Since there's already a list of categories in the store, there's no request to the server. However, here I want to show more detailed information. I want to send a request to the server's show action.
I am probably not thinking in "the ember way", but it seems to me that I either have to, somehow, force the server request in the route, or create a separate model for the menu categories.
How should one go about this, with ember-data?
Notes:
ember.js-1.1.2 and
ember-data-1.0.0.beta.3
I ended up using a separate CategoriesList model. Still, this feels suboptimal.

Ember: unsaved models in dynamic URLs

I'm currently using the FixtureAdapter in my Ember app, but when I switch to the RESTAdapter, my URLs no longer work.
The app is a scorekeeping type thing, and I want users to be able to log all the scores without having to be connected to the Web. After the game is finished they can optionally save all the data to the server.
Now, when Ember wants to route to say, matches/:match_id, the ID isn't there because I didn't commit anything to the server/store, so my models don't yet have an ID and I get URLs like: /match/null/games/null
Is this expected behaviour? And if so, is there a workaround? I thought about using model.clientId and then overriding the model hook for each route to try and fetch the Model from the store using the id when present and falling back to clientId. Any other ideas?
UPDATE March 10, 2013:
The following seems to fit my needs and allows to (for now) forget about moving back and forth between local storage and the REST adapter:
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.RESTAdapter.extend({
namespace: 'api/v1',
bulkCommit: true,
generateIdForRecord: function(store, record) {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
}
})
});
UUID function taken from: Create GUID / UUID in JavaScript?
If a record hasn't been committed, then it shouldn't have an id yet. Furthermore, a url that serializes the application state of viewing that record doesn't make any sense because the record won't exist in another browser until it is committed. You couldn't just paste the url elsewhere and have it load where you left off.
I think what you really want to do is serialize the application state differently (ie. generate a less specific url) when the record is uncommitted. You can achieve this by overriding the serialize method in your route.
For example,
App.PostRoute = Ember.Route.extend({
serialize: function(model, params) {
if (model && model.get('isNew')) {
// corresponds to path '/post/:post_id'
return { post_id: 'new'}
}
return this._super(model, params);
}
});
Now if you call transitionToRoute('post', post) from your controller, where post is a newly created but uncommitted record, the application state will be serialized to the path /post/new. If you pass it a committed record with an id, it will be serialized as usual.

How to change DS.Model url in ember-data?

I am new(to ember) and trying to build a search centric Ember App w/ Ember-data also. I wanted to change the url on the fly(based on search string) and the data should change automatically(on the fly). How to do it?
This is my not working code:
Emapp.Data = DS.Model.extend({
first_name: DS.attr('string')
}).reopenClass({
url: Emapp.MyURL.get('url')
});
Emapp.MyURL = Em.Object.create({
urlParam: 'John',
url: function()
{
return 'emb/data.php?id=%#'.fmt(this.get('urlParam'));
}.property('urlParam')
});
When I execute. emapp.MyURL.set('urlParam', 'Adams'). I can inspect and see the url changed to 'Adams'. But data is not fetched again.
Edit: emapp -> Emapp (pointed out by rudi-angela)
As you have made the 'url' property a computed property, Ember takes care of updating this value when the urlParam changes. That is all you have instructed Ember to do here (and apparently it is doing it properly).
But I reckon what you want here is any change in the 'urlParam' property to trigger a fetch action. In that case a solution would be to create a separate object that observes the urlParam and will take action when the 'urlParam' value changes. Something along these lines:
emapp.watcher = Ember.Object.create({
valueBinding: "emapp.MyURL.urlParam",
observer: function() {
console.log("urlParam has changed:");
// perform your fetch here
}.observes("value"),
});
Note: I thought there was a requirement for the namespace to be capitalised (rather Emapp instead of emapp).