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
- Expect effect
<Dialog visible title=" title "@yes="fn1" @no="fn2" ></Dialog>Copy the code
Support for visible properties
- Be careful not to use show to indicate visibility
- 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
- Note that this cannot be controlled by modifying props. Visible. Cannot change props directly
- 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
- 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
- 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
- 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
- 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
- Why put it under the body: To prevent the Dialog from being obscured.
- Note the stack context
- 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
- I don’t want to declare a visible variable and then change its value
- Technical point: Dynamically mount components
- Hope ooutput
<Button @click="showDialog"> </Button> const showDialog=()=>{title:' HELLO', content:'HELLO', ok(){ console.log('ok') }, cancel(){ console.log('cancel') } }) }Copy the code
- 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