I have this setup:
App.MyModel = Em.Model.extend({
someValue: DS.attr('string'),
parent : DS.belongsTo('mymodel',{async:true, inverse:'rooms'}),
rooms : DS.hasMany('mymodel', {async:true, inverse:'parent'})
});
App.MyRoute = Em.Route.extend({
model:function(params){
var parent = this.store.find('mymodel', params.parent_id);
return this.store.createRecord('mymodel',{parent:parent});
}
});
params.parent_id has the ID I want, so find() should return the right record.
Then someValue gets bound in a input box of a template and after input the action create gets called.
App.MyController = Ember.ObjectController.extend({
actions:{
create:function(){
this.get('model').save();
}
}
});
But when the data is send to the server, only someValue has the right data, parent is null.
I don't know if the error is in the model-definition or in the way I set the relationship.
How do I set relations on records correctly?
The promises of the find should resolve to the actual object.
But this didn't work when using it like in the question.
There is a different method, that can be used when the needed record has already been loaded.
model: function(params){
return this.store.createRecord('mymodel', {
'parent': this.store.getById('mymodel', params.parent_id)
});
}
This works, because route I came from already loaded the parent.
Related
I have a parent route (defined below) that attempts to return multiple models and is based on this discussion: http://discuss.emberjs.com/t/loading-multiple-models-in-a-single-route/5794/15
App.PropertyPricingRoute = Ember.Route.extend({
model: function(params){
return Ember.RSVP.hash({
property: this.store.find('property', { propertyId: params.propertyId }),
rooms: this.store.find('roomType', { propertyId: params.propertyId })
});
}
});
Then in a child route, I call modelFor() on the parent route, and try to access one of the two models. See below:
App.PropertyPricingUpchargesRoute = Ember.Route.extend({
model: function(){
var propertyId = this.modelFor('propertyPricing').property.get('id');
return this.store.find('upcharge', { propertyId: propertyId });
}
}
The problem is that propertyId reports as undefined because this.modelFor('propertyPricing').property instanceof App.Property returns false, meaning I can't get a property off of it as expected.
Not sure where things are going wrong here, or if there's a better practice for what I'm trying to do. Any help would be appreciated!
When you call find by query (which is find with an object as the parameter), Ember Data isn't sure how many results or if any will be returned, so it returns a collection. Usually if you are finding a particular record it would be this.find('property', params.propertyId).
With your current code though, assuming you are guaranteed to get a result back and it's just one you would need to grab the first record from the collection in order to get that id.
App.PropertyPricingUpchargesRoute = Ember.Route.extend({
model: function(){
var propertyId = this.modelFor('propertyPricing').get('property.firstObject.id');
return this.store.find('upcharge', { propertyId: propertyId });
}
}
I've been googling and scouring Stack Overflow for some sort of hint on this subject but the information is scattered at best.
I'm trying to Create a new Child Record (Comment) and save it to an existing Parent Record (Post). I am using Ember-Model, rather than Ember-Data, but any tips or pointers would be greatly appreciated.
At the moment, I've been successful creating a new, embedded Comment but only when it is created with a new Post record. So:
How do I go about loading/retrieving the currently loaded Post(parent record) in order to apply Comments (child records) to it?
I've been reading up on controller dependencies, using needs: and this.controllerFor and this.modelFor in order to have access to another controller/model's content but have been unable to wire these things together into something meaningful.
Anyway, here is what I've whittled my application code down to, in the hopes I might be able to stumble into the proper way of doing this...
Routes
App.Router.map(function() {
this.resource('post', { path: '/:post_id' }, function() {
this.resource('comments', { path: '/comments'} );
});
});
I removed all the other resources & routes, so I'm left with App.Post, App.PostIndex, and App.Comments. I think my routes are the issue here, I assume I'm not properly implementing the methods to use the loaded Post record in my Comments route.
App.IndexRoute = Ember.Route.extend({
model: function() {
return App.Post.find();
},
setupController: function(controller, model) { // I'm not certain if this
controller.set('content', model); // setupController is needed?
}
});
App.PostRoute = Ember.Route.extend({
model: function(params) {
return App.Post.find(params.post_id);
},
setupcontroller: function( controller, model) { // again, unsure if this
this.controllerFor('post').get('comments'); // is correct.
controller.set('content', comments);
}
});
App.CommentsRoute = Ember.Route.extend({
afterModel: function() {
this.set('post', this.modelFor('post'));
},
setupcontroller: function( controller, model) {
this.controllerFor('post').get('comments');
controller.set('content', comments);
}
});
Controller
App.CommentsController = Ember.ArrayController.extend({
needs: "post",
actions: {
addComment: function() {
var post = App.Post.create({
title: 'static post title'
});
post.get('comments').create({
message: 'static message'
});
post.save();
}
}
});
This is my current Comments Controller, which can create a new Post with an embedded Comment. I've found and been given numerous examples in which to create the Comment, but none seem to work for me. Basically, I'm struggling with defining the var post = ... as the currently loaded record. I've implemented various approaches in an attempt at trial & error. Thus far I have attempted:
var post = App.Post.create();, returns property undefined, as this would create a new record. However, I gave it a shot as every example i saw related to this defined their record as such.
var post = this.get('post');, returns a cannot call 'get' on undefined. I've tried using this method of defining my current post on both the Comments controller and Post controller.
var post = this.get('controllers.post.content);, returns a 'cyclic error' from the backend I'm using.
var post = App.Post.find();, returns a cannot call 'get' on undefined.
var post = App.Post.find(1);, Again, returns a cannot call 'get' on undefined. Figured I'd give it a shot because this is one of those recurring examples people provide. The backend I use applies its own ID to each record, and I'm unsure if I would be able to/how to have the .find() method use a dynamic ID value and retrieve only the model I just loaded.
I'm guessing that I'm not properly setting up my Routes and Controller dependencies?
If anyone has a suggestion, relevant link, or fix I would be very grateful.
This one (seemingly simple) issue/use case has me at wit's end at this point.
Try this (works pre beta 2):
App.CommentsController = Ember.ArrayController.extend({
actions: {
addComment: function() {
this.content.createRecord({
message: 'static message'
});
}
}
});
Ember Data Beta 2 and later:
App.CommentsController = Ember.ArrayController.extend({
needs: ["post"],
actions: {
addComment: function() {
var post = this.get('controllers.post');
var comment = this.get('store').createRecord('comment', {
message: 'static message',
post: post
});
comment.save().then(function() {
post.addObject(comment);
// You may or may not need to save your post, too. In my case my backend handles
// the inverses of relationships (if existing), so there's no need. We still need
// to do this for Ember, though
});
}
}
});
I'm using ember 1.0 and ember-data 1.0.0 beta 1. I have the following routes and controller to create and save simple notes ('AuthenticatedRoute' is just a custom made route for logged-in users):
App.Note = DS.Model.extend({
title: DS.attr(),
author: DS.attr(),
body: DS.attr(),
createdAt: DS.attr()
});
App.NotesRoute = App.AuthenticatedRoute.extend({
model: function() { return this.store.find('note'); },
});
App.NotesNewRoute = App.AuthenticatedRoute.extend({
model: function() {
return this.store.createRecord('note');
}
});
App.NotesNewController = Ember.ObjectController.extend({
actions: {
save: function() {
var self = this, model = this.get('model');
model.set('author', localStorage.username);
model.set('createdAt', new Date());
model.save().then(function() {
self.get('target.router').transitionTo('notes.index');
});
}
}
});
When I save a new note everything works as expected. But when I navigate away from the notes route and then back into it, the notes list is populated with a duplicate entry. One entry has an id and can be edited, deleted etc, the other has all the data of the first entry except the id attribute is null. It seems to me ember-data keeps the newly created record (that hasn't been committed to the database and thus has no id yet) alive even when the record becomes committed but I am uncertain as to why. When I reload the page, the list is correctly displayed, no duplicates appear. What am I doing wrong?
For the record, I am using mongodb so I use a custom serializer to convert '_id' attributes to ember-data friendly 'id's, essentially copied from here:
App.NoteSerializer = DS.RESTSerializer.extend({
normalize: function(type, hash, property) {
// normalize the '_id'
var json = { id: hash._id };
delete hash._id;
// normalize the underscored properties
for (var prop in hash) {
json[prop.camelize()] = hash[prop];
}
// delegate to any type-specific normalizations
return this._super(type, json, property);
}
});
I should also mention that this problem existed in ember-data 0.13 as well.
It was a stupid mistake in my RESTful server. I was responding to POST requests with a 204 (empty) response instead of what ember-data expected, that is a 201 ("created") response with the newly created record as the payload. This post made me realize it.
It would be nice though to include this information in the official REST adapter documentation.
That is certainly strange behaviour indeed. Unfortunately I'm not able to explain why you're experiencing this, however:
You can use the willTransition callback in the actions object in your Route to ensure that when it is transitioned away from, if NotesNewController's content property is dirty (i.e. has not been persisted yet), it will have its transaction rolled back.
App.NotesNewRoute = App.AuthenticatedRoute.extend({
model: function() {
return this.store.createRecord('note');
},
actions: {
willTransition: function (transition) {
var model = this.controllerFor('notesNew').get('content');
if (model.get('isDirty') === true) {
model.get('transaction').rollback();
}
return this._super(transition);
}
}
});
I have a route like this:
App.PeopleRoute = Ember.Route.extend({
model: function()
{
return App.Persons.find(personId);
}
});
where personId is loaded asynchronically and is a normal JavaScript variable outside Ember. Now when route is displayed it gets the current PersonId and displays proper data. But when i change the value of personId it does not update the view.
So my question is what is a way to refresh this route to find records with new personId?
This is because model hook is executed only when entered via URL for routes with dynamic segments. Read more about it here.
The easiest solution for this would be to use transitionTo.
App.PeopleRoute = Ember.Route.extend({
model: function(params)
{
return App.Persons.find(params.personId);
},
actions: {
personChanged: function(person){
this.transitionTo("people", person);
}
}
});
App.PeopleController = Em.Controller.extend({
observeID: function(){
this.send("personChanged");
}.observes("model.id");
});
Im trying to set a model for a nested route based on the parents array of models.
Msmapp.ClassroomStudentsRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('classroom').get('classroomStudents');
}
});
Msmapp.ClassroomStudentsStudentRoute = Ember.Route.extend({
model: function(params) {
var studentObjects = this.modelFor('classroom_students'),
studentObject = null;
studentObjects.forEach(function(student) {
if (student.get('id') == params.student_id) {
studentObject = student;
}
});
return studentObject;
}
});
Normally when navigating into this route its fine because im passing along the model, but if I try to copy/paste the url or refresh the page from this point where it has to hit the router i dont get anything.
If I console log studentObject.toString() i can see that its It appears the im returning an Msmapp.Student object so im not sure what im missing or may need to do to get this to work.
These are my routes
this.resource('classrooms', function() {
this.resource('classroom', {path: ':classroom_id'}, function() {
this.route('new_student');
this.resource('classroom_students', function() {
this.route('student', {path: ':student_id'})
});
this.route('new_assignment');
this.resource('classroom_assignments', function() {
this.route('assignment', {path: ':assignment_id'})
});
});
this.route('new');
});
and this initial request for the student objects comes back in the JSON for the classroom.
The classroomStudents is a computed property that loops the student objects and creates an Msmapp.Student object for each one.
Also if I change the route to be Msmapp.ClassroomStudent it works, but its making a request to the server for the object. Then i end up with 2 different instances of the same object, when all i really need is to be able to grab the object from the already setup array of objects.