Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ember.Select and belongsTo with async:true dosen't work #1405

Closed
rickard2 opened this issue Oct 2, 2013 · 16 comments
Closed

Ember.Select and belongsTo with async:true dosen't work #1405

rickard2 opened this issue Oct 2, 2013 · 16 comments

Comments

@rickard2
Copy link

rickard2 commented Oct 2, 2013

When you have a asynchronous belongsTo relationship and bind that to a Ember.Select it seems that the correct value is not selected in the view.

Example here: http://emberjs.jsbin.com/uzUNaQA/1/

Tested with Ember Data Canary as of today and Ember 1.0.0

@decasia
Copy link

decasia commented Oct 23, 2013

I believe this is a longstanding, known issue. See ember.js issue 1333 for discussion. They discuss some workarounds in the thread... I've switched to using non-async options for select boxes when possible, to avoid the issue altogether.

@ugomeda
Copy link

ugomeda commented Nov 5, 2013

I'm having the same issue as you, they describe it as "not a bug" in emberjs/ember.js#1333 but it's actually a pain.

It will also switch your select to an empty mode if you call model.save() since Ember Data will set your fields to a promise.

Tested with Ember 1.1.2 and ember data 1.0.0-beta3

@marcioj
Copy link
Contributor

marcioj commented Dec 2, 2013

@rickard2 your problem is that item.author returns a PromiseObject instead of the record, when the PromiseObject is resolved, it set a record instance in the content property. So you can update your code to use item.author.content and will works.

Here is your updated jsbin http://emberjs.jsbin.com/uzUNaQA/6/edit

@rickard2
Copy link
Author

rickard2 commented Dec 3, 2013

@marcioj that seems to solve the problem, still getting used to ember-data 1.0

thanks!

@rickard2 rickard2 closed this as completed Dec 3, 2013
@stefanpenner
Copy link
Member

@marcioj & @rickard2 this is an Ember.select bug that we should fix. Please check embers issue list, if no similar issue exists please open one. That being said, I am pretty sure an issue such as this is already open.

@WMeldon
Copy link

WMeldon commented Jan 22, 2014

There is a subtle issue with @marcioj solution to this one that I've tried to highlight: http://emberjs.jsbin.com/uzUNaQA/20/edit?html,js,output

If you do this with a fresh object that doesn't have the belongs to explicitly set to null, accessing the content property isn't going to do you much good.

If you do set the author to null when you instantiate the class, it goes through the correct setters and gives you an object with the content property of null.

The real issue comes in when you load an existing model with a null belongsTo because you can't reasonably force it though the setters and are therefore unable to manipulate the relationship. This isn't really a problem with the select or Ember Data (at least not a simple one), but it is a bit of an unintuitive incompatibility.

You can get around this by binding the properties to the controller and then manually setting them on the object, but it's not my favorite approach and I'd love to hear some alternatives.

@karankurani
Copy link

I believe I have a similar issue here - http://stackoverflow.com/questions/22799094/setting-a-belongsto-attribute-via-an-action-on-controller-not-working/22799776?noredirect=1#comment34785844_22799776

Looking at the answer of this post, just having a selectionBinding attached to a belongsTo (async) of a model doesn't render the selection correctly.

I might be wrong in this so wanted to have a second opinion on it.

@neverfox
Copy link

neverfox commented May 2, 2014

My solution for the time being is to do something like this on my controller:

  # I have an ArrayController dedicated to the list
  needs: ["selections"]
  selections: Ember.computed.alias "controllers.selections"
  selectedObject: null # so it doesn't get proxied to the model
  objectSelected: (->
    @set "myModelProperty", @get "selectedObject"
  ).observes "selectedObject"

I have selection=selectedObject on the Select. And in the route:

  setupController: (controller, model) ->
    @_super controller, model
    @store.findAll("selections").then (selections) =>
      @controllerFor("selections").set "model", selections
      # There needs to be a selection made even if the user doesn't touch the element
      controller.set "selectedModel", investmentModels.get "firstObject"

@WMeldon
Copy link

WMeldon commented May 6, 2014

Alright, I've made some progress on the workarounds to get this situation to work. I'm sure there are better ways to do this, but this is (so far) the only one I've found to cover all the problems without excessive manual intervention.

For existing records

For existing records, you need to do something like the following in your route:

// your-route
beforeModel: function() {
  var self = this;
  return Em.RSVP.hash({
    firstBelongsTo: this.store.find('first-belongs-to'),
    secondBelongsTo: this.store.find('second-belongs-to')
    }).then(function (models) {
      self.controllerFor('this-route').setProperties(models);
  });

And in your controller, be sure to declare the properties before setting them as Ember tries to throw then into content when they don't exist:

// your-controller
App.MyController = Ember.Controller.extend({
  firstBelongsTo: null,
  secondBelongsTo: null
});

By returning a promise in the beforeModel hook, you are telling the route to resolve the promise BEFORE loading the model, which also mean before any rendering occurs. This gives your application time to load the data up front before binding it to the select boxes.

For NEW records.

New records present an unexpected quirk in this situation because the belongsTo relationships aren't null, they are undefined. This (quietly) blows up the select boxes.

To solve this, I'm currently manually setting all of the relationships to null in the model hook:

// your-route
model: function() {
  this.store.createRecord('your-record', { 
    firstbelongsTo: null, 
    secondBelongsTo: null
});

Not ideal, but by their powers combined, you can use async belongTo without tossing weird proxy properties in your controller.

There is undoubtably more to this issue, but this seems to have drastically improved the situation for me.

@bmac
Copy link
Member

bmac commented May 7, 2014

Thanks for this @WMeldon. I ran into this problem recently and your comments helped me figure out what I was doing wrong.

@leejt489
Copy link

Has this been fixed?

@johnnyoshika
Copy link

I'm also curious to know if this is something that will be fixed soon. Thanks.

@gopeter
Copy link

gopeter commented Oct 23, 2014

Any news? :) 👍

@stefanpenner
Copy link
Member

use relation.content as the property path and all will work fine, ember select needs to be aware of async relationships, currently it doesn't monitor the change events correctly.

This is not an ember-data issue.

@gopeter
Copy link

gopeter commented Oct 24, 2014

I don't know if this is the right place, so I posted my question at Stackoverflow also: http://stackoverflow.com/questions/26543093/how-to-preselect-a-value-on-an-ember-select-view


Thanks for your help. My select works now, but I don't know how to preselect a value dynamically - or isn't it possible at the moment?

This is my select:

{{view select
  class="uk-width-1-1"
  content=services
  optionLabelPath="content.name"
  optionValuePath="content.id"
  prompt="Service"
  selectionBinding="selectedService"
}}

It works fine when I try to get the current active value: this.get('selectedService'), but when I try to set a specific customer, nothing happens:

var service = timetracking.get('service');
this.set('selectedService', service);

These are my models:

App.Timetracking = DS.Model.extend({
  duration:  DS.attr('number'),
  day:       DS.attr('date'),
  notice:    DS.attr('string'),
  project:   DS.belongsTo('project', {async: true}),
  service:   DS.belongsTo('service', {async: true}),
  user:      DS.belongsTo('user', {async: true})
});

App.Service = DS.Model.extend({
  name:           DS.attr('string'),
  description:    DS.attr('string'),
  timetrackings:  DS.hasMany('timetracking', {async: true}),
  archived:       DS.attr('boolean')
});

@ahacking
Copy link
Contributor

Thanks @stefanpenner !

I found using relation.content works well with an alias on your controllers:

export default Ember.ObjectController.extend({
  relation: Ember.computed.alias('content.relation.content')
});

Be aware the relation.content trick will not work correctly if you are using a buffered proxy on your controllers since the extra .content key path segment will effectively go via the proxy buffer to the underlying promise object content thus mutating the model directly. I was able to work around this issue with a computed property helper that uses the buffer value if its been set on the controller, and otherwise obtains the value from relation.content.

As a result when using Ember.Select in my templates I don't have to worry about async belongsTo relationships, they can just use selection=relation until Ember.Select is taught to handle promise proxies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests