3
votes

I'm learning Ember Data now and I've experienced one issue that I cannot resolve without writing ugly code (this my own project so I have a time to do my best). The problem is: I have a 3 models: Post, User and Comment. Code will describe better what I mean ;)

/* Post Model */

    Blog.Post = DS.Model.extend({
      title: DS.attr('string'),
      text: DS.attr('string'),
      postedAt: DS.attr('string', { defaultValue: new Date() }),
      comments: DS.hasMany("comment", { async: true })
    });

/* ==Post Model== */

/* User Model */

    Blog.User = DS.Model.extend({
      name: DS.attr('string'),
      avatar: DS.attr('string'),
      comments: DS.hasMany("comment", { async: true })
    });

/* ==User Model== */

/* Comment Model */

    Blog.Comment = DS.Model.extend({
        title: DS.attr("string"),
        text: DS.attr("string"),
        user: DS.belongsTo("user", { async: true }),
        post: DS.belongsTo("post", { async: true }),
        postedAt: DS.attr("date", { defaultValue: new Date() })
    });

/* ==Comment Model== */

/* Post Controller (bad variant and it doesn't work - too many redirects error) */

    leaveComment: function () {
        var controller = this, 
            user = controller.get('user'), 
            post = controller.get('model');
        var comment = this.get('store').createRecord('comment', {
              title: controller.get('commentTitle'),
              text:  controller.get('commentText'),
              user:  user,
              post:  post
            });
        comment.save().then(function (res) {
                controller.setProperties({
                    commentTitle: "",
                    commentText : ""
                });
                user.get("comments").then(function (comments) {
                    comments.pushObject(comment);
                    user.save().then(function (ures) {
                        console.log(ures);
                        post.get("comments").then(function (comments) {
                            comments.pushObject(comment);
                            post.save().then(function (pres) {
                                console.log(pres)
                            }, function (err) {
                                console.log(err);
                            });
                        });
                    }, function (err) {
                        console.log(err);
                    })
                });



            }, function (err) {
                console.log(err);
            });

/* ==Post Controller== */

/* Post Route */

Blog.PostRoute = Ember.Route.extend({
    setupController: function (controller, model) {
        this._super(controller, model);
        controller.setProperties({
            user: this.modelFor('application')
        });
    },
    model: function(params) {
        return this.store.find('post', params["post_id"]);
    }
});

/* ==Post Route== */

/* 2nd variant */

   /* Post Controller */
        leaveComment: function () {
            var controller = this, 
            user = controller.get('user'), 
            post =   controller.get('model');
            var comment = this.get('store').createRecord('comment', {
                title: controller.get('commentTitle'),
                text:  controller.get('commentText'),
                user:  user,
                post:  post
            });
            comment.save().then(function (res) {}, function(err) { console.log(err) });

    /* ==Post Controller== */

   /* Application.js */

      Blog.CommentSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
        primaryKey: function() {
            return '_id';
        }.property(),
        attrs: {
            user: { embedded: 'always' },//{serialize: 'id', deserialize: 'id'},
            post: { embedded: 'always' }//{serialize: 'id', deserialize: 'id'}
        }
      });

   /* ==Application.js== */

With the second variant, I'm having user/post id's instead of objects in the preview data when I'm perform saving - may be I don't understand right how to use it. Please, help me with this if you had similar experience.

Updated

When I'm making request using first variant - ERR:Net Error - too many redirect

When I'm making the request using the second variant:

1) It seems that DS.ActiveModelSerializer doesn't work at all 2) In the request there user_id and post_id, instead of embedded objects, actually it doesn't mean for me - to send embedded object or id, only thing about I care is to updated related models. consider the screenshot for better understanding Request using 2nd variant

And of course, after such saving it doesn't include saving the comment in the post.get("comments") and user.get("comments") properties.

P.S. I'm also have searched google for a very long time and haven't got response on my question, so please, do not answer if you aren't confident in your answer - thanks for understanding

==============================================================================================

After several hours of suffering, I've come up with this:

//Controller
comments: function () {
    return this.get('model.comments')
}.property('[email protected]'),
post: function () {
    return this.get('model');
}.property('post'),
user: function () {
    return this.get('user');
}.property('user'),
actions: {
  leaveComment: function () {
        var controller = this, 
        user = controller.get('user'), 
        post = controller.get('post');
        var comment = this.get('store').createRecord('comment', {
            text: controller.get('commentText'),
            user: user,
            post: post,
            postedAt: new Date()
        });
        comment.save().then(function (comment_res) {
            controller.get("comments").pushObject(comment_res);
            post.save().then(function (post_res) {
                controller.set("commentText", "");
            }, function (err) {
                console.log(err);
            });
        }, function (err) {
            console.log(err);
        });
    }
 }

    Blog.CommentSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
      primaryKey: function () {
        return '_id';
      }.property()//,
      //attrs: {//{ embedded: 'always' },
      //  user: { embedded: 'always' }, //{serialize: 'id', deserialize: 'id'},
      //  post: { embedded: 'always' } //{serialize: 'id', deserialize: 'id'}
      //} // <-this doesn't work :\ 
    });

    Blog.PostSerializer = DS.ActiveModelSerializer.extend({
      primaryKey: function () {
        return '_id';
      }.property(),
      serializeHasMany: function(record, json, relationship) {
        var key = relationship.key;
        var json_key = key.singularize().decamelize() + '_ids';

        var relationshipType = DS.RelationshipChange.determineRelationshipType(
          record.constructor, relationship);

        if (relationshipType === 'manyToNone' 
          || relationshipType === 'manyToMany' 
          ||  relationshipType === 'manyToOne') {
            json[json_key] = Ember.get(record, key).mapBy('id');
        }
     }// <- And this really works!

As you can see I'm updating only 1 related model - Post, I can't update User model due to the :Too-many redirections: error(I have bi-directional link between Post and Comment, and unidirectional link with User (Comment belongsTo User), I can't take comments that have User in the comments property). So far it's impossible. Also, I'm really saddened about DS.EmbeddedRecordMixin :( I won't close this topic until finding the answer on my question, however - thanks for helping!

2
I guess the second variant is ok. You can check in this link github.com/emberjs/data/blob/master/… Saving individual records, basically it's the same way by passing the userId. Hope it helps - Altrim
WoW! It has been eye opener for me... however I need to check it. Will write my feedback soon. Thanks! - Blackening
However, it doesn't solve the problem with updating 2 related models at a time - Blackening
For embedding objects, use DS.EmbeddedRecordsMixin. I think using default Ember Data behaviour is more desirable generally, consider updating your models with only IDs and if it didn't work then use DS.EmbeddedRecordsMixin. - Pooyan Khosravi
Ive been doing ember work since I last commented so thought I'd comment with that new knowledge. you say "With the second variant, I'm having user/post id's instead of objects in the preview data when I'm perform saving" - this is correct. hasMany and belongs to are saved as ids on the other model. Comments have a user id and that is how they are related to the user. etc. You dont save the comment to the user, your just creating three times the overhead, saving the comment, saving the comment to the user, and to the post. Instead you link it via an id. Youre screenshot is how it should be. - Craicerjack

2 Answers

2
votes

@Craicerjack,

Well, it might be a little bit confusing - my way of explanation such things :)

I'll try make it easier: We have 3 models: Post, User and Comment. Post has many Comment(s), User has many Comment(s) and Comment belongs to Post, Comment belongs to User. This is what Model configuration describes, easy enough but important to understand it clearly.

Now, hard (for me) part: we're going to post comment! Creating record, and comment.save() produces ajax call (post type) to the server. Our comment object contains commentText, createdAt and "links/references/relationships" to relative models - post_id and user_id -

comment.save().then(function(responseFromSavedComment) { 
    // we will insert next steps here... 
}); 

This is fine. Now we need to update our Post model and User model; we've just added Comment, remember? Ok! First we will update Post; we need to extract post.get('comments') which returns Promise, and then push and object to its response, right?

post.get('comments').then(function(postComments) {
    postComments.pushObject(responseFromSavedComment);
}); 

We just pushing recently created comment to the post in the scope of promise resolving when we've posted the comment itself. Clear? Perfect!

The last thing we need to complete our Post updating is to notify server about this, we just doing post.save()! :) That's all!

But, if we want to update the User model in the same way we will encounter :TOO_MANY_REDIRECTS: error, that's all... We can't do this in the scope(time) of adding comment...

Updated 01.01.2015

I found the solution - not to use embedded records :) The server will get keys and do everything. This is issue is impossible for now to be processed fully on client-side.

0
votes
/* Post Controller  */

leaveComment: function () {
    var user = this.get('user');
    var post = this.get('post');
    var comment = this.store.createRecord('comment', {
        title: this.get('commentTitle'),
        text:  this.get('commentText'),
        user:  user,
        post:  post
    });
    this.get('model').pushObject(comment);
    comment.save();
    };

/* ==Post Controller== */