Vue: Build wheel -04:Dialog component

Demand analysis

  • Click and pop up
  • There are overlay layers
  • There are the close button
  • Have a title
  • There are content
  • There are yes/no buttons
  1. Expect effect
<Dialog visible title=" title "@yes="fn1" @no="fn2" ></Dialog>Copy the code

Support for visible properties

  1. Be careful not to use show to indicate visibility
  2. Create dialog.vue and dialogDemo.vue
  • dialog.vue

Overlay: Overlay. Subject of wrapper:

<template> <div class="dialog-overlay"></div> <div class="dialog-wrapper"> <header> title </header> <main> <p>1</p> <p>2</p> </main> <footer> <Button>ok</Button> <Button>cancel</Button> </footer> </div> </template>Copy the code

The Dialog component accepts the props: visible variable and determines whether to display it with v-if.

Make dialog clickable to close

  1. Note that this cannot be controlled by modifying props. Visible. Cannot change props directly
  2. Add a close event to the dialog that the user clicks.

dialog.VUE

 const close=()=>{
                context.emit('update:visible',false)
            }
Copy the code

dialogDemo.vue

<Dialog :visible="x" @update:visible= "x = $event"></Dialog> // :visible= and @update:visible= can be merged into V-model :visible= <Dialog v-model:visible="x" ></Dialog>Copy the code
  1. If you want the Dialog black mask layer to be closed when clicked, just add @click=”close” to the mask layer as well. But this is not a good experience (the user might accidentally click on the black part and close it), so you can add a props: closeOnClickOverlay

dialog-template

 <div class="gulu-dialog-overlay" @click="closeOnClickOverlay"></div>
Copy the code

dialog-script

 props:{
            visible: {type:Boolean.default: false
            },
            closeOnClickOverlay: {// Whether to close the mask layer by clicking on it
                type:Boolean.default:true}},setup(props,context){
    const close=() = >{
        context.emit('update:visible'.false)}const closeOnClickOverlay=() = >{
        if(props.closeOnClickOverlay ){ // Close is called only if this function is enabled, otherwise nothing is done.
            close()
        }
    }
  
    return {close,closeOnClickOverlay}
}
Copy the code
  1. Click Cancel and OK
  • Cancel triggers the Cancel event (F2), and OK triggers the OK event (F1).
  • DialogDemo passes f1 and F2 events to dialog, dialog accepts props, of type Function
  • ok
 const ok=() = >{
              if(props.ok && props.ok()! = =false) {// If ok exists and the result of ok is not false. ()! ==false
                  close()
              }
            }
Copy the code
  • cancel
 const cancel=() = >{
                context.emit('cancel')
                close()

            }
Copy the code
  1. Click close code
  • dialogDemo
<Dialog v-model:visible="x" :ok="f1" :cancel="f2"></Dialog> export default { name: "DialogDemo", components:{Dialog,Button}, setup(){ const x =ref(false) const toggle = ()=>{ console.log(x) x.value =! x.value } const f1=()=>{ return true } const f2=()=>{ } return {x,toggle,f1,f2} } }Copy the code
  • dialog.vue
<template> <template v-if="visible"> <div class="gulu-dialog-overlay" @click="closeOnClickOverlay"></div> <div Class ="gulu-dialog-wrapper"> <div class="gulu-dialog-wrapper"> <header> <span @click="close" class="gulu-dialog-close"></span>  </header> <main> <p>1</p> <p>2</p> </main> <footer> <Button @click="ok">ok</Button> <Button @click="cancel">cancel</Button> </footer> </div> </div> </template> </template> <script lang="ts"> import Button from './button.vue' export default { name: "dialog", components:{Button}, props:{ visible:{ type:Boolean, default: false }, closeOnClickOverlay:{ type:Boolean, default:true }, ok:{ type:Function }, cancel:{ type:Function } }, setup(props,context){ const close=()=>{ context.emit('update:visible',false) } const closeOnClickOverlay=()=>{ if(props.closeOnClickOverlay ){ close() } } const cancel=()=>{ context.emit('cancel') close() } const ok=()=>{ if(props.ok && props.ok()! ==false){// new writing: props. Ok? ()! ==false close() } } return {close,closeOnClickOverlay,cancel,ok} } } </script>Copy the code

Title and Content are supported

  1. Using slot
  • dialogDemo
<Dialog v-model:visible="x" :ok="f1" :cancel="f2"> <template v-slot:content> <div>hi</div> </template> <template V-slot :title> <strong> </strong> </template> </Dialog>Copy the code
  • dialog
<template>
    <template v-if="visible">
    <div class="gulu-dialog-overlay" @click="closeOnClickOverlay"></div>
    <div class="gulu-dialog-wrapper">
        <div class="gulu-dialog">
        <header>
            <slot name="title"></slot>
            <span @click="close" class="gulu-dialog-close"></span>
        </header>
        <main>
            <slot name="content"></slot>
        </main>
        <footer>
            <Button @click="ok">ok</Button>
            <Button @click="cancel">cancel</Button>
        </footer>
        </div>
    </div>
    </template>

</template>
Copy the code
  • Grammar:
// inside <main> <slot name="content"></slot> </main>Copy the code

Move the Dialog below the body

  1. Why put it under the body: To prevent the Dialog from being obscured.
  2. Note the stack context
  3. Use the new build: Teleport
  • Built-in components, do not import
  • Action: Put the contents under the body.
  • dialog.vue
<template>
    <template v-if="visible">
        <Teleport to="body">
            <div class="gulu-dialog-overlay" @click="closeOnClickOverlay"></div>
            <div class="gulu-dialog-wrapper">
                <div class="gulu-dialog">
                    <header>
                        <slot name="title"></slot>
                        <span @click="close" class="gulu-dialog-close"></span>
                    </header>
                    <main>
                        <slot name="content"></slot>
                    </main>
                    <footer>
                        <Button @click="ok">ok</Button>
                        <Button @click="cancel">cancel</Button>
                    </footer>
                </div>
            </div>
        </Teleport>

    </template>

</template>

Copy the code

Open Dialog in one sentence

  1. I don’t want to declare a visible variable and then change its value
  2. Technical point: Dynamically mount components
  3. Hope ooutput
<Button @click="showDialog"> </Button> const showDialog=()=>{title:' HELLO', content:'HELLO', ok(){ console.log('ok') }, cancel(){ console.log('cancel') } }) }Copy the code
  1. New openDialog. Ts
import Dialog from './dialog.vue' import {createApp,h} from 'vue' export const openDialog = (options)=>{ const {title, The content, ok and cancel} = options const div = document. The createElement method (' div ') document. The body. The appendChild (div) / / create a div, And put it inside the body. const close = ()=>{ app.unmount(div) div.remove() } const app = createApp({ render(){ return h(Dialog,{visible:true, // Render dialog visible:true 'onUpdate:visible':(newVisible)=>{ visible if(newVisible ===false){ close() } }, ok,cancel},{ title:title, Content :content})}}) app.mount(div)Copy the code