There is a very interesting concept in vue.js - the so-called headless components. They are also called renderless components. The goal of these components is to maximize flexibility by highlighting the rendering logic. This is especially useful when the component contains a lot of business logic.
When we work with components in Vue.js, most of the time we make various ajax requests using axios. And often axios requests are copy-and-paste of pieces of code from a component to a component. It is often a bad idea to use mixins in this case. sometimes we need to change request parameters, address, and so on. Therefore, headless components will help us here.
Let's remember how requests are sent using axios:
axios.get('https://jsonplaceholder.typicode.com/posts/1')
.then(repsponse => {
console.log(response.data);
})
.catch(error => {
console.error(error);
})
For a given public API, the response is:
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
To get the title, we need to refer to the object in the following path: response.data.title.
Armed with this knowledge, we can write a special request component whose task will be to send a request, process data. We can use the component as follows:
<request url="https://jsonplaceholder.typicode.com/posts/1">
<template v-slot:default="{ loading, data }">
<div v-if="loading">Loading...</div>
<div v-else>
<h1>
{{data.title}}
</h1>
<p>
{{data.body}}
</p>
</div>
</template>
</request>
In our example, the request component uses the url input parameter, to which the component sends the request.
In response from this component, we receive only the processed data in data, but also the loading parameter, by which we determine whether to show the loading indicator or not. Only after the download is complete, we show the response data.
Vue.component("Request", {
render() {
return this.$scopedSlots.default({
loading: this.loading,
data: this.response && this.response.data
})[0];
},
props: {
url: String
},
data() {
return {
loading: true,
response: null
}
},
created() {
axios.get(this.url)
.then(response => {
this.response = response;
this.loading = false;
})
}
});
In this very simple example, we only accept url as an input parameter and control the response data, the loading state. The entire procedure for sending an ajax request takes place in the created hook. Its task includes not only sending the request, but also processing it, saving the response, changing the loading flag. After processing the data, all information is sent via scoped slots to the parent component.
There is one very important detail - our component has no internal state. As in a regular component, we can initiate events, and in the parent component, subscribe to them.
And you must admit, it's very convenient that now we can use this large component with all the business logic for processing ajax requests an unlimited number of times with different types of templates. After all, notice that the component does not have any template rendering at all. It is carried out in the parent component, and here we only pass the data up