3

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('model.@each.comments'),
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!

Blackening
  • 84
  • 2
  • 10
  • I guess the second variant is ok. You can check in this link https://github.com/emberjs/data/blob/master/TRANSITION.md#transaction-is-gone-save-individual-records Saving individual records, basically it's the same way by passing the userId. Hope it helps – Altrim Jul 10 '14 at 20:14
  • WoW! It has been eye opener for me... however I need to check it. Will write my feedback soon. Thanks! – Blackening Jul 10 '14 at 20:59
  • However, it doesn't solve the problem with updating 2 related models at a time – Blackening Jul 13 '14 at 11:48
  • 1
    For embedding objects, use [`DS.EmbeddedRecordsMixin`](http://www.toptal.com/emberjs/a-thorough-guide-to-ember-data#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 Jul 13 '14 at 19:31
  • 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 Aug 28 '14 at 08:37
  • `"it doesn't include saving the comment in the post.get("comments") and user.get("comments") properties."` - this could be a lot of things, from promises to your serializer, to how your server returns data, to whether your array is live or not (automatically updates or is static). Your first model looks correct but stop at `comment.save()` on the line before `comment.save()` try `post.pushObject(comment);` to see if that updates the post.get('comments') array. – Craicerjack Aug 28 '14 at 08:42
  • See update to my answer below. Question: did your `user.get('comments')` and `post.get('comments')` update if you refreshed the page? – Craicerjack Aug 28 '14 at 08:48
  • Bidirectional relation sync is an Ember Data issue, this [GitHub issue](https://github.com/emberjs/data/issues/1308) is a good starting point for it, I'm not sure what is the current status. – snovity Dec 29 '14 at 14:57

2 Answers2

2

@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.

Community
  • 1
  • 1
Blackening
  • 84
  • 2
  • 10
0
/* 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== */
Craicerjack
  • 5,524
  • 2
  • 27
  • 36
  • Thanks for answer, however I have doubts about this approach: 1) You proposing to save comment model twice (may be mistyping), it leads to :TOO MANY REDIRECTIONS: error; 2) The general: comment.set('user', user), there's no updating of relevant model. After saving comment model you should also save model that relates by relationship with it - User and Post models – Blackening Jul 23 '14 at 15:06
  • Would this work? setting the post within the comment and then afterwards adding the user relationship? answer edited above – Craicerjack Jul 24 '14 at 14:13
  • It doesn't change anything ! :) You will save the Comment with relation to User and Post, but the User and Post haven't got the information about Comment ! – Blackening Jul 25 '14 at 15:20
  • Once you save the comment it has a relationship and should be accessible by that relationship. `var user_id = this.get('user').id; var userComments = this.get('comments').filterBy('user', user_id);` – Craicerjack Jul 25 '14 at 19:20
  • What are you trying to do exactly? It seems the solution for this should be something 'relatively' simple. – Craicerjack Jul 25 '14 at 19:21
  • Once you save the comment it has a relationship and should be accessible by that relationship - false. That's why I've created this topic. I mean, when I save Comment model, I can access everything via this model, however, I need to have an access from other related models: from User and from Post – Blackening Jul 26 '14 at 10:10
  • Are you making all the models you need available? The models have a relationship but you still have to make them available in your controller to access them. There's a post here about loading multiple models in one route using the user/post/comments example similar to yours http://stackoverflow.com/questions/16463958/how-to-use-multiple-models-with-a-single-route-in-emberjs-ember-data – Craicerjack Jul 26 '14 at 10:23
  • Craicerjack, man, it's useless info... and it is off topic. Of course I've added other models via route 'setupController' method. The problem is that there's exists an impossibility of updating more than 1 related model in bi-directional mode – Blackening Jul 27 '14 at 10:51
  • Im not really understanding where the problem comes in. Updating the comments, should update the relationship to the other models... Sorry I couldnt be of any help man. – Craicerjack Jul 27 '14 at 11:33
  • Thanks for giving a try anyway ;) "should update the relationship to the other models" - but it doesn't :( and it's sad – Blackening Jul 27 '14 at 17:38
  • good tutorial on ember data here - http://www.toptal.com/emberjs/a-thorough-guide-to-ember-data#. – Craicerjack Aug 02 '14 at 18:54