One to many relationship cannot access parent model


#1

I have a parent data store (user) with a one-to-many relationship with children data stores (channels).

When I accept the create channel dialog and create a new channel record. it is not hooked up to the parent user.

Can someone please help me with this?

/** ROUTER **/

App.Router.map(function(){
  this.resource('users', function(){
    this.route('create');
    this.resource('user', { path: '/:user_id' }, function(){
        this.route('edit');
        this.resource('channels', function(){
            this.route('create');
            this.resource('channel', { path: ':channel_id' }, function() {
                this.route('edit');
            });
        });
    });
  });
});

/** MODELS **/

App.User = DS.Model.extend({
    name: DS.attr('string'),
    channels: DS.hasMany('channel', {async: true})
});

App.Channel = DS.Model.extend({
    name: DS.attr('string'),
    user: DS.belongsTo('user', {async: true})
});

/** TEMPLATE **/

<script type="text/x-handlebars" data-template-name="channels/create">
    <div class="row">
        <div class="col-md-10">
            <h1>Channel :: New</h1>
            <div class="list-group">
                <form class="form-horizontal" role="form">
                    <div class="list-group-item">
                        <div class="form-group">
                            <label class="col-sm-2 control-label">Name</label>
                            <div class="col-sm-10">
                                {{view "select" class="form-control" content=channelList value=channelName}}
                            </div>
                        </div>
                        <div class="form-group">
                            <label class="col-sm-2 control-label">Status</label>
                            <div class="col-sm-10">
                                {{view "select" class="form-control" content=statusList value=channelStatus}}
                            </div>
                        </div>
                        <div class="form-group">
                            <label class="col-sm-2 control-label">Message</label>
                            <div class="col-sm-10">
                                {{input class="form-control" value=channelMessage}}
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-sm-offset-2 col-sm-10">
                                <button type="button" class="btn btn-primary tbtn-sm" {{action "save"}}>
                                <span class="glyphicon glyphicon-ok"></span> Ok
                                </button>
                                <a href="#" {{action "cancel"}}>Cancel</a>
                            </div>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
</script>

/** CONTROLLER **/

App.ChannelsCreateController = Ember.ObjectController.extend({
    channelList: ['xmpp','spreed','voip','skype','facebook','google+','whatsapp'],
    statusList : ['online','available','away','busy','blocked','offline','green','yellow','red','grey','unknown'],

    channelName: '',
    channelStatus: '',
    channelMessage: '',

    actions: {
        save: function(){
            var id      = 0,
                name    = this.get('channelName'),
                status  = this.get('channelStatus'),
                message = this.get('channelMessage'),
                model   = this.get('model');

            if (typeof(name) == 'undefined') {
                alert('Please select a channel name from the list.');
                return false;
            }

            if (typeof(status) == 'undefined') {
                alert('Please select a channel status from the list.');
                return false;
            }

            // Get the next highest id, TODO: must be a better way to do this.
            model.forEach(function(item){
                var id_next = item.get('id');
                if (id_next > id) {
                   id = id_next;
                }
            });
            id++;

            var channel = this.store.createRecord('channel', {
                id: id,
                name: name,
                status: status,
                message: message
            });

            // PUT => /users/user_id/channel/create
            channel.save();

            // Clear out the values for next time
            this.set('channelName', '');
            this.set('channelStatus', '');
            this.set('channelMessage', '');

            this.transitionToRoute('users');
        },
        cancel: function(){
        }
    }
});

#2

As from my experience, you should either manually set the user_id inside your channel model or the backend is going to that work for you, communicating it back to you in the PUT response.

Aside question: with is your channel.save() call generating a PUT to /users/user_id/channel/create ? I would expect the URL to be a POST to /channels. How do you achieve that behavior with EmberData?


#3

Thanks. I ended up solving the problem by including a needs: ['user'] in the channel controller, and then doing the following:

actions: {
    save: function() {
        var user  = this.get('controllers.user.content');
        ...
        var channel = this.store.createRecord('channel', {...});
        channel.save();
        user.get('channels').pushObject(channel);
        user.save();
        this.transitionToRoute('user', user.get('id'));
    },
    ...
}