Search code examples
vuejs2nuxt.jsbootstrap-vue

Clear bootstrap modal's body on hidden


I have a Nuxt App that uses bootstrap vue.

I have a re-usable component for Modal

components/BaseModal.vue

<template>
  <b-modal id="base_modal" :title="title" size="md" @hidden="hide">
    <slot name="body"></slot>
    <hr>    
    <BaseButton class="btn" variant="outline-secondary" @click="close" buttonText="Cancel"/>      
    <slot name="footer"></slot>
  </b-modal>
</template>

<script>
export default {
  name: 'BaseModal',
  props: {
    title: { type: String, required: true },
    size: { type: String, default: "lg" }
  }, 
  methods: {  
    close() {
      this.$root.$emit('bv::hide::modal', 'base_modal');
    }, 
    hide() {
      // Clear slot body contents here
    }
  }
};
</script>

I invoke this component by rendering html elements using named scopes:

<template>
  <BaseButton @click="showModal" buttonText="Show Modal"/>

  <BaseModal title="Add Recommendation" size="md">
    <div slot="body">
      <b-form @submit.prevent="submit" autocomplete="off">
        <b-card-body>
          <b-row>
            <b-col><BaseTextInput v-model.trim="name" label="Headline"/></b-col>
          </b-row>
        </b-card-body>
      </b-form>
    </div>
    <BaseButton class="float-right btn" variant="outline-primary" @click="submit" buttonText="Save" slot="footer"/>
  </BaseModal>
</template>

<script>
  export default {
    name: 'FormInput',
    data() {
      return {
        name: ""
      } 
    },
    methods: {
      showModal: {
        this.$root.$emit('bv::show::modal','base_modal');
      },
      submit() {
        // dispatch creation
      }
    }
  }
</script>

How can I reset/clear the contents of modal's body when hidden, so that when I trigger the showModal it will re-render the content again?


Solution

  • The following solutions could be considered:

    Option 1: emit event from Modal component

    1. once the modal is getting closed, emit a custom event in Modal component:

    Modal.vue

    <template>
      <b-modal id="base_modal" title="Modal example" @hidden="hide" >
        <slot name="body"></slot>
        <slot name="footer"></slot>
      </b-modal>
    </template>
    
    <script>
    export default {
      name: "BaseModal",
      methods: {
        hide() {
          this.$emit('hide')
        }
      }
    };
    </script>
    
    1. then in parent component once hide event is triggered, form values could be cleared like this:

    App.vue

    <template>
      <div>
        <b-button @click="showModal" ref="btnShow">Open Modal</b-button>
        <ModalExample @hide="hideModal">
          <div slot="body">
            <form @submit.stop.prevent="handleSubmit">
              <b-form-input type="text" placeholder="Enter your name" v-model="name"></b-form-input>
            </form>
          </div>
        </ModalExample>
      </div>
    </template>
    
    <script>
    import ModalExample from "./components/ModalExample.vue";
    
    export default {
      components: {
        ModalExample
      },
      data() {
        return {
          name: ""
        };
      },
      methods: {
        showModal() {
          this.$root.$emit("bv::show::modal", "base_modal");
        },
        hideModal() {
          this.name = ''; //clear form values
        }
      }
    };
    </script>
    

    Here is a demo which demonstrates this approach.

    Option 2: utilize scoped slots

    Modal component could get access to data via scoped slots:

    ModalExample.vue

    <template>
      <b-modal id="base_modal" title="Modal example" @hidden="hide" >
        <slot :formData="formData" name="body"></slot>
        <slot name="footer"></slot>
      </b-modal>
    </template>
    
    <script>
    export default {
      name: "ModalExample",
      data() {
        return {
          formData : {}
        }
      },
      methods: {
        hide() {
          //clear form fields
          for (const name of Object.keys(this.formData)) {
            this.formData[name] = ""
          }
        }
      }
    };
    </script>
    

    App.vue

    <template>
      <div>
        <b-button @click="showModal" ref="btnShow">Open Modal</b-button>
        <ModalExample @hide="hideModal">
          <template slot="body" slot-scope="{formData}">
            <form @submit.stop.prevent="handleSubmit">
              <b-form-input
                type="text"
                placeholder="Enter your first name"
                v-model="formData.firstname"
              ></b-form-input>
              <b-form-input
                type="text"
                placeholder="Enter your last name"
                v-model="formData.lastname"
              ></b-form-input>
            </form>
          </template>
        </ModalExample>
      </div>
    </template>
    
    <script>
    import ModalExample from "./components/ModalExample.vue";
    
    export default {
      components: {
        ModalExample
      },
      data() {
        return {
        };
      },
      methods: {
        showModal() {
          this.$root.$emit("bv::show::modal", "base_modal");
        }
      }
    };
    </script>