Skip to content

Instantly share code, notes, and snippets.

@parallelo3301
Created August 14, 2017 19:02
Show Gist options
  • Save parallelo3301/b6ec2706b3e784a810866b5043b9f60c to your computer and use it in GitHub Desktop.
Save parallelo3301/b6ec2706b3e784a810866b5043b9f60c to your computer and use it in GitHub Desktop.
Vue Dynamic web - defined by YAML definition loaded from server
<template>
<div>
<component v-if="source"
:is="source.type || 'div'"
:items="source.data"
:config="source.config"
:query="source.query"
:dataField="source.dataField"
:pageEntityObject="pageEntityObject"
:parentBlockTitle="source.parentBlockTitle"
@update="resendUpdateEvent"
>
<EntityComponent v-if="hasRecursiveComponents(source)"
v-for="(value, key) of recursiveComponents(source)"
:key="key"
:source="value"
:pageEntityObject="pageEntityObject"
@update="resendUpdateEvent"
></EntityComponent>
</component>
<div v-else></div>
</div>
</template>
<script type="text/javascript">
import EntityMixin from '../mixins/entityComponent'
import Standard from './grid/Standard.vue'
import Full from './grid/Full.vue'
import VideoPlayer from './VideoPlayer.vue'
import HeaderStandard from './HeaderStandard.vue'
import FooterStandard from './FooterStandard.vue'
import BlockHeader from './misc/BlockHeader.vue'
import MetaTitle from './misc/MetaTitle.vue'
export default {
name: 'EntityComponent',
mixins: [EntityMixin],
props: {
pageEntityObject: {},
source: {},
},
created () {
// console.log('source', this.source)
},
methods: {
resendUpdateEvent (msg) {
this.$emit('update', msg)
},
hasRecursiveComponents (source) {
if (! source || ! source.components) {
return false
}
return true
},
recursiveComponents (source) {
// console.log('recursive components', source)
if (! this.hasRecursiveComponents(source)) {
return []
}
return source.components.map(obj => {
for (const i in obj) {
return obj[i]
}
})
},
},
components: {
// async components - defined by function - every component can be code-splitted
Feedback (resolve) { require(['./custom/Feedback.vue'], resolve) },
ImproveTopic (resolve) { require(['./custom/ImproveTopic.vue'], resolve) },
GoogleMap (resolve) { require(['./custom/GoogleMap.vue'], resolve) },
ReallyBigCustomizableSvg (resolve) { require(['./custom/ReallyBigCustomizableSvg.vue'], resolve) }, // :-)
// no code splitting - basic components
MetaTitle,
Standard,
Full,
VideoPlayer,
HeaderStandard,
FooterStandard,
BlockHeader,
},
}
</script>
<template>
<div>
<TopMenu key="menu" :config="headerMeta" />
<!--
staticke komponenty zustavaji zobrazeny pri prechodu mezi jednotlivymi strankami..
pokud je dalsi stranka po nacteni dat neobsahuje, pak zmizi, ale v dobe nacitani zde zustavaji;
daji se tim vytvaret hezke komponenty, napr. mapa - zustane vykreslena, cele okoli stranky se vsak zmeni
-->
<div key="staticComponents">
<EntityComponent v-for="(item, key) of topLevelStaticComponents" :key="key" :source="item" :pageEntityObject="loadedEntityObject" />
</div>
<div v-if="isTemplateLoading">
<div class="spacer-full"></div>
<Standard class="loader-container">
<div class="loader align-center">
<img src="~public/loader.gif" />
<div class="spacer-full"></div>
<h3>{{ $t('loader.level.' + loaderLevel) }}</h3>
</div>
</Standard>
</div>
<div v-else-if="templateLoadingFailed">
<div class="spacer-full"></div>
<Standard style="min-height: 450px">
<h1>{{ $t('loader.failed') }}</h1>
<div class="spacer-full"></div>
<ErrorLinks />
</Standard>
</div>
<div v-else>
<EntityComponent v-for="(item, key) of topLevelComponents" :key="key" :source="item" :pageEntityObject="loadedEntityObject" />
</div>
<FooterStandard key="footer" />
<Sidebar />
</div>
</template>
<script type="text/javascript">
import { mapGetters, mapActions } from 'vuex'
import TopMenu from '../components/TopMenu.vue'
import Standard from '../components/grid/Standard.vue'
import FooterStandard from '../components/FooterStandard.vue'
import Sidebar from '../components/Sidebar.vue'
import EntityComponent from '../components/EntityComponent.vue'
import ErrorLinks from '../components/custom/ErrorLinks.vue'
import { translateCollection } from '../utils'
const createId = (collection, id, $route) => {
const col = routeName === 'topics' ? routeName : collection
return translateCollection(col) + '/' + (id || 0)
}
export default {
props: ['collection', 'id'],
components: {
TopMenu,
EntityComponent,
Standard,
FooterStandard,
ErrorLinks,
Sidebar,
},
data () {
const { collection, id } = this.$route.params
const { name } = this.$route
return {
id: createId(collection, id, name),
loaderLevel: 0,
timeout: null,
}
},
metaInfo () {
return {
title: this.isTemplateLoading ? 'Loading...' : (
this.templateLoadingFailed
? 'Error'
: ''
),
}
},
computed: {
...mapGetters([
'loadedEntityObject',
'loadingEntityId',
'getMetaData',
'getCompleteTemplate',
'isTemplateLoading',
'templateLoadingFailed',
]),
topLevelComponents () {
return this.recursiveComponents(this.getCompleteTemplate)
.filter(i => i.static !== true)
},
topLevelStaticComponents () {
return this.recursiveComponents(this.getCompleteTemplate)
.filter(i => i.static === true)
},
headerMeta () {
if (this.isTemplateLoading) {
if (this.loadingEntityId === 'homepage/0' || this.loadingEntityId === 'topics/0') {
return {
header: {
variant: 'default',
},
}
}
return {}
}
return this.getMetaData
},
},
methods: {
recursiveComponents (source) {
if (! source || ! source.components) {
return []
}
return source.components.map(obj => {
for (const i in obj) {
return obj[i]
}
})
},
startLoader () {
this.loaderLevel = 0
this.timeout = setTimeout(() => {
this.loaderLevel++
this.timeout = setTimeout(() => {
this.loaderLevel++
}, 2500)
}, 1500)
},
...mapActions([
'loadTemplateData',
]),
},
// fetching data (also for server-side rendering)
asyncData ({ store, route }) {
return store.dispatch('loadTemplateData', {
entityId: createId(route.params.collection, route.params.id, route),
})
},
watch: {
'isTemplateLoading' (to) {
if (! to && this.timeout) {
clearTimeout(this.timeout)
}
},
'$route' (to, from) {
const toEntityId = createId(to.params.collection, to.params.id, this.$route)
if (this.id !== toEntityId) {
this.id = toEntityId
this.loadTemplateData({
entityId: toEntityId,
})
this.startLoader()
if (window) {
window.scrollTop = 0
}
}
},
},
}
</script>
<style lang="stylus" scoped>
.loader
padding 180px 0
</style>
<!-- Ukazka komponenty, do ktere lze vlozit dalsi uroven komponent
Zde je pomoci <slot></slot> mozne vlozit obsah do te komponenty (obdoba React children), tj. dalsi vnoreni <component> z YAMLu -->
<template>
<div class="block full">
<div class="content" :style="{ backgroundImage: bgImage, backgroundColor: bgColor }">
<slot></slot>
</div>
</div>
</template>
<script type="text/javascript">
import EntityMixin from '../mixins/entityComponent'
import BackgroundCustomizable from '../mixins/backgroundCustomizable'
export default {
mixins: [EntityMixin, BackgroundCustomizable],
}
</script>
<style lang="stylus" scoped>
@import '../../variables.styl'
// component is using global classes, so nothing is there for now
</style>
meta:
title: Toto je titulek topicu z meta dat
components:
# query po nacteni dat z api vytvori property data na vyssi urovni - tj. na stejne, jako type, config, query, atd...
# level 1 je vzdy popis pro editora
- header radek:
type: HeaderStandard
config:
buttons:
- follow
- share
query:
type: api
route: header/classes/18
- popularni v zanru:
type: FullExtended
config:
#bgImage: https://cdn.bum.pp.ua/thumbs/Mark-Hamill-Star-Wars_0.jpg
components: # seznam vnorenych komponent, opet zanorene do popisu pro editora
- hlavicka:
type: BlockHeader
data: music.genre.popular
- topicy:
type: TopicWithVideosUnderList
query:
type: api
fields:
videos: 3
route: music/genre/classes/18/interprets/popular
limit: 8
- popular v zanru featured se ctyrma:
type: Full
components:
- popis pro editora:
type: FeaturedWithFourVideos
query:
type: api
limit: 1
fields:
followers: true
videos: 4
route: music/genre/classes/18/interprets/popular
- podobne zanry s videem:
type: Full
components:
- left:
type: TopicWithVideosNextToList
query:
type: api
limit: 4
offset: 1
fields:
videos: 2
route: music/genre/classes/18/similar
- podobne zanry bez videa:
type: Full
components:
- left:
type: TopicImageCardSmallList
query:
type: api
limit: 8
offset: 5
fields:
videos: 0
route: music/genre/classes/18/similar
- pop albums by decades:
type: Full
components:
- left:
type: ListVideoCardText
query:
type: api
limit: 4
route: music/albums/genre/classes/18/decades
- pop videos:
type: Full
components:
- left:
type: ListVideoCardText
query:
type: api
route: music/genre/classes/18/videos
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment