Unusual "visual" phenomenon with autocomplete feature in VUE.js

Can someone review this code snippet?

Check out the code here

This is a peculiar example of a custom autocomplete VUE component.

If you enter a value in one of the fields in Section 1 (like 'Apple'), then click on the Next button, you'll notice that the same value appears incorrectly in some of the fields in Section 2 (still 'Apple'). However, if you click on Display vals, it will correctly display all values from both sections.

Many thanks in advance!

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>

VUE:

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() {
      // Notify parent of change
this.$emit("input", this.search);
      // Handle async vs. local data
      if (this.isAsync) {
        this.isLoading = true;
      } else {
        // Search local array
        this.filterResults();
        this.isOpen = true;
      }
    },

    filterResults() {
      // Filter results based on input
      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) {
      // Check for changes in items
      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;
}

Thank you once more!

Answer №1

It seems that the issue arises when utilizing the v-if binding for toggling, causing your <autocomplete> component to be destroyed and losing its state in the process. However, your data remains intact as they are reactive and updated when displayed in the DOM (e.g. using {{ test1 }} in your app template).

To resolve this issue, you can wrap your component inside a <keep-alive> tag and utilize the v-bind:key attribute, like so:

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

You can view a proof-of-concept of this solution in this updated fiddle: https://jsfiddle.net/teddyrised/t0ey81jw/9/

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How should we structure our JavaScript code: MVC or self-rendering components?

I'm in the process of developing a highly JS-centric web application. The bulk of the work is being carried out on the client side, with occasional syncing to the server using AJAX and XMPP. This is my first venture into creating something of this ma ...

Obtain the result of the Mongoose find operation

Greetings, I am facing a challenge with accessing elements returned from a find operation in Mongoose due to the asynchronous nature and callback functions. Below is the code for reference: function retrieveBudgets(email, callback) { models.User.f ...

Experimenting with Nuxtjs application using AVA and TypeScript

I'm in the process of developing a Nuxt application using TypeScript and intend to conduct unit testing with AVA. Nonetheless, upon attempting to run a test, I encounter the following error message: ✖ No test files were found The @nuxt/typescrip ...

What steps can be taken to prevent the "unable to convert undefined to an object" error from occurring?

There seems to be an issue with some of the documents not containing the planDetails and planId properties, resulting in the error "can't convert undefined to an object." However, I need to fetch that document whether these properties exist or not. Ho ...

Issues with integrating the jsPDF package into a JavaScript project

I'm struggling to solve this issue. I've been attempting to create a program that can download a pdf from a webpage using the jsPDF npm module. After downloading it, I tried importing it in two different ways: Using the node.js require statemen ...

Stopping HTTP client calls in OnDestroy hook of an Angular Service

Is it possible to automatically unsubscribe from an http call in an Angular service using the ngOnDestroy hook? Just to note, I am already familiar with using the rxjs 'take' operator or manually unsubscribing from the service within the compone ...

Tips for efficiently handling navigation re-rendering on a Single Page Application using Vue.js

I am currently in the process of developing a Single Page Application using Vue.js. The structure involves having a template in App.vue which includes a navigation bar followed by a router-view component. When a user attempts to log in, a modal window appe ...

A solution for managing MUI data grid rows and columns using useRef to prevent infinite loops when handling onSortModelChange

I am encountering a similar issue as described in the question below. A suggestion was given to utilize useRef to encapsulate rows and columns and access its .current value. How can I implement this solution?? Material-UI Data Grid onSortModelChange Causi ...

Using Vuetify timepicker to update model value

I have integrated the Vuetify timepicker into my project, but I am facing an issue where the selected time is not updating the model. How can I ensure that the model gets updated when a time is selected? The component that is utilizing the timepicker is a ...

The typography text exceeds the boundaries of the Material-UI CardContent

In the React Material-UI framework, I am working with a CardContent component that looks like this: <CardContent className={classes.cardContent}> <Typography component="p" className={classes.title} variant="title"> {this.props.post.title ...

What is the correct placement for $.validator.setDefaults({ onkeyup: false }) in order to deactivate MVC3 onKeyup for the Remote attribute?

After coming across various solutions on how to disable the onKeyup feature of MVC3 Remote Validator, I noticed that many suggest using the following code: $.validator.setDefaults({ onkeyup: false }); However, I'm in a dilemma about where to place t ...

Delivering objects from controller in AngularJS

I'm currently working on a project where I need to retrieve objects from the controller. Here's a snippet of my code: score.component.js: angular.module('score').component('score',{ templateUrl : 'app/score/score.t ...

What is the method to convert Javascript values from pixels to percentages?

Is it possible to change the scrolltop value dynamically based on a percentage of the user's screen size? I've been trying to achieve this using JS but haven't had any luck. Here is a link to a codepen that showcases the issue: [link] (http ...

Divs animated with jQuery keep on moving even after the animation has finished

My current project involves animating a single circle that scales, collides with two nearby circles, and then causes those circles to animate to specific positions. While everything works as expected, there is an issue where the two circles involved in the ...

IntelliJ Community offers comprehensive support for React.js development

Looking for a free React.js plugin with features like syntax highlighting and autocomplete that can be used in IntelliJ Community edition. Considering migrating from Ultimate license to Community edition. Found some answers on stackoverflow but none were ...

Displaying information according to the specified route prefix

Is there a method within VueRouter to identify if the route has a specific prefix? For instance, I have a navigation component that should display certain user-related links only if the route starts with /user-settings/ So, for example: /user-setti ...

The error message in Express points to module.js line 550 and states that the module cannot be

I am currently in the process of setting up a basic express application using the code below: const express = require('express'); const app = express() const bodyParser = require('body-parser'); const cookieParser = require('cooki ...

Can you provide tips on using Ajax to store a cache file?

I've created a javascript/ajax function that retrieves a json file from an external server. The functionality I'm trying to implement includes: Fetching the json file from the external server Saving the json file on the local server Checking if ...

What are the steps to create a mirror image of an object within itself?

Can an object reflect itself? I am interested in seeing a self-reflection on a metallic object. In essence, the two rings of the mechanism should be reflected in the lower part. Thank you in advance! https://i.sstatic.net/m3KUY.jpg https://i.sstatic.n ...

Utilize Ionic and Angular to display the local storage value within a text box

I am in the process of developing an application using the Ionic framework. I am able to upload files to the server and receive a response, which I then save the file name in localstorage. However, I am facing an issue where I need to display this value in ...