title: How do I open and close v-dialog from a component under its parent? Use Vuex?
body: <p>I need to open a CRUD dialog from a data table component. Both the dialog and data table share the same parent. The data table is reusable but the CRUD dialog is not. </p>
  
  <p>The use case seems very common. An admin page contains a table of data, each row containing an edit button that opens edit dialog. </p>
  
  <p>I've attempted using Vuex below - however this error occurs:</p>
  
  <pre><code>[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'showUserModal' of undefined"
  
  found in
  
  ---&gt; &lt;VBtn&gt;
         &lt;VSimpleTable&gt;
           &lt;VData&gt;
             &lt;VDataTable&gt;
               &lt;DataTable&gt; at src/components/DataTable.vue
                 &lt;Settings&gt; at src/views/Settings.vue
                   &lt;VContent&gt;
                     &lt;VApp&gt;
                       &lt;App&gt; at src/App.vue
                         &lt;Root&gt;
  </code></pre>
  
  <p><strong>Why is the imported mutator not available and is this a good approach to achieving the common functionality?</strong></p>
  
  <p>I arrived at my current solution using these 2 approaches
  <a href="https://markus.oberlehner.net/blog/building-a-modal-dialog-with-vue-and-vuex/" rel="nofollow noreferrer">https://markus.oberlehner.net/blog/building-a-modal-dialog-with-vue-and-vuex/</a>
  <a href="https://forum.vuejs.org/t/how-to-trigger-a-modal-component-from-vuex-store/27243/9" rel="nofollow noreferrer">https://forum.vuejs.org/t/how-to-trigger-a-modal-component-from-vuex-store/27243/9</a></p>
  
  <p><strong>UserAdmin.vue</strong></p>
  
  <pre><code>&lt;template&gt;
    &lt;v-container fluid &gt;
        &lt;DataTable v-bind:rows="allUsers" v-bind:headers="headers" /&gt;
        &lt;EditUser /&gt;
    &lt;/v-container&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
  import { mapGetters, mapActions } from "vuex";
  import DataTable from '../components/DataTable';
  import EditUser from '../modals/EditUser';
  
  export default {
    name: 'UserAdmin',
  
    methods: {
      ...mapActions(["getUsers"])
    },
  
    computed: mapGetters(["allUsers"]),
  
    components: {
      DataTable, EditUser
    },
  
    data(){
      return {
        headers: [ 
          { text: 'Name', value: 'name' },
          { text: 'Username', value: 'email' },
          { text: 'Administrator', value: 'admin' },
          { text: "", value: "controls", sortable: false}
        ]
      }
    },
  
    created(){
      this.getUsers();
    }
  }
  &lt;/script&gt;
  </code></pre>
  
  <p><strong>DataTable.vue</strong></p>
  
  <pre><code>&lt;template&gt;
      &lt;v-data-table
          :headers="headers"
          :items="rows"
          :items-per-page="5"
          class="elevation-1"
      &gt;
      &lt;!-- https://stackoverflow.com/questions/59081299/vuetify-insert-action-button-in-data-table-and-get-row-data --&gt; 
       &lt;template v-slot:item.controls="props"&gt;
          &lt;v-btn class="my-2" fab dark x-small color="blue" @click="onButtonClick(props.item.email)"&gt;
            &lt;v-icon dark&gt;mdi-pencil&lt;/v-icon&gt;
          &lt;/v-btn&gt;
        &lt;/template&gt; 
      &lt;/v-data-table&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
  
    import { mapMutations } from "vuex";
  
    export default {
      name: "DataTable",
      props:["headers", "rows"],
      methods: {
        ...mapMutations(["toggleUserModal"]),
        onButtonClick: function(email) {
          console.log("clicked: " + email)
          this.toggleUserModal();
        }
      }
    }
  &lt;/script&gt;
  </code></pre>
  
  <p><strong>EditUser.vue</strong></p>
  
  <pre><code>&lt;template&gt;
    &lt;v-row justify="center"&gt;
      &lt;v-dialog v-model="dialog" persistent max-width="600px" v-show='showUserModal'&gt;
        &lt;v-card&gt;
          &lt;v-card-title&gt;
            &lt;span class="headline"&gt;User Profile&lt;/span&gt;
          &lt;/v-card-title&gt;
          &lt;v-card-text&gt;
            &lt;v-container&gt;
              &lt;v-row&gt;
                &lt;v-col cols="12" sm="6" md="4"&gt;
                  &lt;v-text-field label="Legal first name*" required&gt;&lt;/v-text-field&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12" sm="6" md="4"&gt;
                  &lt;v-text-field label="Legal middle name" hint="example of helper text only on focus"&gt;&lt;/v-text-field&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12" sm="6" md="4"&gt;
                  &lt;v-text-field
                    label="Legal last name*"
                    hint="example of persistent helper text"
                    persistent-hint
                    required
                  &gt;&lt;/v-text-field&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12"&gt;
                  &lt;v-text-field label="Email*" required&gt;&lt;/v-text-field&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12"&gt;
                  &lt;v-text-field label="Password*" type="password" required&gt;&lt;/v-text-field&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12" sm="6"&gt;
                  &lt;v-select
                    :items="['0-17', '18-29', '30-54', '54+']"
                    label="Age*"
                    required
                  &gt;&lt;/v-select&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12" sm="6"&gt;
                  &lt;v-autocomplete
                    :items="['Skiing', 'Ice hockey', 'Soccer', 'Basketball', 'Hockey', 'Reading', 'Writing', 'Coding', 'Basejump']"
                    label="Interests"
                    multiple
                  &gt;&lt;/v-autocomplete&gt;
                &lt;/v-col&gt;
              &lt;/v-row&gt;
            &lt;/v-container&gt;
            &lt;small&gt;*indicates required field&lt;/small&gt;
          &lt;/v-card-text&gt;
          &lt;v-card-actions&gt;
            &lt;v-spacer&gt;&lt;/v-spacer&gt;
            &lt;v-btn color="blue darken-1" text @click="dialog = false"&gt;Close&lt;/v-btn&gt;
            &lt;v-btn color="blue darken-1" text @click="dialog = false"&gt;Save&lt;/v-btn&gt;
          &lt;/v-card-actions&gt;
        &lt;/v-card&gt;
      &lt;/v-dialog&gt;
    &lt;/v-row&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
    export default {
      data: () =&gt; ({
          dialog: false,
      }),
      computed: {
          showUserModal(){
              return this.$store.state.showUserModal
          }
      }
    }
  &lt;/script&gt;
  </code></pre>
  
  <p><strong>modals.js</strong></p>
  
  <pre><code>const state = {
      showUserModal: false
  }
  
  const mutations = {
      toggleUserModal: () =&gt; (this.showUserModal = !this.showUserModal)
  }
  
  const getters = {
      showUserModal: state =&gt; {
          return state.showUserModal
      }
  }
  
  export default {
      state,
      getters,
      mutations
  }
  </code></pre>
  
  <p>New code based on @Anatoly suggestions - everything works except the events emitted from the dialog, ex: <code>onEditUserConfirmed</code> are not picked up in the parent component. </p>
  
  <p><strong>ModalComponent</strong></p>
  
  <pre><code>&lt;template&gt;
    &lt;v-row justify="center"&gt;
      &lt;v-dialog v-model="visible" persistent max-width="600px"&gt;
        &lt;v-card v-if="user"&gt;
          &lt;v-card-title&gt;
            &lt;span class="headline"&gt;User Profile&lt;/span&gt;
          &lt;/v-card-title&gt;
          &lt;v-card-text&gt;
            &lt;v-container&gt;
              &lt;v-row&gt;
                &lt;v-col cols="12" sm="6" md="4"&gt;
                  &lt;v-text-field v-model="user.name" label="Legal first name*" required&gt;&lt;/v-text-field&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12" sm="6" md="4"&gt;
                  &lt;v-text-field label="Legal middle name" hint="example of helper text only on focus"&gt;&lt;/v-text-field&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12" sm="6" md="4"&gt;
                  &lt;v-text-field
                    label="Legal last name*"
                    hint="example of persistent helper text"
                    persistent-hint
                    required
                  &gt;&lt;/v-text-field&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12"&gt;
                  &lt;v-text-field label="Email*" required&gt;&lt;/v-text-field&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12"&gt;
                  &lt;v-text-field label="Password*" type="password" required&gt;&lt;/v-text-field&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12" sm="6"&gt;
                  &lt;v-select :items="['0-17', '18-29', '30-54', '54+']" label="Age*" required&gt;&lt;/v-select&gt;
                &lt;/v-col&gt;
                &lt;v-col cols="12" sm="6"&gt;
                  &lt;v-autocomplete
                    :items="['Skiing', 'Ice hockey', 'Soccer', 'Basketball', 'Hockey', 'Reading', 'Writing', 'Coding', 'Basejump']"
                    label="Interests"
                    multiple
                  &gt;&lt;/v-autocomplete&gt;
                &lt;/v-col&gt;
              &lt;/v-row&gt;
            &lt;/v-container&gt;
            &lt;small&gt;*indicates required field&lt;/small&gt;
          &lt;/v-card-text&gt;
          &lt;v-card-actions&gt;
            &lt;v-spacer&gt;&lt;/v-spacer&gt;
            &lt;v-btn color="blue darken-1" text @click="onCancel"&gt;Close&lt;/v-btn&gt;
            &lt;v-btn color="blue darken-1" text @click="onSave"&gt;Save&lt;/v-btn&gt;
          &lt;/v-card-actions&gt;
        &lt;/v-card&gt;
      &lt;/v-dialog&gt;
    &lt;/v-row&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
  export default {
    name: "EditUser",
    props: {
      user: Object,
      visible: {
        type: Boolean,
        default: false
      }
    },
    methods: {
      onSave() {
        console.log('save button gets here...')
        this.$emit("onEditUserConfirmed", this.user);
      },
      onCancel() {
        console.log('cancel button gets here...')
        this.$emit("onEditUserCancelled");
      }
    }
  };
  &lt;/script&gt;
  </code></pre>
  
  <p><strong>Parent Component</strong></p>
  
  <pre><code>&lt;template&gt;
    &lt;v-container fluid&gt;
      &lt;v-data-table :headers="headers" :items="allUsers" :items-per-page="5" class="elevation-1"&gt;
        &lt;!-- https://stackoverflow.com/questions/59081299/vuetify-insert-action-button-in-data-table-and-get-row-data --&gt;
        &lt;template v-slot:item.controls="props"&gt;
          &lt;v-btn class="my-2" fab dark x-small color="blue" @click="onEditClick(props.item)"&gt;
            &lt;v-icon dark&gt;mdi-pencil&lt;/v-icon&gt;
          &lt;/v-btn&gt;
        &lt;/template&gt;
      &lt;/v-data-table&gt;
  
      &lt;EditUser
        :user="user"
        :visible="isDialogVisible"
        @confirmed="onEditUserConfirmed"
        @cancelled="onEditUserCancelled"
      /&gt;
    &lt;/v-container&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
  import { mapGetters, mapActions } from "vuex";
  import EditUser from "../modals/EditUser";
  
  export default {
    name: "Settings",
    data() {
      return {
        user: null,
        isDialogVisible: false,
        headers: [
          { text: "Name", value: "name" },
          { text: "Username", value: "email" },
          { text: "Administrator", value: "admin" },
          { text: "", value: "controls", sortable: false }
        ]
      };
    },
    methods: {
      ...mapActions(["getUsers"]),
      onEditClick: function(user) {
        console.log('Editing user: ' + user.email)
        this.user = user;
        this.isDialogVisible = true;
      },
      onEditUserConfirmed(user) {
        console.log('Saving user: ' + user.email)
        this.isDialogVisible = false;
      },
      onEditUserCancelled () {
        this.isDialogVisible = false;
      }
    },
  
    computed: mapGetters(["allUsers"]),
  
    components: {
      EditUser
    },
  
    created() {
      this.getUsers();
    }
  };
  &lt;/script&gt;
  </code></pre>