LoginSignup
4
1

More than 5 years have passed since last update.

[Vue.js] えっ、モーダルコンポーネントってthis.$confirm的な記法で呼び出せるんですか!

Last updated at Posted at 2019-03-08

出来るんですよ

mixin/IndependentComponent.js

これをmixinすることで呼び出すコンポーネントをvmへ追加してあげます
肝なのが、mountedのなかで$el(自分自身の要素)をcreatedで作ったvmへ差し込む部分とcreatedでその差し込み先のコンポーネントを作っているところです(つまり全部)

import Vue from 'vue'
import camelCase from 'lodash/camelCase'
import upperFirst from 'lodash/upperFirst'

export default {
  created () {
    let name = this.$options.name
    if (!name) {
      console.error('please set components name.')
      if (process.env.NODE_ENV === 'development') {
        console.warn('create unique id')
        const uuidv4 = require('uuid/v4')
        name = uuidv4()
      }
    }
    name = upperFirst(camelCase(name))
    let $parent = this.$parent
    if (!$parent) {
      let parent = document.querySelector(this.container)
      if (!parent) {
        // Lazy creating `div.notifications` container.
        const className = this.container.replace('.', '')
        const Component = Vue.extend({
          name,
          render (h) {
            return h('div', {
              'class': {
                [`${className}`]: true
              }
            })
          }
        })
        $parent = new Component().$mount()
        document.body.appendChild($parent.$el)
      } else {
        $parent = parent.__vue__
      }
      // Hacked.
      this.$_parent_ = $parent
    }
  },
  mounted () {
    // independent instance
    if (this.$_parent_) {
      this.$_parent_.$el.appendChild(this.$el)
      this.$parent = this.$_parent_
      delete this.$_parent_
    }
  }
}

genModalComponent.js

import Vue from 'vue'
import store from 'src/store'
import router from 'src/router'

export function genModalComponent (component) {
  const Instance = Vue.extend(component)
  // param has to be Object
  return (param) => {
    return new Promise((resolve, reject) => {
      let notUsedValue = new Instance({ // eslint-disable-line no-unused-vars
        el: document.createElement('div'),
        store,
        router,
        param: param || {},
        propsData: {
          active: true,
          callback: function (result, ...param) {
            if (result === this.$enums.result.CANCELED) {
              resolve(null)
            }
            if (result === this.$enums.result.OK) {
              if (param instanceof Array && param.length === 1) {
                param = param[0]
              }
              resolve(param)
            }
          }
        }
      })
    })
  }
}

ここで新規Vueインスタンスを生成しています
当然元のVueインスタンスと別になるので現状のVue.js Devtoolsではインスペクトすることができません(対応してくれ〜〜〜)
propにcallback関数を渡していますが、これは呼び出し側へ値を返却する必要がある際に利用します
ここの中の書き方はもうちょっといい方法があるかもしれません

plugins/enum.js

genModalComponent.jsで使用している$enumsはこの辺で適当に宣言してます

const promise = {
  RESOLVED: Symbol('enum_promise_resolved'),
  FAILED: Symbol('enum_promise_failed')
}
const result = {
  OK: Symbol('enum_result_ok'),
  CANCELED: Symbol('enum_result_canceled'),
  FAILED: Symbol('enum_result_failed')
}

export default {
  install: function (vue) {
    vue.prototype.$enums = {
      promise,
      result
    }
  }
}

使い方

Confirmモーダルをだす例をあげます (Buefy使用)

components/Confirm.vue

<template>
  <b-modal :active.sync="isActive" @close="cancel">
    <div class="modal-card" ref="modal">
      <header class="modal-card-head">
        <p class="modal-card-title">{{ title }}</p>
      </header>
      <section class="modal-card-body">
        <div class="content block">
          {{ body }}
        </div>
      </section>
      <footer class="modal-card-foot">
        <button class="button" type="button" @click='submit()'>OK</button>
        <button class="button" type="button" @click='cancel()'>Cancel</button>
      </footer>
    </div>
  </b-modal>
</template>

<script>
import IndependentComponent from 'components/mixins/IndependentComponent'
export default {
  name: 'Confirm',
  mixins: [ IndependentComponent ],
  props: {
    active: {
      type: Boolean,
      default: false
    },
    callback: {
      type: Function, // status, id
      default: () => {}
    },
    container: {
      type: String,
      default: '.confirm--container'
    }
  },
  data () {
    return {
      isActive: false,
      title: this.$options.param.title,
      body: this.$options.param.body
    }
  },
  methods: {
    cancel () {
      this.callback(this.$enums.result.CANCELED)
      this.close()
    },
    close () {
      this.$emit('close')
      this.$emit('update:active', false)
      this.isActive = false
    },
    submit () {
      this.callback(this.$enums.result.OK, true)
      this.close()
    }
  },
  mounted () {
    // Delay assign
    this.isActive = this.active
  }
}
</script>

呼び出し側からの変数はthis.$options.paramに詰め込まれています
b-modalに渡しているisActiveですがpropsをそのまま渡してしまうとmountされる前にactive === trueとなってしまい虚空の彼方へ消え去りますので、mountedの中でisActive = activeとしています

plugins/Confirm.js

import Confirm from 'components/Confirm'
import { genModalComponent } from 'src/utils/genModalComponent'

export default {
  install: function (vue, options) {
    vue.prototype.$confirm = genModalComponent(Confirm)
  }
}

pluginとして登録してあげる事によって、呼び出したいコンポーネントからthis.$confirmのような形で書くことができます
パラメータを渡す際は以下のようにObjectに詰めてあげるとOK

this.$confirm({
    title: 'hoge',
    body: 'message'
})

合わせて読みたい

勝手にリコメンドします

4
1
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
4
1