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
Related
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 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
SO,
I am working on an Ember app and experiencing a confusing problem. At the index route the app performs a find() and returns an array of dataset and links to a template to show further details about each dataset which are sideloaded when a resquest is made to find by id. (i.e. find(1), where 1 is the id.)
The first request with an id works fine, returning the dataset object and it's sideloaded data, however subsequent requests do not seem to do anything. The server does not see any request if I try to navigate to any other dataset after the first one's details have been loaded. However if I navigate from a specific dataset back to index and then back to any dataset it will send the request again (twice even, am not sure if this a related problem) and work. In other words:
/# works
/#/1 also works (or any other id as long as it is the first one visited)
/#/1 then /#/2 does not work, no request is sent
/#/1 followed by /# then /#/2 does work, maintaining the data at /#/1 & getting the new data for /#/2.
How do I get all of the specific dataset objects to return upon visiting them, without the hacky pitstop at index? Any advice would be greatly appreciated, thank you in advance!
The code:
-app.js
/**************************
* Application
**************************/
var App = Em.Application.create();
App.Router.map(function() {
this.resource('application', {path:'/'}, function() {
this.resource('dataset', {path: '/:dataset_id'}, function() {
});
});
});
App.ApplicationRoute = Em.Route.extend({
model: function() {
return App.Dataset.find();
}
});
App.DatasetRoute = Em.Route.extend({
activate: function() {
this.modelFor('dataset').reload();
}
});
/**************************
* Models
**************************/
App.Store = DS.Store.extend({
adapter: DS.RESTAdapter.create({
url: 'http://***.***.***.***:5000',
namespace: 'api',
serializer: DS.RESTSerializer.extend({
primaryKey: function(type) {
return '_id';
}
})
})
});
App.Dataset = DS.Model.extend({
dataset: DS.attr('string'),
title: DS.attr('string'),
points: DS.hasMany('App.Point')
});
App.Point = DS.Model.extend({
dataset: DS.attr('string'),
dataset_id: DS.attr('string'),
date: DS.attr('date'),
value: DS.attr('string')
});
A route's activate hook is only called when the route is first transitioned to. It is not called again if the route's model changes. So when you transition into App.DatasetRoute either by entering the url directly or by clicking link on index page, the activate hook runs and your dataset is reloaded. When you switch from #/1 to #/2, the route remains active and no hook is called.
If I am understanding your question correctly, you want to reload the dataset whenever a user visits its url. In that case instead of the route's activate hook what you probably want to do is observe changes to the dataset controller's content. Something like this should work:
App.DatasetController = Ember.ObjectController.extend({
refreshOnChange: function() {
var dataset = this.get('content');
if (dataset) {
console.log('reloading dataset ', dataset.toString());
dataset.reload();
}
}.observes('content')
}
In my ember app, I have a router with nested resources, like so:
App.Router.map(function () {
this.resource('explore', function() {
this.resource('building', { path: 'building/:slug' });
this.resource('country', { path: ':slug' }, function() {
this.resource('state', {path: ':slug' });
});
});
});
App.CountryRoute = Ember.Route.extend(App.SlugRouter, {
setupController: function(controller, country) {
controller.set('title', 'country detail');
controller.set('model', country);
}
});
App.SlugRouter = Ember.Mixin.create({
serialize: function(model, params) {
var name, object;
object = {};
name = params[0];
object[name] = model.get('slug');
return object;
}
});
App.Building = DS.Model.extend({
country: DS.belongsTo('App.Country'),
name: DS.attr('string'),
slug: DS.attr('string')
});
App.Country = DS.Model.extend({
name: DS.attr('string'),
slug: DS.attr('string'),
buildings: DS.hasMany('App.Building'),
states: DS.hasMany('App.State')
});
Loading the explore route shows a list of buildings received from the server (a django-rest-framework app), each building has a relationship to a country with a belongsTo attribute.
In the explore.index route, I display the list of the buildings, with links to the country route for each building, using {{linkTo this.country}}. The href, however, is loaded as #/explore/undefined, instead of #/explore/<country-name>.
The part that is confusing me is that this only happens the first time that I load the list. If I go to another route, then come back to #/explore, the links render correctly.
In the debugger, putting a breakpoint in the serialize method, I see that the first time that I load the page, the model object is empty (_data.attributes is an empty object). Going to the network tab in the debugger, I see that the a request has been made to the server to get the country data, but the response has not been received yet:
The response is eventually received, since {{this.country.name}} renders correctly, but after it's too late.
Thanks in advance for any responses/tips.
I am using:
Ember: 1.0.0-rc.5,
Handlebars: 1.0.0-rc.4,
jQuery: 1.8.3,
ember-data: 0.13,
ember-data-django-rest-adapter: 0.13
Firstly you are mixing in App.SlugRouter before it's definition. You should be seeing an error like Assertion failed: Expected hash or Mixin instance, got [object Undefined] in the console.
After that you need to load the model for a country by the slug. I don't see this in your Route either. You need something like this in CountryRoute depending on your persistence library.
model: function(params) {
return App.Country.find({slug: params.slug});
}
I suspect the part that is working right now is because your index route is loading the model and passing it in to setupController with the linkTo. Direct loading of the nested page requires configuring that route's model hook.
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.