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

Label text doesn't change if options updated #39

Closed
vitalybaev opened this issue Mar 9, 2021 · 11 comments
Closed

Label text doesn't change if options updated #39

vitalybaev opened this issue Mar 9, 2021 · 11 comments

Comments

@vitalybaev
Copy link

vitalybaev commented Mar 9, 2021

Version

  • Vue version: 3

Description

We have typical use case: country select, translated to several locales. Options is array of objects:

const options = [{code: 'au', name: 'Autstralia'}]

If we have selected value au for example in English locale, our label shows Australia. But when change locale for example to Russian (ru), and update options, label will remain the same

I wrote test-case for this to clear our problem:

it('should reactively changes label when options has been changed', () => {
  let select = createSelect({
    value: 'ru',
    label: 'name',
    valueProp: 'code',
    options: [{ code: 'au', name: 'Australia' }, { code: 'ru', name: 'Russia' }, { code: 'us', name: 'USA' }],
  })

  select.vm.options = [{ code: 'au', name: 'Австралия' }, { code: 'ru', name: 'Россия' }, { code: 'us', name: 'США' }]

  expect(select.find('.multiselect-single-label').element).toBeVisible()
  expect(select.find('.multiselect-single-label').html()).toContain('Россия') // <---- Fails here, but shouldn't
})

Demo

https://jsfiddle.net/cubk7z50/13/

@osi-oswald
Copy link

@adamberecz The fix is now causing a new issue (Maximum recursive updates exceeded)

See: (best to open in Chrome)

@adamberecz adamberecz reopened this Mar 13, 2021
adamberecz added a commit that referenced this issue Mar 13, 2021
@adamberecz
Copy link
Collaborator

In fact the infinite recursion occures because you're reading value.value in options computed, which makes value a dependency of options so it triggers recalculation even when value changes. And when you select a tag value does change, making options to change with the new fix which watches options and re-sets value upon change.

Now I added a fix that prevents that.

@osi-oswald
Copy link

osi-oswald commented Mar 18, 2021

Another issue (probably related to the changes introduced here) is when trying to remove an option which has already been selected. Using setTimeout in v1.3.4 fixes the issue (but was not necessary in v1.3.2).

See: (best to open in Chrome):

May be related to #38

@adamberecz adamberecz reopened this Mar 18, 2021
@adamberecz
Copy link
Collaborator

Often nextTick comes into play when dealing with watchers which in case we do. So I'd recommend simply putting the options.value change into nextTick and things should be fine.

@vesper8
Copy link

vesper8 commented Mar 31, 2021

@osi-oswald Are you still crashing?

I'm seeing crashes fairly often (whole page becomes unresponsive and fans start spinning like crazy) on 1.3.5 and 1.3.6.

It happens when I use the autocomplete search and then press enter (or click with the mouse) on one of the results which does nothing else but execute this code:

    onSelect(option) {
      // console.log('onselect.option', option);
      this.$emit('onSelect', option);
    },
    
    ...
    
    @onSelect="navigateToPage"
    
    ...
    
    navigateToPage(e) {
        this.$router.push(e.url);
    },

It works some of the times and other times it crashes but it's fairly easy to reproduce and not that rare

@vesper8
Copy link

vesper8 commented Mar 31, 2021

It's happening constantly now but only on some pages.. no idea what's going on.. no errors in console at all.. page becomes unresponsive, fans spinning, can't reload, can't , can't ctrl-w/cmd-w, all I can do is X the page.

I tried wrapping my this.$emit code in this.nextTick but that didn't help neither

@adamberecz
Copy link
Collaborator

Do you have any code to reproduce?

@adamberecz adamberecz reopened this Mar 31, 2021
@vesper8
Copy link

vesper8 commented Mar 31, 2021

not really man. Best I can do is share my component maybe the answer lies in there. I've moved back to shentao/vue-multiselect for now.

<template>
  <div class="tw-flex tw-flex-col tw-flex-items-center tw-flex-justify-center">
    <multiselect
      v-model="selected"
      :searchable="true"
      :caret="false"
      :max-height="500"
      :placeholder="placeholder"
      :filter-results="false"
      :min-chars="1"
      :resolve-on-load="options.allowEmptySearch"
      :delay="200"
      :track-by="options.trackBy"
      :value-prop="options.valueProp"
      :label="options.label"
      :limit="options.limit"
      :options="async function(query) {
        return await asyncFindApi(query)
      }"
      :object="true"
      :no-results-text="options.noResults"
      :no-options-text="options.noOptions"
      class="tw-text-white lg:tw-w-96"
      @select="onSelect"
    >
      <template #option="{ option }">
        <text-highlight
          :queries="[query]"
          highlight-class="tw-p-0 tw-font-bold tw-text-gray-100 tw-bg-transparent"
        >
          {{ option.n }}
        </text-highlight>
      </template>
    </multiselect>
  </div>
</template>

<script>
import Vue from 'vue';
import TextHighlight from 'vue-text-highlight';

import multiselect from '@vueform/multiselect/dist/multiselect.vue2.js';

Vue.component('TextHighlight', TextHighlight);

export default {
  components: {
    multiselect,
  },

  props: {
    api: {
      type: String,
      default: '',
    },
    config: {
      type: Object,
      default: () => ({}),
    },
    placeholder: {
      type: String,
      default: 'Search',
    },
  },

  data() {
    return {
      query: '',
      selected: '',
      results: [],
      isLoading: false,

      defaultOptions: {
        valueProp: 'value',
        trackBy: 'n',
        label: 'n',
        limit: 20,
        allowEmptySearch: false,
        noResults: '',
        noOptions: '',
      },
    };
  },

  computed: {
    options() {
      return Object.assign(this.defaultOptions, this.config);
    },
  },

  methods: {
    onSelect(option) {
      this.$nextTick(() => {
        this.$emit('onSelect', option);
      });
    },

    async asyncFindApi(query) {
      this.query = query;

      const response = await this.$http.post(this.api, {
        query,
      });

      return response.data;
    },
  },
};
</script>

@adamberecz
Copy link
Collaborator

adamberecz commented Apr 1, 2021

Thanks, it helps! I was able to reproduce the bug, which is... very strange. Watcher keeps kicking in infinitely at useOptions.js@424 even though the value does not change. I need to do further investigation, though it seems like a Vue internal bug. A quick fix might be for anyone who gets here is to change sync to pre at useOptions.js@428.

@osi-oswald
Copy link

Btw just some small unrelated sidenote: You are a beast @adamberecz!! 🙌🤩🤗

adamberecz added a commit that referenced this issue Apr 6, 2021
adamberecz added a commit that referenced this issue Apr 6, 2021
@adamberecz
Copy link
Collaborator

@vesper8 now things should be fine with 1.3.7. @vitalybaev I had to change watcher to flush: 'pre' so when you're changing options to a new language a tick will pass until the label will be refreshed.

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

4 participants