Last active
December 9, 2018 09:40
-
-
Save piboistudios/46911dd3a7690b7d21637dbcd9c4d7f2 to your computer and use it in GitHub Desktop.
ViewData Mixin for Vue.js; requires axios
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const axios = require('axios').default; | |
const AxiosTransformer = (data, headers) => data | |
const consume = value => { | |
if (value instanceof Function) { | |
return value(); | |
} | |
else return value | |
} | |
const propOrOther = (vm, value) => { | |
const otherName = `${value.charAt(0).toUpperCase()}${value.slice(1)}`; | |
return vm[otherName] !== undefined ? vm[otherName] : vm[value]; | |
} | |
const consumePropOrOther = (vm, value) => { | |
return consume(propOrOther(vm, value)); | |
} | |
const defaultNothing = () => ({ | |
type: Object | Function, | |
default: () => null | |
}); | |
const defaultTransformation = () => ({ | |
type: Function | Array, | |
default: AxiosTransformer | |
}); | |
const data = () => ({ | |
view: { | |
data: null | |
}, | |
internal: { | |
busy: false | |
} | |
}); | |
const viewDataMixin = { | |
props: { | |
apiUrl: String | Function, | |
method: { | |
type: String | Function, | |
default: 'get', | |
}, | |
baseURL: Function, | |
transformRequest: defaultTransformation(), | |
transformResponse: defaultTransformation(), | |
headers: Object | Function, | |
params: defaultNothing(), | |
data: defaultNothing(), | |
auth: defaultNothing(), | |
lazy: { | |
type: Boolean | Function, | |
default: true, | |
}, | |
debounceTime: { | |
type: Number | Function, | |
default: -1 | |
}, | |
concatenate: Boolean | Function, | |
onError: { | |
type: Function, | |
default: console.error | |
}, | |
}, | |
computed: { | |
ViewData() { | |
const retVal = this.view.data; | |
if (!retVal && consumePropOrOther(this, "lazy")) this.GetViewData(); | |
return retVal || [] | |
}, | |
Busy() { | |
return this.internal.busy; | |
} | |
}, | |
data, | |
methods: { | |
Reset(cfg = { keepData: true, retry: false }) { | |
if (consumePropOrOther(this, "debounceTime") < 0) this.internal.busy = false; | |
const { keepData, retry } = cfg; | |
!keepData && (this.view.data = null); | |
retry && this.GetViewData(); | |
return; | |
}, | |
AddViewData(data) { | |
this.view.data instanceof Array && this.view.data.push(data); | |
}, | |
RemoveViewData(query, count = 1) { | |
if (!this.view.data instanceof Array) return; | |
for (let index in this.view.data) { | |
const object = this.view.data[index]; | |
const match = true; | |
const keys = Object.keys(query); | |
for (let index in keys) { | |
const key = keys[index]; | |
if (object[key] !== query[key]) { | |
match = false; | |
break; | |
}; | |
} | |
if (match) { | |
this.view.data.splice(index, 1); | |
if (--count === 0) break; | |
} | |
}; | |
}, | |
GetViewData() { | |
if (this.internal.busy) return; | |
this.internal.busy = true; | |
const debounceTime = consumePropOrOther(this, "debounceTime"); | |
if (debounceTime > -1) { | |
setTimeout(() => (this.internal.busy = false), debounceTime); | |
} | |
axios.request({ | |
url: consumePropOrOther(this, "apiUrl"), | |
method: consumePropOrOther(this, "method"), | |
baseURL: consumePropOrOther(this, "baseUrl"), | |
transformRequest: propOrOther(this, "transformRequest"), | |
transformResponse: propOrOther(this, "transformResponse"), | |
headers: consumePropOrOther(this, "headers"), | |
params: consumePropOrOther(this, "params"), | |
data: consumePropOrOther(this, "data"), | |
auth: consumePropOrOther(this, "auth") | |
}) | |
.then(response => { | |
if (debounceTime < 0) this.internal.busy = false; | |
const { data } = response; | |
if (data instanceof Array && consumePropOrOther(this, "concatenate")) { | |
if (!this.view.data) this.view.data = []; | |
this.view.data = this.view.data.concat(data); | |
} | |
else { | |
this.view.data = data; | |
} | |
}).catch(propOrOther(this, "onError")); | |
} | |
} | |
} | |
export default viewDataMixin; | |
/* example usage: | |
<my-component | |
api-url="/api/people" | |
:on-error="err => Reset({keepData: false, retry: true})" | |
:params="{personNamed: 'piboi'}" | |
:transform-response="(data, headers) => Object.assign({headers}, data)" | |
/> | |
MyComponent.vue: | |
<template> | |
<div class="my-component"> | |
My name is {{ ViewData.name }} | |
</div> | |
</template> | |
<script> | |
import ViewDataMixin from 'ViewDataMixin'; | |
export default: { | |
mixins: [ViewDataMixin] | |
} | |
</script> | |
*/ |
By default, using the ViewData property lazy loads the data.
You can disable this with prop ":lazy="false":
<my-component
:lazy="false"
api-url="/api/people"
:on-error="err => Reset()"
/>
MyComponent.vue:
<template>
<form @submit.prevent="GetViewData">
<input v-model="query"/>
</form>
<ul v-if="ViewData.length">
<li v-for="(element, index) in ViewData" :key="index" v-text="element"></li>
</ul>
</template>
<script>
export default {
computed: {
params() {
return { query: this.query};
}
},
data() {
return {
query: "",
}
}
}
</script>
You can alternatively override the props inside of the component itself by capitalizing the first letter:
MyComponent.vue:
import ViewData from 'Mixins/ViewData'
export default {
mixins: [ViewData],
computed: {
ApiUrl() {
return new Date().getDay() === 6 ? '/api/only-on-saturdays' : '/api/business-as-usual';
},
OnError() {
return () => this.Reset();
}
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
All props can be functions that evaluate to a value of their expected value-type: