cibernox / ember-power-select

The extensible select component built for ember.
http://www.ember-power-select.com
Other
540 stars 377 forks source link

Stale selection and options when options are changed #208

Closed amageed closed 8 years ago

amageed commented 8 years ago

I'm seeing this buggy behavior where options and selections are stale when options change. I'm not sure if there's a way to immediately clear the displayed options (and currently selected item) when options are changed, and set selected to null.

I've tested this issue with version 0.7.0.

I have a scenario where selecting an option in one dropdown loads options for a 2nd dropdown. The problem is if a selection is made in the 2nd dropdown, and then the user changes the 1st dropdown selection, the following can be observed in the 2nd dropdown:

The behavior I am expecting is once the dropdown's data is being loaded ("Loading options...") that the displayed selection and options are cleared. The selected value should no longer point to a stale option, and should probably be set to null.

Repro steps

To see this behavior in action, refer to "The loading state" example from the docs here: http://www.ember-power-select.com/docs/the-list

In that example, follow these steps:

  1. Click "Refresh collection"
  2. Select an item
  3. Click "Refresh collection"
  4. Observe the item selected in step 2 is still visible
  5. Quickly open the options and notice the "Loading options..." is visible, along with stale options

image

In this example, after the promise resolves, the item selected gets re-selected since the data matches. In my scenario that data will be different, but I still have a stale selection.

To further illustrate the issue, use the same code from the above example, but add a count and change generatePromise to the following:

let count = 0;
function generatePromise() {
  count++;
  let values = count % 2 === 0 ? ['one', 'two', 'three'] : [1, 2, 3];
  return new Ember.RSVP.Promise((resolve) => {
    setTimeout(() => resolve(values), 5000);
  });
}

In this example, the initial options are numeric. At step 3, when new data is coming in, you can see the stale numeric options are still there and selectable:

Repeat the steps above, and you will end up with a stale numeric selection, with new options as words.

image

cibernox commented 8 years ago

Let's clarify the expected behaviour, as I designed the component:

In you case, if the selection of the select number 1 has to replace the options in the select number 2 and you want to nullify the selection in that component too, that's something I'd do in the onchange action.

{{#power-select options=categories selected=category onchange=(action "switchCategory") as |categ|}}
  {{categ.name}}
{{{/power-select}}

{{#power-select options=subcategories selected=subcategory onchange=(action (mut subcategory)) as |subcateg|}}
  {{subcateg.name}}
{{{/power-select}}
actions: {
  switchCategory(category) {
    this.set('category', category);
    this.set('subcategories', this.get('store').query('subcategory', { parent: category.id });
    this.set('subcategory', null);
  }
}

Makes sense?

amageed commented 8 years ago

@cibernox thanks for clarifying. Yes, it makes sense that the selected item is always controlled by the user, so I agree with you there. That answers the stale select issue, and I am fine setting it to null. In fact that's what I am doing in my real app, but I figured I'd ask about it as part of this overall scenario in case it made sense for it to be reset if the entire options get swapped out.

However, there's still the question about stale options while new options are being loaded. Is there a remedy for clearing that and just having "Options Loading..." appear? I tried playing around with setting subcategories to null and then loading them in from the store, but that didn't change anything. Probably since the last thing being set is the promise of the store query, so I am back to my original scenario.

I was trying to avoid completely disabling the dropdown until the options were loaded since it was pretty cool to see "Options Loading..." which served a similar indication to the user.

Ultimately, if that's by design and I can't clear the options, setting selected to null is good enough since I can add validation around the selected values.

Thanks!

cibernox commented 8 years ago

You're right, when replacing the options with a promise the default behaviour is to keep the previous results around while the promise is not resolved.

This is this way because the usual pattern is to fire the search action and from that action generate a a new set of results. When you are "refining" a search you don't want the list of options to be cleared, but in this case since you're swapping options completely from another source of data that is not what you want.

I don't see an easy way to prevent this default behaviour, but perhaps we could use the label of the promise to check if this promise "belongs to the same collection" or not. If the label of the promise replacing the previous one is different means that is a different data source. I know that RSVP promises have labels, but I'm not sure if this also part of the native promises spec.

Idea:

return new Ember.RSVP.Promise(function(resolve, reject) {
  // ...
}, 'origin:categories'); // setting the label to something different that "other:categories" will not reuse existing results.

What do you think about that? It keeps the API unchanged.

amageed commented 8 years ago

From what I can tell it seems label is an RSVP extra (beyond the spec so far), but it could be a creative way to handle it.

cibernox commented 8 years ago

Closing this issue for being very old. I think this can be tackled with the existing primitives.

zardaloop commented 7 years ago

@cibernox I know this issue is closed but could you tell me please what is the solution to this ? I want to reset the selected Item when options are changed and the selected item is no longer within the options.

cibernox commented 7 years ago

@zardaloop Basically it's something the programmer must do. Ember Power Select just receives options=someOptions selected=something and display them. Nothing more. If you replace someOptions with some other value, wherever you do that you should also clear something if now it is not a valid selection.

zardaloop commented 7 years ago

@cibernox thanks for getting back to me so quick. perfect, I guess I need to extend the component and create a new one which has observer on options and when it changes goes ahed and reset the component's selected value. However is there anyway to set back the placeholder? what would be the clear function? I had a look at the documentation's API but I couldn't figure out what I should use to clear the selected item and reset it back to the placeholder text.

cibernox commented 7 years ago

@zardaloop Is this kind of behaviour something you will have very often? If your don't or you don't know yet, I'd start by doing wherever wherever you update the list of options.

If you have this, perhaps the simples solution I see would be to create a generic tagless component that does that:

{{#selected-options-validator options=myOptions selected=mySelection as |validatedSelection|}}
  {{#power-select options=myOptions selected=validatedSelection as |opt|}}
    {{opt}}
  {{/power-select}}
{{/selected-options-validator}}

That way you don't have to extend the select component. The logic that resets the selected option can be in this wrapper component. This might be my initial approach, as it doesn't require you to extend the select in any form.

zardaloop commented 7 years ago

@cibernox thanks. But that way I am resetting selected value twice which is no longer allowed in ember 2.xx.

k-dauda commented 7 years ago

I am having the same problem and was wondering if this will fix the problem if you changed this line https://github.com/cibernox/ember-power-select/blob/642f9397b29beffc8db00a15a9ba3cd8cc3b96bf/addon/components/power-select.js#L432

to: this.updateOptions([]); this.updateState({ loading: true });

cibernox commented 7 years ago

@k-dauda @zardaloop I finally could dedicate some time to this today.

Once #990 is merged, this twiddle should work as you want: https://ember-twiddle.com/574971bab5e77c125656b596d45691e7?openFiles=controllers.application.js%2C

cibernox commented 7 years ago

Published in 1.9.9