Avoid stopping Bootstrap Vue's events

Need help with a b-form-select control in Bootstrap Vue. Trying to execute a function when the value changes, but want the option to cancel the event and keep the original value.

Complicating matters, the select is in a child component while the function is in the parent component.

The child component

public values: any[] = [
  { name: 'default'},
  { name: 'forbidden'},
  { name: 'other option' },
]

<b-form-select :value="property" @change="$emit('onPropertyChange', arguments[0])">
   <option v-for="(val, key) in values" :value="val" :key="key">{{val.Name}}</option>
</b-form-select>

The parent component:

this.property = { name: 'default' }
public onPropertyChange(newValue) {
  if (newValue.name === 'forbidden') {
    // Not changing this.property
  } else {
    // Changing it
    this.property = newValue
  }     
}

<child :property="property" @onPropertyChange="onPropertyChange"></child>

After selecting 'forbidden' in the select, I notice that the box shows the new value but both properties remain unchanged, which is desired. How do I prevent the select from updating as well?

Bootstrap Vue seems lacking a prevent modifier for change events. Even using the native event leads to the same issue.

Is my approach incorrect?

Answer №1

Enhancing user experience by disabling forbidden options instead of allowing selection confusion can be achieved through a change event handler. By saving and restoring the old value using nextTick, the desired behavior can be implemented.

It is important to note that the structure of your options may not align with b-form-select. To address this, a computed property has been created to generate the correct structure and handle the disabled attribute, which is then used in the b-form-select.

new Vue({
  el: '#app',
  data: {
    selected: 'default',
    options: [{
        name: 'default',
      },
      {
        name: 'forbidden'
      },
      {
        name: 'other option'
      },
      {
        name: 'what I had before'
      },
    ]
  },
  computed: {
    selectOptions() {
      return this.options.map((opt) => ({
        text: opt.name,
        value: opt.name,
        disabled: opt.name === 'forbidden'
      }));
    }
  },
  methods: {
    onChange(newValue) {
      const oldValue = this.selected;

      if (newValue === 'what I had before') {
        this.$nextTick(() => {
          this.selected = oldValue;
        });
      }
    }
  }
});
<script src="https://unpkg.com/vue@latest/dist/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.js"></script>
<div id="app">

  <b-form-select v-model="selected" :options="selectOptions" @change="onChange">
  </b-form-select>

  <div>Selected: <strong>{{selected}}</strong></div>

</div>

Answer №2

My suggestion involves utilizing $refs and computed getter/setter functions to address the issue at hand, although it can also be accomplished with v-bind/v-on. This approach is particularly beneficial in scenarios where user confirmation is required.

<template>
    <div>
        <b-form-select
            ref="mySelect"
            v-model="choiceHandler"
            :options="selectOptions"
        >
        </b-form-select>
    </div>
</template>
<script>
export default {
    data() {
        return {
            selectOptions: [
                { value: 1, text: "Choice 1" },
                { value: 2, text: "Choice 2" },
                { value: 3, text: "Choice 3" },
            ],
            storedChoice: 3,
        };
    },
    computed: {
        choiceHandler: {
            get() {
                return this.storedChoice;
            },
            set(newValue) {
                this.$refs.typeSelect.localValue = this.storedChoice; 
                let confirmDialog = new Promise((resolve) => {
                    setTimeout(() => {
                        resolve(true);
                    }, 1500);
                });
                confirmDialog.then((res) => {
                    if (res) {
                        this.storedChoice = newValue;
                    } else console.log("No changes");
                });
            },
        },
    },
};
</script>

In order to prevent undesired changes from affecting the data model or store, we employ a setter function. Prior to validation, we revert the localValue of the b-form-select component to its original state. If validation succeeds, the data model is updated, triggering Vue to refresh the b-form-select. Otherwise, no action is taken.

Although this method may result in a Vue warning related to direct DOM manipulation, I believe it is justified in this context. It prevents the browser from displaying data that have not yet been saved in the model, rather than overriding the existing data model.

For an alternative solution using a vanilla <select> tag without BootstrapVue, as well as employing v-bind/v-on instead of getters and setters, please refer to this link.

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

Are the functions 'useEffect' and 'useCallback' being repetitively used in this case?

I have implemented a custom hook in my React application to manage back navigation actions (POP). The functionality is operational, but I can't help but notice that useCallback and useEffect seem to be performing similar tasks. Here is the snippet of ...

I find it frustrating to constantly remove node_modules and reinstall the packages just to get npm run prod to function properly

Encountering this issue repeatedly: $ npm run production @ production /var/www/html/**** cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js xnpm ERR! ...

Exploring the capabilities of Laravel Webpack Mix and harnessing the power of the

I'm curious about integrating Laravel Webpack Mix with version 5.8 of Laravel. Do I have to include Vue/jQuery in app.js using the extract method in Webpack Mix? Will this result in duplication when loading manifest.js/vendor.js/app.js ? ...

What is the best way to display a Vuex state based on a function being activated?

I have noticed similar questions on this topic but couldn't find a solution, so I decided to create my own. Here's the issue: I have an array named "allCountries" in the state, initially filled with 250 country objects. I am successfully render ...

Angular 2 allows for duplication of elements using drag and drop functionality

Check out my code on Plunker for drag and drop functionality in drag.ts: http://plnkr.co/edit/PITLKzBB6YXobR1gubOw?p=preview. Please note that it only works in a separate window preview. import {Component, OnInit, ElementRef, Renderer} from '@angular ...

When using Vue2, pushing a string to an array simply replaces the existing string instead of appending it

My current task involves manipulating a local data array by adding and removing strings within a method. However, I have noticed that my logic always results in the array containing only a single string passed to the updateIdArr method. Even after removin ...

What is the best way to implement ES2023 functionalities in TypeScript?

I'm facing an issue while trying to utilize the ES2023 toReversed() method in TypeScript within my Next.js project. When building, I encounter the following error: Type error: Property 'toReversed' does not exist on type 'Job[]'. ...

The SpinButton object has an undefined public property called 'value' and the public method 'focus()' is not available

Recently, I've delved into using reactjs, typescript, and Office UI Fabric. However, I'm facing a challenge with one of the components from fabric. In this scenario, I have a simple Product component containing a SpinButton and a DefaultButton. M ...

Vue.js Asynchronous while loop (continuously checking backend until task completion)

When working inside a vue action, I am interested in continuously checking the status of my backend by calling another async action until a certain task is completed (there will be a loader on the frontend to show progress). To elaborate, I need to keep m ...

JavaScript: Choosing between explicit imports and the * sign

Why do this in one way: import * as copy from 'copy-to-clipboard'; instead of the other way: import { someMethod } from 'copy-to-clipboard'; Does it impact performance or bundle size? Personally, I find the second option cleaner. ...

Backend is currently unable to process the request

Whenever a user clicks on a note in my notes page, a request is supposed to be made to the backend to check if the user is the owner of that particular note. However, for some reason, the request is not being processed at all. The frontend is built using ...

Exploring the Usage of sessionStorage within the <template> Tag in Vue.js

Is it possible to access sessionStorage in the script section of a Vuejs component like this? <template> {sessionStorage} </template> Whenever I try to call it this way, I consistently receive the error message "cannot read property &apo ...

Manage the appearance of a component using props

Here is the code snippet that I am working with: export type BreadcrumbItemProps = { isCurrent?: boolean; }; const isCurrent = (props: { isCurrent?: boolean }) => props.isCurrent ? 'normal' : 'bold'; export const Item = styled.s ...

After modifying environment variables in Vue.js, the application still refers to the previous values

Currently, I am working on a Vue.js project where I have a .env.development file with various VUE_APP_* environment variables. Despite changing the values of some variables, the Vue.js code continues to reference the previous values. I have attempted mult ...

Numerous access points within Vite

I am currently working on a Vue2 project that uses Webpack, and I have decided to make the switch to Vite. Within my webpack.common.js file, there are multiple entry points defined: module.exports = { entry: { appSchool: './resources/scho ...

What is the best way to add bootstrap.min.js into a Vue JS project?

My Vue JS application's Bootstrap nav-bar is not functioning properly on mobile devices. Despite installing the Bootstrap, JQuery, and popper node modules, I am encountering an error message that reads "Module Not Found. Can't resolve 'node_ ...

Angular 4 - Automatically scroll to specific list item based on search query input

While I can achieve this with custom JavaScript, I'm curious if Angular 4 has any built-in features that could help. I have a list of checkboxes that are scrollable and a search input above them. My goal is to enable users to quickly jump to a section ...

Contact the help desk and receive information that is currently unknown

There are a few issues that I'm struggling to resolve. I am utilizing SwaggerService to fetch data, but the response is coming back as undefined. import {SwaggerService} from '../../services/swagger.service'; export class TestComponent im ...

Utilizing Vue's computed properties for advanced filtering and sorting capabilities

I have created a computed function to filter my houses based on a search input field and it is functioning properly. computed: { filtered: function() { var self = this; let searchTerm = (this.search || "").toLowerCase() if(this ...

deliver a promise with a function's value

I have created a function to convert a file to base64 for displaying the file. ConvertFileToAddress(event): string { let localAddress: any; const reader = new FileReader(); reader.readAsDataURL(event.target['files'][0]); reader ...