The cause of

Recently, a page of the project used many dialog boxes (about 7-8), and the structure of each dialog box is different, some need the “ok” button at the bottom, some just need the “OK” button, and some don’t have anything at all. The dialog box also has its own internal logic. Sync has to declare some tokens in data every time. Managing these variables is fine. The key is that if all the dialogs were written in a single parent component, this component would be quite large (1000 lines +) because dialogs can be nested with complex structures and difficult to maintain. Was thinking each dialog box as a child component, but found that if the dialog is a chronological (such as a dialog click ok after the success of the requested data, only showing the next dialog box), then the component of communications and data transfer is difficult, at first I use Vuex manage these dialog displays of hidden variables and data transmission, But managing these things in a store is a lot of work, and looking at the element-UI instructions, writing a bunch of code in the store, committing and dispatch all over the component, is not going to work either

If the visible property is bound to a variable in Vuex’s store, then.sync will not work properly. You need to remove the.sync modifier, listen for both open and close events on the Dialog, and perform the corresponding mutation in Vuex in the event callback to update the value of the variable in the Visible property binding

Following the initial display hide in the Data declaration variable control dialog, component A is written like this:

<template>
    <div>
        <el-dialog :visible.sync="dialogVisible.dialog1">... < EL-dialog :visible.sync="dialogVisible.dialog2">... < EL-dialog :visible.sync="dialogVisible.dialog3">... < EL-dialog :visible.sync="dialogVisible.dialog4">... < EL-dialog :visible.sync="dialogVisible.dialog5">... HTML structure </el-dialog>... other code </div> </template> <script>export default {
  data() {
    return {
      dialogVisible: {
        dialog1: false,
        dialog2: false,
        dialog3: false,
        dialog4: false,
        dialog5: false,
        dialog6: false,
        dialog7: false,
        dialog8: false,
        dialog9: false,}}}} </script> then the entire component adds up to more than 1000 lines, sometimes an El-Dialog HTML structure alone can be more than 100 lines, which is difficult to read ~~~Copy the code

Problems to be solved

  • Dialog box shows hide
  • Dialog box into a separate component
  • Dialog boxes are displayed in order and data is transferred

To solve the process

What I did was wrap the dialog as a global popover component based on the El-Dialog component, providing a method called Invoke that calls out the dialog and returns a promise. Inside the dialog, either resolve or reject the promise, This lets you chain then calls out the next dialog and pass the value through a promise. Here is the global dialog based on my business scenario:

The directory structure. The dialog | ____dialog. Js | ____dialog. Vue ------------------------------------------dialog.vue:--------------------------------------- <template> <el-dialog :visible.sync="visible" :title="title" @close="close" @open="open":width="width">
    <functionalComponent :params="params" :render="render" ></functionalComponent>
  </el-dialog>
</template>
<script>
const functionalComponent = {
  functional: true, props: { render: Function, params: Object }, render(h, ctx) { const params = { ... ctx.props.params };return ctx.props.render(h, params);
  }
}
const noop = () => {};
export default {
  data() {
    return {
      title: ' ',
      width: '50%',
      render: noop,
      open: noop, // 'The current business is only using the close&open event in the el-Dialog, it can be extended'
      close: noop,
      visible: false,
      resolver: {},
      params: {},
    };
  },
  methods: {
    invoke(config) {
      this.visible = true;
      this.title = config.title;
      this.width = config.width;
      this.render = config.render;
      this.open = config.open;
      this.close = config.close;
      this.params = config.params;
      returnnew Promise((resolve, reject) => { this.resolver = { resolve, reject }; })},hide() {
      this.visible = false;
    },
    resolve(v) {
      this.resolver.resolve(v);
    },
    reject(err) {
      this.resolver.reject(new Error(err));
    },
    resolver() {
      returnthis.resolver; } }, components: { functionalComponent, } } </script> ---------------------------------------dialog.js-------------------------------------------- import Dialog  from'./dialog.vue';
import router from '@/router';
import store from '@/store'
import Vue from 'vue';
const noop = () => {};
letdialogInstance = null; / / mount Dialog and instance const newInstance = properties = > {const props = properties | | {}; const Instance = new Vue({ data: props, store, //'Because dialog is a global component, mounted at the end of the body, not in the #app container, and has no parent-sibling relationship to any component, we need to reintroduce sotre, router'
        router,
        render: h => h(Dialog, {
            props
        })
    });
    const component = Instance.$mount(a); document.body.appendChild(component.$el);
    return Instance.$children[0]. }; // There is only one instance globally, Const getDialogInstance = () => {dialogInstance = each invoke simply replaces the contents of the functionalComponent and returns a new Promise (pending state) dialogInstance || newInstance();return dialogInstance;
}

export default {
    invoke({
        title = ' ',
        render = noop,
        close = noop,
        open = noop,
        params = {},
        width = '50%'{} = {})return getDialogInstance().invoke({title, render, open, close, params, width})
    },
    hide() {
        getDialogInstance().hide();
    },
    resolve(v) {
        getDialogInstance().resolve(v);
    },
    reject(err) {
        getDialogInstance().reject(err);
    },
    resolver: () => getDialogInstance().resolver(),
}
Copy the code

use

Global introduction: import Dialog from'@/components/dialog';
Vue.prototype.$dialog= Dialog; In the views file directory:  .dialog |____dialog1.vue |____dialog2.vue |____dialog3.vue |____index.vue ---------------------------------------------dialog1.vue------------------------------- <template> <div> ... Assume many lines of code < button@click ="showNextDialog"</button> </div> </template> <script>export default {
  data() {
    return {
      name: 'jiaxin'
    }
  },
  methods: {
    showNextDialog() {// Use timer to simulate THE HTTP request, and display the next dialog box after 2 secondssetTimeout(() => {
        this.$dialog.resolve(this.name); }, 2000); }}, } </script> ---------------------------------------------dialog2.vue------------------------------- <template> <div> ... Assume many lines of code < button@click ="showNextDialog"</button> </div> </template> <script>export default {
  data() {
    return {
      age: 33
    }
  },
  methods: {
    showNextDialog() {// Use timer to simulate THE HTTP request, and display the next dialog box after 2 secondssetTimeout(() => {
        this.$dialog.resolve(this.age); }, 2000); }}, } </script> ---------------------------------------------dialog3.vue------------------------------- <template> <div> ... Assume many lines of code < button@click ="closeDialog"</button> </div> </template> <script>export default {
    props: {
      hobby: String,
    },
    methods: {
      closeDialog () {
        setTimeout(() => {
          console.log(this.body);
          this.$dialog.hide(); // Close dialog}, 1000); }}, } </script> ---------------------------------------------index.vue------------------------------- <template> <div> ... Let's say I have a couple hundred lines of structure < button@click ="showDialog1"</button> </div> </template> <script> import Dialog1 from'./dialog1';
import Dialog2 from './dialog2';
import Dialog3 from './dialog3';
  export default {
    data() {
      return {
        hobby: 'eat'
      }
    },
    methods: {
      showDialog1() {
        this.$dialog.invoke({
          title: 'Dialog 1',
          render: h => h(Dialog1)
        }).then(name => {
          console.log(name); // 'jiaxin'
          return this.$dialog.invoke({
            title: 'Dialog 2', render: h => h(Dialog2)}); }).then(age => { console.log(age); // 33 this.$dialog.invoke({
            title: 'Dialog 3'Params: {hobby: this.hobby,}, render(h, {hobby}) {params: {hobby: this.hobby,}, render(h, {hobby}) {returnH (Dialog3, {props: {hobby}}) // If using JSXreturn<Dialog3 hobby={hobby} /> }}); }); }}} </script> the order is to show dialog 1, dialog 2, dialog 3, via promisethenChain callsCopy the code

Beautify this.$dialog.resolve() with custom directives

We don't display the next dialog until the HTTP request completes successfully, and we bring the request data to the next dialog, if we return a promise, this.$dialogResolve () will not be executed until the promise succeeds, otherwise the binding value will be passed to the next dialog box vue.directive ('dialog', {
  inserted: (el, binding, vnode) => {
    el.onclick = () => {
      const bind = binding.value;
      const bindingVal = typeof bind= = ='function' ? bind() : bind;
      const resolve = vnode.context.$dialog.resolve;
      if (bindingVal instanceof Promise) {
        bindingVal.then(resolve);
      } else{ resolve(bindingVal); }}}}) to change the above dialog1. Vue -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - dialog1. Vue -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - <template> <div> ... Assume many lines of code <button V-dialog ="showNextDialog"</button> </div> </template> <script>export default {
  data() {
    return {
      name: 'jiaxin'
    }
  },
  methods: {
      showNextDialog() {
        return new Promise(rs => setTimeout(rs, 2000)); }},} this will have the same effect.$dialog.hide() do the same thing, but not here </script>Copy the code

conclusion

The global dialog method encapsulated above is not suitable for all scenarios, mainly for my side of the business, but the direction is in the right direction, mainly use promise to implement asynchronous call dialog box and data communication.