JSFiddle - React, Tailwind, and code Playground
by Simon Herteby
HTML
<div id="vue">
<input v-model="string">
<input v-model="string2">
<wait :for="one">
<div>
{{one}}
</div>
</wait>
<hr>
<wait :for="[one, two]">
<ul slot-scope="[one, two]">
<li>{{one}}</li>
<li>{{two}}</li>
</ul>
</wait>
<hr>
<wait :for="fail">
<div slot-scope="fail">
{{fail}}
</div>
</wait>
<hr>
<wait :for="[one, two, fail]">
<ul slot-scope="[one, two, fail]">
<li>{{one}}</li>
<li>{{two}}</li>
<li>{{fail}}</li>
</ul>
</wait>
</div>
Vue
const Spinner = {
template:`<div>LOADING</div>`
}
const ErrorDisplay = {
template:`
<div class="error">
<h2>{{header}}</h2>
<p>{{body}}</p>
</div>
`,
props:{
error:{
type:Error,
required:true
}
},
computed:{
header(){
return this.error.message
},
body(){
}
}
}
Vue.component('wait', {
render(h){
if(this.error){
return h(ErrorDisplay, {props:{error:this.error}})
} else if(this.resolved) {
if(!this.$scopedSlots.default){
throw new Error('Missing scoped slot')
}
return this.$scopedSlots.default(this.result)
} else if(this.spinner){
return h(Spinner)
}
},
props:{
for:{
validator: prop => {
if(prop instanceof Array){
return !prop.find(item => typeof item.then !== 'function')
} else {
return typeof prop.then === 'function'
}
}
},
spinner:{
type:Boolean,
default:false
}
},
data(){
return {
resolved:false,
result:null,
error:null
}
},
computed:{
promise(){
return this.for instanceof Array ? Promise.all(this.for) : this.for
}
},
watch:{
promise:{
handler(promise){
this.resolved = false
this.result = null
this.error = null
promise.then(result => {
if(promise === this.promise){
this.resolved = true
this.result = result
}
}).catch(error => {
if(promise === this.promise){
this.error = error
}
})
},
immediate:true
}
}
})
function sleep(ms){
return new Promise(resolve => setTimeout(resolve, ms))
}
new Vue({
el:'#vue',
data(){
return {
string:'foo',
string2:'bar'
}
},
computed:{
async one(){
return 'ONE'
},
async two(){
const {string, string2} = this
await sleep(1000)
return 'TWO ' + string + string2
},
async fail(){
...