Reputation:
I am trying to render components based on the data that is passed to the parent page.
I am trying to use dynamic components to render the correct components based on the JSON object @type and grab the file name in the url (without the extension) of that object to then render the related component and pass the data from Page.vue to the rendered components.
If I set :is="content.@type" (as an example) then the output will render child components with the name Image & Video (removing everything in the URL expect for the filename) to render the correct component.
Is this possible or is there a better approach to this?
Data:
Content: object
Content [Array]
0:Object
@id: "4effb045"
@type: "http://url/images.json"
_meta: Object
name: "TestingImage"
1:Object
@id: "4effb045"
@type: "http://url/video.json"
_meta: Object
name: "TestingVideo"
The data comes from a CMS and I'm not able to change the data.
Page.vue
<template>
<div id="page" v-if="content" class="page-wrapper container" :cid="this.$root.cidItem">
<div class="row">
<div class="page__items">
<component v-for="content in content.content" :is="content.@type" :key="content.id"></component>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import Images from "./components/Images"
import Video from "./components/Video"
import TextType from "./components/TextType"
import Card from "./components/Card"
export default {
name: 'page',
props: ['cid'],
components: {
Images,
Video,
TextType,
Card
},
mounted () {
axios({
method: 'GET',
'url': this.$root.contentDeliveryUrl + this.$root.encodedQuery + this.$root.cidItem + this.$root.store
}).then(result => {
// eslint-disable-next-line
this.content = amp.inlineContent(result.data)[0];
console.log(this.content)
}, error => {
console.error(error)
})
},
data () {
return {
content: []
}
}
}
</script>
<style lang="scss">
@import '../node_modules/bootstrap-css-only/css/bootstrap.min.css';
.container {
max-width: 1366px;
}
</style>
Child Component Example Images.vue
<template>
<div class="images-wrapper container">
<div class="row">
<div class="col">
<div class="images">
{{content.imageAltText}}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Images'
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.images-wrapper {
&__image {
}
}
</style>
Upvotes: 2
Views: 2665
Reputation: 9190
As mentioned in the comment, you could parse the URL in a method with v-bind:is
directive. You've probably figured it out by now, but here's my quick attempt.
const content = {
content: [{
'@id': '4effb045',
'@type': 'http://url/images.json',
_meta: {
name: 'TestingImage'
}
}, {
'@id': '4effb046',
'@type': 'http://url/videos.json',
_meta: {
name: 'TestingVideo'
}
}]
}
new Vue({
el: '#app',
data() {
return {
content
}
},
methods: {
extractName(content) {
const [name] = /[^\/]*(?=\.\w+$)/.exec(content['@type']);
return name;
}
},
components: {
Images: {
template: '<img src="https://via.placeholder.com/150x45" />'
},
Videos: {
template: '<div>Some video component</div>'
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<component
v-for="content in content.content"
:is="extractName(content)"
:key="content['@id']">
</component>
</div>
BTW, I would advise against using HTML reserved words as the component names (like in your case <video>
) as it may fail to compile. See this related issue on GitHub.
[Vue warn]: Do not use built-in or reserved HTML elements as component id: Dialog
Upvotes: 2
Reputation: 468
Your approach is correct. You can use the is
directive to call your components. It is the best way.
As you asked, you need a method in your Page.vue
to parse your @type
.
methods: {
parseType: function(type) {
let typeParts = type.split("/")
let filenameParts = typeParts[typeParts.length - 1].split('.')
let parsedType = filenameParts[0]
return parsedType
}
}
Now you can use this function in your template.
<component v-for="content in content.content" :is="parseType(content.@type)" :key="content.id"></component>
Upvotes: 1