How and when to fetch many-to-many relationships objects from server - javascript

I'm having some issue with my Ember web application.
I'm using Ember 1.0 stable as the base JS framework and Ember-data.js 1.0 beta 1 to handle the relationships between model and the CRUD operations.
My simple application manages two entities: User and Order with a many to many relationship between them.
Here are the models:
/*User Model*/
App.User = DS.Model.extend({
first_name: DS.attr('string'),
last_name: DS.attr('string'),
fullName: function() {
return this.get('first_name') + ' ' + this.get('last_name');
}.property('first_name', 'last_name'),
userorders: DS.hasMany('userorder', {async: true})
});
/*Order Model*/
App.Order = DS.Model.extend({
description: DS.attr('string'),
userorders: DS.hasMany('userorder', {async: true})
});
I created the model Userorder which maintains the one-to-one relationships with both User and Order.
Here is the model for Userorder:
App.Userorder = DS.Model.extend({
order: DS.belongsTo("order", {async: true, key: "order_id"}),
user: DS.belongsTo("user", {async: true, key: "user_id"})
});
My problem comes up when i try to get (into UserorderRoute) the single instance of a usersorder. Follows the code for UserorderRoute:
App.UserorderRoute = Ember.Route.extend({
model: function(params) {
return this.store.find("userorder", params.userorder_id);
}
});
The userorder instance is correctly fetched, but if I try to access to the 'order' object I get undefined, (obviously, I get the same behavior with the access to 'user').

Related

Error while processing route: home No model was found for 'App.undefined'

DEBUG: Ember : 1.7.1
DEBUG: Ember Data : 1.0.0-beta.12
DEBUG: Handlebars : 1.1.2
DEBUG: jQuery : 1.10.2
Having an issue with what I believe is the belongsTo attribute on my user model. (This happens on my other belongsTo attributes within my application as well). I have a Django backend which returns a response when I comment out the network: attribute.
{
email: "test#test.com",
first_name: "Test",
global_code: "daht64q691zy4k887ch",
global_id: "GBID-USER-dat64q6917zy4k887ch",
institution_gbid: "GBID-GINS-567j53ey0lojsu2kys",
institution_name: "Some University",
last_name: "Testing",
network: { },
view_policy: {
capability: "system:view",
description: "Anyone can view a user",
hold: true,
id: "daht64q691y4k887ch:system:view",
values: ""
}
}
Code for the User Model:
App.User = DS.Model.extend({
first_name: DS.attr('string'),
last_name: DS.attr('string'),
global_id: DS.attr('string'),
network: DS.belongsTo('basicgrouping')
}):
Code for Basic Grouping model:
App.Basicgrouping = DS.Model.extend({
global_id: DS.attr('string'),
name: DS.attr('string'),
gbid_code: function(){
return getGBIDCode(this.get('global_id'));
}.property('global_id')
});
Debugging ember-data I placed a console.log() within the following code:
relationshipsByName: Ember.computed(function() {
var map = Map.create();
this.eachComputedProperty(function(name, meta) {
console.log(name, meta);
if (meta.isRelationship) {
meta.key = name;
var relationship = relationshipFromMeta(this.store, meta);
relationship.type = typeForRelationshipMeta(this.store, meta);
map.set(name, relationship);
}
});
This seems to show that the type of the object that it belongs to is not being found (Basicgrouping) as it's returning App.undefined.
My theory is it may have something to do when parsing the server response and maybe the payload response. This also happens in other belongTo relationships in my code.
It turns out that there was a file that was overriding some of the DS. methods and causing an empty type to be sent. I was in the process of removing use of the shim but didn't know that it was being used.
Thanks the Bmacs from the ember community for the help debugging the issue.

Get one item from many using slug in Ember.js

I have these two models:
deomens.Artwork = DS.Model.extend({
title: DS.attr('string'),
slug: DS.attr('string'),
description: DS.attr('string'),
thumbnail: DS.attr('string'),
artwork: DS.attr('string'),
created_at: DS.attr('string'),
updated_at: DS.attr('string'),
options: DS.hasMany('option', {async: true}),
hasOptions: Ember.computed.gt('options.length',0)
});
deomens.Option = DS.Model.extend({
name: DS.attr('string'),
detail: DS.attr('string'),
artwork: DS.belongsTo('artwork')
});
I'm using the RESTAdapter like this:
deomens.Store = DS.Store.extend({
revision: 12,
adapter: DS.RESTAdapter.create({
namespace: 'deomens/public/api'
})
});
deomens.ArtworkSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
options: { embedded: 'always', serialize: 'ids', deserialize: 'records' }
}
});
Here is my router:
deomens.Router.map(function(){
this.resource('artworks');
this.resource('artwork', {path: '/view/:slug'});
});
And here are the two routes I've created:
deomens.ArtworksRoute = Ember.Route.extend({
model: function(){
return this.store.findAll('Artwork');
}
});
deomens.ArtworkRoute = Ember.Route.extend({
model: function(params){
//need to return a single item based on slug
//something like return this.store.find('Artwork', {slug: params.slug});
}
});
ArtworksRoute works fine - it returns all the artworks with their options.
However whenever I go to /view/artworkSlug, it fires up another GET request. So can I not get an item from already retrieved data? That's one question.
The second question - let's say if I make it fire up another GET request using:
return this.store.find('Artwork', {slug: params.slug});
it sends a GET request to the following URL:
/api/artworks?slug=artworkSlug
Can't I change this to something like this?
/api/view/slug
Nevermind the first question - it was a bad idea as someone landing on a specific artwork page, would not have all the artworks cached to find one.
As for the second question, I had to use return this.store.find('Artwork', params.slug); instead.

EmberJS: Fetch a Single Attribute of a Model After the Model is Fetched

I have two models, a User with many Messages:
App.Router.map(function() {
this.resource('user', { path: ':user_id' }, function () {
this.route('profile', { path: 'profile' });
this.resource('messages');
});
});
App.User = DS.Model.extend({
displayName: DS.attr('string'),
email: DS.attr('string'),
firstName: DS.attr('string'),
lastName: DS.attr('string'),
location: DS.attr('string'),
messages: DS.hasMany('message')
});
App.Message = DS.Model.extend({
user: DS.belongsTo('user'),
createdAt: DS.attr('date'),
updatedAt: DS.attr('date'),
fullText: DS.attr('string'),
subject: DS.attr('string'),
recipients: DS.attr('string')
});
Currently, I must load the entire model, including all messages associated with the user, when I perform a search for users.
What I'd like to do instead is:
Retrieve the user
Retrieve the messages of the user (when the user is selected)
I cannot find an easy way to do this in Ember. My best guess is call find in the route, possibly:
App.UserRoute = Ember.Route.extend({
model: function (params) {
var user = this.store.find('user', params.user_id);
this.store.find('message', { user: 3 });
return user;
}
}
But this generates the url /messages?user_id=3. What I'd like instead is something like /users/3/messages.
How do I query for messages associated with a user?
I think { async: true } is what you're looking for. See http://discuss.emberjs.com/t/what-is-an-async-relationship-async-true-vs-async-false/4107 for some discussion on what it is, and how to use it.
You would probably want to setup your model like this:
App.User = DS.Model.extend({
displayName: DS.attr('string'),
email: DS.attr('string'),
firstName: DS.attr('string'),
lastName: DS.attr('string'),
location: DS.attr('string'),
messages: DS.hasMany('message', { async: true } )
});
Then when your Handlebars template makes a request for {{myUser.messages}} (or {{myUser.messages.someProperty}} Ember Data will look at the User model, and note that the message id's (say, 5, 10, and 12). It will then look in the local datastore for messages 5, 10, and 12. If it has them, it will display them. If they're not present, it will then use the Adapter you've defined for Message (or if no adapter is defined the default RESTAdapter) to fetch these using an HTTP GET.
I believe it will make one request for each id (versus one request for all of them), but that's something I'm not 100% sure about.

Ember Fixture Adapter not setting record ID

Okay, so I'm trying to copy an existing model as a new model instance (so all attributes are the same except for its ID. In my template, I have an action, copy, that passes the model in scope at that place in a list to the controller so it can be copied. My controller code is below. I appear to be able to create a new record, but its ID is set to 'fixture-0', 'fixture-1' etc., and calling .save() on it (see below) results in an error,
Uncaught TypeError: Cannot call method 'serialize' of undefined
As I'm currently stubbing the models for development, I'm using the fixture adapter, as it seems that's got something to do with the problem.
Controller code:
REApp.ApplicationController = Ember.ArrayController.extend({
actions: {
copy: function(current_model){
var self = this;
console.log(current_model);
var new_record = this.store.createRecord('cycle', {
railsId: null,
accountId: current_model._data.accountId,
//lots more attributes here
});
new_record.save();
console.log(new_record);
}
}
});
console.log call from the controller, showing new_record:
a {id: "fixture-0", store: a, container: t, currentState: Object, _changesToSync: Object…}
Note: even if you take out the .save() method call from the controller, this new record is created with the id set to a string. If .save() is not called (and thus no error raised in the controller), then the new record appears in the ember inspector's data panel with its ID set to a string as above.
This is only my third Ember app (and my first with 1.3.0), so open to the idea that I'm just doing it wrong.
Edit:
Here's the model definition. If it's relevant, I've had other errors returning both varOfModelObject.save(); and this.content.save(); from the controller since, so def thinking it's related to the FixtureAdapter.
REApp.Cycle = DS.Model.extend({
//id: DS.attr('number'), not needed with fixtures
railsId: DS.attr('number'),
siteId: DS.attr('number'), //all numbers here are ints unless noted
clientId: DS.attr('number'),
accountId: DS.attr('number'),
startAt: DS.attr('datetime'),
endAt: DS.attr('datetime'),
chargePerEvent: DS.attr('decimal', {defaultValue: 0.0}),
createdAt: DS.attr('datetime'),
updatedAt: DS.attr('datetime'),
scheduleYaml: DS.attr('string'),
allDay: DS.attr('boolean'),
repeat: DS.attr('boolean'),
exceptionDates: DS.attr('string'),
additionalDates: DS.attr('string'),
charge: DS.attr('number'), //decimal in rails
chargePeriod: DS.attr('string'),
oldRegularEventId: DS.attr('number'),
scheduleHuman: DS.attr('string'),
bagLimit: DS.attr('number'),
untilDate: DS.attr('number'),
sendToWebfleet: DS.attr('boolean', {defaultValue: false}),
contractId: DS.attr('number'),
hourlyRate: DS.attr('number'), //decimal in rails
hourlyCharge: DS.attr('number', {defaultValue: 0.0}), //decimal in rails
doubleEvent: DS.attr('number'),
//these are used for the simple view
weekdays: DS.attr('boolean', {defaultValue: null}),
weekends: DS.attr('boolean', {defaultValue: null}),
//data below this needed for associations
clientName: DS.attr('string'),
commentBody: DS.attr('string'),
siteAddress: DS.attr('string'),
carer1Id: DS.attr('number', {defaultValue: null}),
carer1Name: DS.attr('string'),
carer2Id: DS.attr('number', {defaultValue: null}),
carer2Name: DS.attr('string'),
users: DS.hasMany('user', {async: true}),
items: DS.hasMany('item', {async: true})
});
As mentioned in the comments above, when working with fixtures, you can create new objects in memory, but they are distinguished from the hard-coded fixture objects by a fixture- prefix in their object ID. As a result of this, you may find behaviour being a little screwy - perhaps this will be fixed in a newer version of Ember Data, but in the meantime the answer seems to be to get off using fixture data and move to a live data situation as quick as possible in your dev process.
try this and tell me what you get, yes in the console you gonna get an error yet it works, simply override the method createrecord as follows:
REApp.ApplicationAdapter = DS.FixtureAdapter.extend({
mockJSON: function(store, type, record) {
try {
record.id = Math.floor((Math.random() * 100) + 1);
return this._super(store, type, record);
}
catch(err) {
}
},
}
);

Infinite Loop in Ember-Data clearRelationships?

Inquiry:
Can anybody tell me why I might be getting an infinite loop when attempting to delete an Ember-Data Model. Stepping through the crash, the issue appears to be in clearRelationships, but no matter how minimal I attempt to make the Model relationships I can't seem to get away from the infinite loop, without avoiding them all together.
Relevant Code:
//// Config
App = Ember.Application.create();
App.Adapter = DS.RESTAdapter.extend();
App.store = DS.Store.create({
revision: 11,
adapter: App.Adapter.create()
});
App.Router = Ember.Router.extend({});
App.Router.map(function() {
this.resource('main', { path: '/' });
});
//// Models
App.Scientist = DS.Model.extend({
name: DS.attr('string'),
tests: DS.hasMany('App.Tests'),
criteria: DS.belongsTo('App.Criteria')
});
App.Test = DS.Model.extend({
name: DS.attr('string'),
scientist: DS.belongsTo('App.Scientist'),
criteria: DS.belongsTo('App.Criteria')
});
App.Criteria = DS.Model.extend({
name: DS.attr('string'),
scientist: DS.belongsTo('App.Scientist'),
test: DS.belongsTo('App.Test'),
resources: DS.hasMany('App.Resource')
});
App.Resource = DS.Model.extend({
name: DS.attr('string'),
criteria: DS.belongsTo('App.Criteria')
});
//// Pre-Load Models
App.store.loadMany(App.Test,
[{id:'1', scientist_id:'1', name:'gravity', criteria_id:'2'},
{id:'2', scientist_id:'1', name:'not gravity', criteria_id:'3'}]);
App.store.load(App.Scientist,
{id:'1', name:'Bill', tests:['1', '2'], criteria_id:'1'});
App.store.load(App.Criteria,
{id:'1', name:'control', scientist_id:'1', test_id:null, resources:['1']});
App.store.loadMany(App.Criteria,
[{id:'2', name:'variation1', scientist_id:null, test_id:'1', resources:['2','3']},
{id:'3', name:'variation2', scientist_id:null, test_id:'2', resources:['4','5']}]);
App.store.loadMany(App.Resource,
[{id:'1', name:'resource1', criteria_id:'1'},
{id:'2', name:'resource2', criteria_id:'2'},
{id:'3', name:'resource3', criteria_id:'2'},
{id:'4', name:'resource4', criteria_id:'3'},
{id:'5', name:'resource5', criteria_id:'3'}]);
var filter = App.Test.filter(
function(model) { if (model.get('isLoaded') === true) { return model; } }
);
App.MainRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('tests', filter);
}
});
///// Controller
App.MainController = Ember.ArrayController.extend({
name: 'Main',
createTest: function() {
App.Test.createRecord();
App.store.commit();
},
removeTest: function(test) {
test.deleteRecord();
App.store.commit();
}
});
Steps to Reproduce:
http://jsfiddle.net/hilem/GMt7H/
1) Open Browser Console.
2) Run the fiddle.
3) Watch console as you hit remove on one of the list items.
Update 1 (8:44PM 1/27/2013)
Additional Context: This is using ember-1.0.0-pre.4.js && the latest commit of ember-data on master as of 1/27/2013. I also added a bit more to the example code above.
Update 2 (5:25PM 2/1/2013)
Bump! Issue still exists, there seems to be quite a few issues involving deleteRecord lately on github issue tracker that sound similar to my issue. Here's a link to my specific issue on the tracker:
https://github.com/emberjs/data/issues/671
It's a bug. This pull request on Github fixed it for me. https://github.com/emberjs/data/pull/715
I was having a similar issue. I had models like these
App.User = DS.Model.extend({
account: DS.belongsTo('App.Account'),
name: DS.attr('string')
});
App.Account = DS.Model.extend({
user: DS.belongsTo('App.User'),
type: DS.attr('string')
});
The recursion isn't actually happening in clearRelationships but in this section of the ember.js set method
var meta = obj[META_KEY], desc = meta && meta.descs[keyName],
isUnknown, currentValue;
if (desc) {
desc.set(obj, keyName, value);
} else {
....
}
when I deleted the User it tried to clearRelationships which calls set(this, "account", null) which eventually gets to the section above which calls set again and again and again.
I'm not sure the right way to fix this but I got it to work in my situation by removing the user belongsTo relationship on the account. so my new models look like this
App.User = DS.Model.extend({
account: DS.belongsTo('App.Account'),
name: DS.attr('string')
});
App.Account = DS.Model.extend({
// user: DS.belongsTo('App.User'),
type: DS.attr('string')
});

Categories

Resources