Does ember-data subsequent find generates XHR every time without caching? - ember.js

I have an app built using ember-cli 0.2.5 (which is Ember 1.12.0 and Ember-Data 1.0.0-beta.17).
My models' store.find() always generate XHR request to my backend. I would expect subsequent route accesses to use the store's cache.
I have two routes, router.js is:
Router.map(function() {
this.route('rules', {path: '/rules'});
this.route('users', {path: '/users'});
});
The routes models are:
Ember.Route.extend({
model: function (params) {
return this.store.find('user');
}
});
and
Ember.Route.extend({
model: function (params) {
return this.store.find('rule');
}
});
I am using the RESTAdapter and targeting an apache server which executes a perl cgi. The returned JSON (snippet) is:
{"rules":[{"canAutoUnblock":1,"creator":"spaling","status":null,"autoUnblockDate":"2015-05-30","createTime":"2015-01-19 19:59:56","privComment":"not private","pubComment":"Port scanning the Library","id":12,"ipaddr":"31.7.59.152"},{"canAutoUnblock":0,"creator":"spaling","status":"delete","autoUnblockDate":null,"createTime":"2015-01-19 19:59:56","privComment":"private","pubComment":"public","id":13,"ipaddr":"31.7.59.160"},
formatted ...
{
rules: [
{
canAutoUnblock: 1,
creator: "spaling",
status: null,
autoUnblockDate: "2015-05-30",
createTime: "2015-01-19 19:59:56",
privComment: "not private",
pubComment: "Port scanning the Library",
id: 12,
ipaddr: "31.7.59.152"
},
{
canAutoUnblock: 0,
creator: "spaling",
status: "delete",
autoUnblockDate: null,
createTime: "2015-01-19 19:59:56",
privComment: "private",
pubComment: "public",
id: 13,
ipaddr: "31.7.59.160"
},
Any advice greatly appreciated.
Barry

This is expected behavior for find with no additional parameters.
store.find('modelName')
This will ask the adapter's findAll method to find the records for the given type, and return a promise that will be resolved once the server returns the values. The promise will resolve into all records of this type present in the store, even if the server only returns a subset of them.
See http://emberjs.com/api/data/classes/DS.Store.html#method_find
If you want cached records only use
store.all('modelName')
http://emberjs.com/api/data/classes/DS.Store.html#method_all

Related

Routing error with ember-data 2.0 and emberjs 2.0.1

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(){

Ember Dynamic Segment Unavailable in Route

I have the following router setup in Ember where I am trying to capture a dynamic search term and pass it to the router for querying ember-data.
Router
this.resource('resources', function() {
this.resource('resource', { path: '/:resource_id' }, function() {});
this.resource('search', { path: '/search/:search_term' }, function() {});
this.route('new');
});
Route
export default Ember.Route.extend({
model: function(params) {
return this.store.findQuery('resource', {
sTerm: params.search_term,
limit: 15,
offset: 0
});
}
});
Unfortunately, search_term in not available in the route to pass into the query, I am unsure what is causing this not to work. If someone can point me in the right direction I would sure appreciate it. Thanks.
Update as requested
Logging this.constructor produces the following:
lrs-ui#route:search/index:
I have built this with ember-cli and the route is in search/index so this makes sense. Should I just have the route at search maybe?
Answer
As #kingpin2k led to, the route was in search/index and it needed to be in search, then everything worked just fine.
For historical sake, the index route doesn't pick up the params from the parent resource.
Changing the route from search/index to search fixed the issue.

Route is not calling rest webservice

I have started a simple example using the fixture Adapter. When I navigate to my route /teams, I can see all Teams i have registered in my adapter.
Now I changed my code to register the Default restadapter:
Football.ApplicationAdapter = DS.RESTAdapter.reopen({
host: "http://localhost:8080/MatchService.svc/json"
});
When I now navigate to my route /Teams, I can see no Errors, the site looks as it should, just without no Teams listed in the list.
Then I started to have a look at the Network-Traffic: No calls are being sent, nothing.
But: When I try to add a team (I havent implemented this Method in rest service yet), I can see that there is a call going to
localhost:8080/MatchService.svc/json...
and offcourse, I get a 404.
But why dont I get a call against
localhost:8080/MatchService.svc/json/teams
when I navigated to my Teams-route?
Here is my route:
Football.TeamsRoute = Ember.Route.extend({
model: function() {
return this.store.all('team');
}
});
I changed it already from this.store.find .... to this.store.all... because after using the restadapter I got an "error while loading the route : undefined".
I also added the current ember.js -Version as reference from the ember site.
Thats the json which gets returned by the Server:
{"TeamsResult":[
{"ID":1,"Name":"Deutschland"},
{"ID":2,"Name":"Frankreich"}
]}
I also tried a a not wrapped json result from the WebService:
{[{"ID":1,"Name":"Deutschland"},{"ID":2,"Name":"Frankreich"}]}
all doesn't make a call to the server, it just returns all of the records already fetched client side. you should use this.store.find('team')
Additionally you should define your adapter using extend:
Football.ApplicationAdapter = DS.RESTAdapter.extend({
host: "http://localhost:8080/MatchService.svc/json"
});
reopen applies it to every instance created of the rest adapter, which may not be appropriate.
Here's a small example that shows the basic structure of your app:
Football = Ember.Application.create();
Football.Router.map(function() {
this.resource('teams', {path:'/teams'});
});
Football.TeamsRoute = Ember.Route.extend({
model: function() {
return this.store.find('team');
}
});
Football.ApplicationAdapter = DS.RESTAdapter.extend({
host: "http://localhost:8080/MatchService.svc/json"
});
Football.Team = DS.Model.extend({
name: DS.attr()
});
For convenience sake I mocked the call to http://localhost:8080/MatchService.svc/json/teams with what Ember Data is expecting as a response:
{
teams:[
{
id: 1,
name: "Miami Heat"
},
{
id: 2,
name: "Seattle Seahawks"
},
{
id: 3,
name: "Texas Longhorns"
}
]
}
http://emberjs.jsbin.com/OxIDiVU/425/edit

Ember query params not sending params in the request

The query-params-new flag set to true.
Using latest ember and ember data. Ember: 1.6.0-beta.1+canary.b5b90241 ember.js and Ember Data : 1.0.0-beta.7+canary.b45e23ba
Application Adapter is set to this:
export default DS.RESTAdapter.extend({
host: 'http://example.com',
namespace: 'api/v2',
headers: {
"ClientId": "a-key",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "X-Requested-With"
}
});
The locations route looks like this:
export default Ember.Route.extend({
model: function(params){
return this.store.find('restaurant');
}
});
The locations controller looks like this
export default Ember.ArrayController.extend({
queryParams: ['lat', 'lon', 'limit'],
lat: null,
lon: null,
limit: null,
});
Navigating to this url http://example.com/locations?lat=47.620508&limit=22&lon=-122.349174 Ember data sends http://example.com/locations as the request.
I must be missing something?
There are a few things you need to do to get query-params-new working as expected.
First, you say you're using the latest ember, but don't specify whether it's the latest stable release or the latest canary release (master). In order to use query-params-new, you need to use canary, which is just the master branch. It is not available in the stable or beta releases yet, but you can download it here.
Second, you need to specify in your controller which params you want to bind to the route. Your locations controller would need to look something like this:
export default Ember.ArrayController.extend({
queryParams: ['lat', 'lon', 'limit']
});
All of this is documented on the Ember site. http://emberjs.com/guides/routing/query-params/
Edit based on additional question info:
It looks like you're not using the params in your route. You can use findQuery to send the params to the server like so:
export default Ember.Route.extend({
model: function(params){
return this.store.findQuery('restaurant', params);
}
});

EmberJS: How to Handle Route with Dynamic Segment Returning an Array?

EDIT:
I've gotten around this by upgrading to EmberJS RC4. This version doesn't automatically call the model hook on routes, which allows the following:
App.LiftsRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('content', App.Lift.find({
county: model.county || model.id
}));
}
});
EDIT END
I'm attempting to add a route with a dynamic segment in EmberJS & Ember Data w/ RESTful Adapter which returns an array but I failing.
App.Router.map(function() {
this.route('lifts', { path: '/lifts/:county' });
});
App.LiftsRoute = Ember.Route.extend({
model: function(params) {
return App.Lift.find(params.county);
}
});
App.Lift = DS.Model.extend({
name: DS.attr('string'),
date: DS.attr('number'),
description: DS.attr('string'),
destination: DS.attr('string')
});
This is returning the following error:
Uncaught Error: assertion failed: Your server returned a hash with the key lifts but you have no mapping for it.
From JSON in the form {lifts: [{id: 1, name: "xyz", ...}, {id: 2, name: "abc", ...]}
Any ideas?
EDIT: Setting up a route with a single dynamic segment to return an array of objects
You can still keep the same route structure:
this.route('lifts', { path: '/lifts/:county_ids' });
And then override the model hook to parse params.county_ids into a query string:
model: function(params) {
ids = parseQueryIds(params.county_ids) // you have to parse this in a format that your server will accept
App.Lift.find({query: ids}) // returns an record array
}
This will preserve the url structure (if you go to /lifts/1,2,3, that url will be saved) but also returns an array of items.
END EDIT
This is happening because App.Lift.find, when passed a string, will try to query by id for a single object, but your response from the server is returning multiple objects (id 1, id 2, etc).
When you do App.Lift.find(params.county) (let's say params.county is "1"), Ember will make a GET '/lifts/1'. But for whatever reason, your server is returning JSON with a key that has an array.
Can you check that
the GET request ember is making is indeed for a single id? If you're using chrome, check the network requests -- what is the resource that App.Lift.find(params.county) asks for?
that params.county is defined? If it's undefined, you'll be calling App.Lift.find(undefined), which makes the GET to /lifts, and that might cause your server to return the array of objects.
that your server is responding to requests properly when a single id is requested?
the error message occurs because the root id of your JSON object is plural, and should be singular. Your server should return:
{lift: [
{id: 1, name: "xyz", ...},
{id: 2, name: "abc", ...}
]
}
You will most likely subsequently run into the issue Sherwin describes, as find() for the RESTAdapter assumes a singleton to be returned.