65
50

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vuetify でなるべく疎結合になるように ref を使って Modal(モーダル)を実装する

Posted at

Vuetify は Material デザインベースの Vue 用 Component ライブラリで、豊富なコンポーネントと丁寧なマニュアルが魅力だ。

Vuetify を使って Modal を実装する場合には v-dialogue component を使うといいと思う。(他にもっといいのあったら教えてください)

今回はこれをサンプルよりも疎結合になるように実装する方法について。ref を使う。

普通に実装する

サンプルを元に簡単に実装すると次のようになる。

App.vue
<template>
  <v-app>
  <div class="text-xs-center">
    <v-dialog
      v-model="dialog"
      width="500"
    >
      <v-btn
        slot="activator"
        color="red lighten-2"
        dark
      >
        Click Me
      </v-btn>

      <v-card>
        <v-card-title
          class="headline grey lighten-2"
          primary-title
        >
          Privacy Policy
        </v-card-title>

        <v-card-text>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
        </v-card-text>

        <v-divider></v-divider>

        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn
            color="primary"
            flat
            @click="dialog = false"
          >
            I accept
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </div>
  </v-app>
</template>

<script>
  export default {
    data () {
      return {
        dialog: false
      }
    }
  }
</script>

ただし上記例だと長すぎる。v-dialog 以下を別コンポーネントに切り出す

上記のように普通に実装すると、非常にコードが長くなる。v-dialog 以下を別コンポーネントに切り出したい。dialog.vue に切り出すとしよう。

ただしその際に問題になるのは、モーダルの開閉状態を制御する state である dialogApp.vue の中に残ってしまう。dialogdialog.vue でしか使わない state なので App.vue が持っているのはおかしい。これを dialogu.vue に任せることにしたい。

しかし、任せたら任せたで難しくなることが一つある。dialogu.vue に譲渡した dialog を、親コンポーネントである App.vue からどのように変更すればいいのか、という問題だ。親コンポーネントから、子コンポーネントの状態を変更したい場合は結構ある。具体的には、親コンポーネントで API からデータを fetch し、それが終わった時に子コンポーネントの modal を開く、といった場合だ。子コンポーネントにアクセスしなくてはいけない。

これを vuex で管理するのも一つの方法だが、この画面でしか使わない state を vuex で管理すべきでない。

今回は ref を使った実装を紹介する。

dialog.vue
<template>
  <v-app>
    <div class="parent">
      <button class="parent-button" @click="refToParentOpen">parent button</button>
      <app-dialogue ref="childDialogue"></app-dialogue>
    </div>
  </v-app>
</template>

<script>
  import appDialogue from './components/dialogue.vue'
  export default {
    components: {
      appDialogue
    },
    methods: {
      refToParentOpen(){
        this.$refs.childDialogue.open()
      }
    }
  }
</script>
<style scoped>
.parent {
  background: red
}

.parent-button {
  color: white;
  text-align: center;
  background: blue;
}
</style>

ポイントは、親コンポーネントで呼び出す「子コンポーネント」に対して、ref="name" という形で、参照を紐づけることだ。これによって、親コンポーネントからは this.$refs.name で参照することができる。あとは、this.$refs.name.methodName() という形で子コンポーネントの持つメソッドにアクセスできる。

注意点として、:ref="name" と書いてもうまく機能しないことをあげておく。なぜなら :refv-bind なので name という変数を参照することになってしまうからだ。もしこうかくのであれば、前もってこの変数を宣言しておく必要がある。

こうすることで、次のようにフェッチが終わって初めてモーダルを開く実装を親コンポーネントですることができる。

<script>
    methods: {
      refToParentOpen(){
        this.$refs.childDialogue.open()
      },
      async fetchData(){
        // まずフェッチする
        await axios.get(/api/endpoint)
     // フェッチが終わって初めてモーダルを開く 
        this.refToParentOpen()
      }
    }
</script>
65
50
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
65
50

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?