how to make a future builder like widget in Vue js
Now, you can just import PromiseBuilder component from npm.
PromiseBuilder.vue
<template>
<div>
<slot
:pending="pending"
:fulfilled="fulfilled"
:rejected="rejected"
:settled="settled"
:result="result"
:error="error"
/>
<slot v-if="pending" name="pending" />
<slot v-else-if="fulfilled" name="fulfilled" :result="result" />
<slot v-else-if="rejected" name="rejected" :error="error" />
<slot v-if="settled" name="settled" :result="result" :error="error" />
</div>
</template>
<script>
import { reactive, toRefs, watch, computed } from '@vue/composition-api'
const PromiseStatus = Object.freeze({
pending: Symbol('pending'),
fulfilled: Symbol('fulfilled'),
rejected: Symbol('rejected'),
})
export default {
props: {
promise: {
type: Promise,
required: true,
},
},
setup (props) {
const state = reactive({
status: PromiseStatus.pending,
result: null,
error: null,
pending: computed(() => state.status === PromiseStatus.pending),
fulfilled: computed(() => state.status === PromiseStatus.fulfilled),
rejected: computed(() => state.status === PromiseStatus.rejected),
settled: computed(() => state.status !== PromiseStatus.pending),
})
watch(
() => props.promise,
async (promise) => {
state.status = PromiseStatus.pending
state.result = null
state.error = null
try {
state.result = await promise
state.status = PromiseStatus.fulfilled
} catch (error) {
state.error = error
state.status = PromiseStatus.rejected
throw error
}
},
{
immediate: true,
},
)
return {
...toRefs(state),
calculate,
}
},
}
</script>
You can use either default slot with props or named slot for each promise state
<template>
<div>
<PromiseBuilder #default="snapshot" :promise="calculation">
<template v-if="snapshot.pending">
Calculating
</template>
<template v-else-if="snapshot.fulfilled">
{{ snapshot.result }}
</template>
<template v-else-if="snapshot.rejected">
{{ snapshot.error.message }}
</template>
<template v-if="snapshot.settled">
<button @click="calculation = calculate()">
Retry
</button>
</template>
</PromiseBuilder>
<PromiseBuilder :promise="calculation">
<template #pending>
Calculating
</template>
<template #fulfilled="{ result }">
{{ result }}
</template>
<template #rejected="{ error }">
{{ error.message }}
</template>
<template #settled>
<button @click="calculation = calculate()">
Retry
</button>
</template>
</PromiseBuilder>
</div>
</template>
<script>
import { reactive, toRefs } from '@vue/composition-api'
export default {
setup () {
const state = reactive({
calculation: calculate(),
})
async function calculate () {
await new Promise(resolve => setTimeout(resolve, 1000))
const rejected = sample([true, false])
if (rejected) {
throw new Error('error message')
}
return Math.floor(Math.random() * 100)
}
return {
...toRefs(state),
}
},
}
function sample (collection) {
return collection[Math.floor(Math.random() * collection.length)]
}
</script>
Comments
-
Neo Wakeup over 1 year
this is my first Question. I want to know how to make A Component in Vue js. The component will have functionality like that of a FutureBuilder Widget from Flutter Framework.The Component will deal with a Promise in javascript (for e.g a network call or some other promise) and return a component based on the state of the promise.I want to do this because it makes the code look a lot cleaner to ME.
A Future Builder is like this:-
FutureBuilder( future: _calculation, // a previously-obtained Future<String> or null builder: (BuildContext context, AsyncSnapshot<String> snapshot) { if (snapshot.hasData) { return AWidgetWithData();} else if (snapshot.hasError) {return ErrorWidget();} else { WidgetWhenThereIsNoInternet();}
);
It takes a Future in dart (or a Promise in javascript) and returns something depending upon the State of the Future(Promise)
so in Vuejs i want a Component which would achieve similar functionality.
<PromiseBuilder :promise="axios.get('https://api-endpoint.org')"> <div slot="hasData">Data Recieved {{data}}</div> <div slot="hasError">Error Occured! {{data}}</div> <div slot="none"> Check internet</div></PromiseBuilder>
you can shorten/improve this if you like.