Karan
Karan

Reputation: 434

Setting a belongsTo attribute via an action on controller not working

I have the following models, customer:

App.Customer = DS.Model.extend({
    //Other Attributes
    area: DS.belongsTo('area', {async: true})
});

and area model -

App.Area = DS.Model.extend({
    name: DS.attr('string'),
    zip_code: DS.attr('number')
});

And I use an Ember.Select view to show a dropdown of the area a customer is in -

{{view Ember.Select viewName="select_area" content=possible_areas optionValuePath="content.id" optionLabelPath="content.name" value=area.id prompt="No area selected" selectionBinding="selected_area"}}

and the controller which wires up everything together -

App.CustomerController = Ember.ObjectController.extend({
    possible_areas: function() {
        return this.store.find('area');
    }.property(),

    selected_area: null,
    actions: {
        save: function() {
            var selected_area_id = this.get('selected_area.id');
            console.log(selected_area_id);
            var selected_area = this.store.find('area', selected_area_id);
            var self = this;
            selected_area.then(function(area) {
                console.log(area.get('id'));
                var updated_customer = self.get('model');
                updated_customer.set('area', area );
                console.log(new_customer.get('area.id'));
                updated_customer.save()
                    .then(function(customer) {
                        //success
                    },
                    function() {
                        //failure
                    });
            });
        }
    }
});

Now here is the weird thing. Upon calling the 'save' action the first time, the line updated_customer.set('area', area ); fails with the error

Uncaught Error: Assertion Failed: Cannot delegate set('id', ) to the 'content' property of object proxy <DS.PromiseObject:ember551>: its 'content' is undefined.

Upon calling 'save' action immediately after that, the save goes through without any error and area of the customer is updated successfully. Although the dropdown shows selected_area to be null.

How do I prevent the first save from erroring out?

I am using ember-data 1.0.0-beta.6.

Upvotes: 2

Views: 1034

Answers (1)

Feech
Feech

Reputation: 4102

Since you have the association defined in your Customer model, I would remove the selected_area property from the controller and use ember-data's associations instead. Bind to the "area" association in the Ember.Select by using the selectionBinding property.

{{view Ember.Select viewName="select_area"
                    content=possible_areas
                    optionValuePath="content.id"
                    optionLabelPath="content.name"
                    prompt="No area selected"
                    selectionBinding="area"}}

This way, the area attribute will change when the user interacts with the select menu.

This has the added benefit of cleaning up your save action since we're binding directly to the area association for the Customer.

App.CustomerController = Ember.ObjectController.extend({
  possible_areas: function() {
    return this.store.find('area');
  },

  actions: {
    save: function() {
      this.get('model').save().then(this.onDidCreate.bind(this), this.onDidFail.bind(this))      
    }

    onDidCreate: function() {
      // Fullfilled callback
    }

    onDidFail: function() {
      // Rejected callback
    }
  }
});

However, the possible_areas property won't be populated when the template first renders since this.get('area') returns a promise. I would wait to render the select until the promise settles. You can do this in the routes model hook since it waits until promises settle to render the template.

App.CustomerRoute = Ember.Route.extend({
  route: function(params) {
    return Ember.RSVP.hash({
      customer: this.store.find('customer', params.customer_id),
      possible_areas: this.store.find('area')
    });
  },

  // Since the route hook returns a hash of (hopefully) settled promises, we
  // have to set the model property here, as well as the possible_areas property.
  setupController: function(controller, model) {
    controller.set('model', model.customer);
    controller.set('possible_areas', model.possible_areas);
  }
});

Upvotes: 2

Related Questions