Странный "оптический" эффект с автозаполнением в VUE.js


Странный "оптический" эффект с автозаполнением в VUE.js

27.09.2020 12:24:45 Просмотров 33 Источник

Не мог бы кто-нибудь взглянуть на этот код?

https://jsfiddle.net/JLLMNCHR/09qtwbL6/123/

Это пользовательский компонент автозаполнения VUE strange sample.

Если вы пишете что-то в некоторых полях раздела 1 (например, "яблоко"), а затем нажимаете кнопку Далее, вы видите - неправильно-то же самое значение в некоторых полях раздела 2 (то же самое "яблоко"). Но если вы нажмете кнопку Display vals, то увидите правильные значения всех значений обоих разделов.

Спасибо!

HTML:

<div id="app">

  <div v-if="section == 1">
    <p>Section {{section}}</p>
    <br>
    <div>
      <label>Test1</label>                          
      <autocomplete v-model="test1" 
                         :items="theItems">
      </autocomplete>
    </div>
    <br>
    <div>   
      <label>Test2</label>                          
      <autocomplete v-model="test2" 
                         :items="theItems">
      </autocomplete>
    </div>
  </div>

  <div v-if="section == 2">
    <p>Section {{section}}</p>
    <br>
    <div>
      <label>Test3</label>                          
      <autocomplete v-model="test3" 
                         :items="theItems">
      </autocomplete>
    </div>
    <br>
    <div>   
      <label>Test4</label>                          
      <autocomplete v-model="test4" 
                         :items="theItems">
      </autocomplete>
    </div>
  </div>

  <br>

<button v-if="section == 2" type="button" v-on:click="section=1">Prev</button>
<button type="button" v-on:click="displayVals()">Display vals</button>
<button v-if="section == 1" type="button" v-on:click="section=2">Next</button>

</div>



<script type="text/x-template" id="autocomplete">
  <div class="autocomplete">
    <input type="text" @input="onChange" v-model="search" @keyup.down="onArrowDown" @keyup.up="onArrowUp" @keyup.enter="onEnter" />
    <ul id="autocomplete-results" v-show="isOpen" class="autocomplete-results">
      <li class="loading" v-if="isLoading">
        Loading results...
      </li>
      <li v-else v-for="(result, i) in results" :key="i" @click="setResult(result)" class="autocomplete-result" :class="{ 'is-active': i === arrowCounter }">
        {{ result }}
      </li>
    </ul>

  </div>
</script>

Вью:

const Autocomplete = {
  name: "autocomplete",
  template: "#autocomplete",
  props: {
    items: {
      type: Array,
      required: false,
      default: () => []
    },
    isAsync: {
      type: Boolean,
      required: false,
      default: false
    }
  },

  data() {
    return {
      isOpen: false,
      results: [],
      search: "",
      isLoading: false,
      arrowCounter: 0
    };
  },

  methods: {
    onChange() {
      // Let's warn the parent that a change was made
this.$emit("input", this.search);
      // Is the data given by an outside ajax request?
      if (this.isAsync) {
        this.isLoading = true;
      } else {
        // Let's search our flat array
        this.filterResults();
        this.isOpen = true;
      }
    },

    filterResults() {
      // first uncapitalize all the things
      this.results = this.items.filter(item => {
        return item.toLowerCase().indexOf(this.search.toLowerCase()) > -1;
      });
    },
    setResult(result) {    
      this.search = result;
            this.$emit("input", this.search);
      this.isOpen = false;
    },
    onArrowDown(evt) {
      if (this.arrowCounter < this.results.length) {
        this.arrowCounter = this.arrowCounter + 1;
      }
    },
    onArrowUp() {
      if (this.arrowCounter > 0) {
        this.arrowCounter = this.arrowCounter - 1;
      }
    },
    onEnter() {
      this.search = this.results[this.arrowCounter];
      this.isOpen = false;
      this.arrowCounter = -1;
    },
    handleClickOutside(evt) {
      if (!this.$el.contains(evt.target)) {
        this.isOpen = false;
        this.arrowCounter = -1;
      }
    }
  },
  watch: {
    items: function(val, oldValue) {
      // actually compare them
      if (val.length !== oldValue.length) {
        this.results = val;
        this.isLoading = false;
      }
    }
  },
  mounted() {
    document.addEventListener("click", this.handleClickOutside);
  },
  destroyed() {
    document.removeEventListener("click", this.handleClickOutside);
  }
};

new Vue({
  el: "#app",
  name: "app",
  components: {
    autocomplete: Autocomplete
  },
  methods: {
    displayVals() {
        alert("test1=" + this.test1 + ", test2=" + this.test2 + ", test3=" + this.test3 + ", test4=" + this.test4);
    },
  },
  data: {
    test1: '',
        test2: '',
        test3: '',
        test4: '',
        section: 1,
        theItems: [ 'Apple', 'Banana', 'Orange', 'Mango', 'Pear', 'Peach', 'Grape', 'Tangerine', 'Pineapple']
  }
});

CSS:

#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin-top: 60px;
}

.autocomplete {
  position: relative;
  width: 130px;
}

.autocomplete-results {
  padding: 0;
  margin: 0;
  border: 1px solid #eeeeee;
  height: 120px;
  overflow: auto;
  width: 100%;
}

.autocomplete-result {
  list-style: none;
  text-align: left;
  padding: 4px 2px;
  cursor: pointer;
}

.autocomplete-result.is-active,
.autocomplete-result:hover {
  background-color: #4aae9b;
  color: white;
}

Еще раз спасибо!

У вопроса есть решение - Посмотреть?

Ответы - Странный "оптический" эффект с автозаполнением в VUE.js / Strange "optical" effect with autocomplete in VUE.js

Является ответом!
Terry

27.09.2020 12:39:27

Вероятно, это связано с тем, что при использовании привязки v-if для переключения ваш компонент <autocomplete> разрушается и, следовательно, теряет свое состояние. Ваши данные должны быть нетронутыми, потому что когда вы пытаетесь распечатать данные компонента в DOM (например, используя {{ test1 }} в любом месте шаблона приложения), они действительно реагируют и обновляются.

Комбинация обертывания вашего компонента внутри тега <keep-alive> и использования v-bind:key устранит вашу проблему, т. е.:

<keep-alive>
    <autocomplete v-model="test1" v-bind:key="1" :items="theItems">
    </autocomplete>
</keep-alive>

См. доказательство концепции в этой обновленной скрипке: https://jsfiddle.net/teddyrised/t0ey81jw/9/

Помочь в развитии проекта:
Закрыть X