Reputation: 21
I have an issue that needs to be resolved.
a.ts
const open = ref<boolean>(false)
h(RowAction, {
open: open,
onDelete: () => {
Data.remove().then(() => {}).catch(() => {}).finally(() => (open.value = false))
}
})
RowAction.vue
<script lang="ts" setup>
defineProps<{open: boolean}>()
defineEmits<{ (e: 'delete'): void }>()
</script>
<template>
<div>
<button @click="$emit('delete')">
delete
</button>
</div>
<my-alert :open="open">
<alert-tetx>test</alert-text>
<button @click="open = false">close</button>
<button @click="$emit('delete')">delete</button>
</my-alert>
</template>
I want the delete event to run when clicked, and the alert to close after the promise resolves or rejects. However, I don't know why my code is not running.
Upvotes: 2
Views: 62
Reputation: 23907
Given that events are props named onXXX you can define the delete event as a prop:
RowAction.vue
<script setup lang="ts" >
import {ref} from 'vue';
const props = defineProps<{
onDelete: () => void | Promise<any>
}>();
const open = ref(false);
const error = ref('');
const disabled = ref(false);
async function onDelete(){
error.value = '';
disabled.value = true;
try{
await props.onDelete();
close();
}catch(e){
error.value = e.message;
}finally{
disabled.value = false;
}
}
function close(){
open.value = false;
error.value = '';
}
</script>
<template>
<div>
<button @click="open = true">
delete
</button>
<dialog :open>
<div> are you sure to delete ? </div>
<div v-if="error" style="font-size: small;color: red;">{{ error }}</div>
<button :disabled @click="close">close</button>
<button :disabled @click="onDelete">delete</button>
</dialog>
</div>
</template>
App.vue
<script setup lang="ts">
import {h} from 'vue';
import RowAction from './RowAction.vue';
class Data{
static remove(){
return new Promise((resolve, reject) => {
Math.random() > .5 ?
setTimeout(resolve, 1000) :
setTimeout(() => reject(new Error('Operation has failed')), 1000);
});
}
}
const alert = msg=>window.alert(msg);
</script>
<template>
<label>With promise work</label>
<row-action @delete="Data.remove()"/>
<label>Immediate</label>
<row-action @delete="alert('deleted')"/>
</template>
Upvotes: 0
Reputation: 23907
Exposing and managing internal property open
of RowAction
could be not a perfect design pattern. Just provide a resolve/reject pair in the delete
event to narrow scope of the problem to the event callback only:
RowAction.vue:
<script setup lang="ts" >
import {ref} from 'vue';
const open = ref(false);
const error = ref('');
const disabled = ref(false);
const $emit = defineEmits<{ (e: 'delete', defer: () => ReturnType<PromiseConstructor['withResolvers']>) }>()
function onDelete(){
let needsResolving = false;
error.value = '';
$emit('delete', () => {
needsResolving = true;
disabled.value = true;
const {promise, resolve, reject} = Promise.withResolvers();
promise
.then(() => open.value = false, e => error.value = e.message)
.finally(() => disabled.value = false);
return {promise, resolve, reject};
});
needsResolving || close();
}
function close(){
open.value = false;
error.value = '';
}
</script>
<template>
<div>
<button @click="open = true">
delete
</button>
<dialog :open>
<div> are you sure to delete ? </div>
<div v-if="error" style="font-size: small;color: red;">{{ error }}</div>
<button :disabled @click="close">close</button>
<button :disabled @click="onDelete">delete</button>
</dialog>
</div>
</template>
App.vue
<script setup lang="ts">
import {h} from 'vue';
import RowAction from './RowAction.vue';
class Data{
static remove(){
return new Promise((resolve, reject) => {
Math.random() > .5 ?
setTimeout(resolve, 1000) :
setTimeout(() => reject(new Error('Operation has failed')), 1000);
});
}
}
function render(){
return h(RowAction, {
onDelete: async (defer) => {
const {resolve, reject} = defer();
Data.remove().then(resolve, reject);
}
});
}
</script>
<template>
<label>With promise work</label>
<render/>
<label>Immediate</label>
<row-action/>
</template>
Upvotes: 0