Im working on responsive Vue.js app with Uikit. I must create 2 menus - one for desktop version, one for mobile. So I must define same menu items 2 times, first for desktop links slot, second time for mobile. I dont want define it 2x. How can I deal with this problem elegantly? Any ideas?
<template id="app">
<app-layout>
<template slot="navlinks-desktop">
<router-link to="/link1" tag="li" exact><a>Link 1</a></router-link>
<router-link to="/link2" tag="li" exact><a>Link 2</a></router-link>
</template>
<template slot="navlinks-mobile">
<router-link to="/link1" tag="li" exact><a>Link 1</a></router-link>
<router-link to="/link2" tag="li" exact><a>Link 2</a></router-link>
</template>
<transition name="fade" slot="content">
<router-view></router-view>
</transition>
<p slot="footer">Footer text</p>
</app-layout>
</template>
<template id="app-layout">
<div class="main-container">
<header class="uk-margin-bottom">
<nav class="uk-navbar uk-navbar-attached">
<div class="uk-navbar-brand uk-hidden-small">My Application</div>
<div class="uk-navbar-flip">
<ul class="uk-navbar-nav uk-hidden-small">
<slot name="navlinks-desktop"></slot>
</ul>
<div class="uk-navbar-toggle uk-button-dropdown uk-visible-small uk-dropdown-close" data-uk-dropdown="{mode: 'click'; justify: 'nav'}">
<div class="uk-dropdown uk-dropdown-navbar uk-dropdown-small">
<ul class="uk-nav uk-nav-dropdown">
<slot name="navlinks-mobile"></slot>
</ul>
</div>
</div>
</div>
<div class="uk-navbar-brand uk-navbar-center uk-visible-small">My Application</div>
</nav>
</header>
<div class="uk-container uk-container-center">
<main>
<slot name="content"></slot>
</main>
</div>
<footer class="uk-text-center fixed-bottom">
<slot name="footer"></slot>
</footer>
</div>
</template>
EDIT: So... After some research and closer look to documentation, I must say, that this specific problem DOES NOT HAVE SOLUTION... Although it is possible to reuse slots, just render it programatically, IT IS NOT POSSIBLE TO REUSE SLOT WITH ROUTER LINK.
Example - no hacks - true "Vue way" to reuse slots:
<template id="app">
<app-layout>
<template slot="myslot">
<li>THIS WORKS</li>
<li>WILL</li>
<li>WORK</li>
</template>
</app-layout>
</template>
<script>
Vue.component('app-layout', {
render: function (createElement) {
var myslot = this.$slots.myslot
return createElement('div', [
createElement('ul', myslot),
createElement('ul', myslot)
])
}
})
</script>
Its simple, elegant and readable. But, unfortunately, if you render component programatically, you cant use templates. So something like this...
<template id="app-layout">
<div class="main-container">
<header class="uk-margin-bottom">
<nav class="uk-navbar uk-navbar-attached">
<div class="uk-navbar-brand uk-hidden-small">My Application</div>
<div class="uk-navbar-flip">
<ul class="uk-navbar-nav uk-hidden-small">
<slot name="myslot"></slot>
</ul>
</div>
</nav>
</header>
</div>
</template>
...you can throw away and you must create whole structure, one by one programatically with createElement function, which is really painfull and error prone. Yes, you can use JSX, but then you must transpile it with Babel. And this is not what I want... Non working example:
<template id="app">
<app-layout>
<template slot="navlinks">
<router-link to: "/">Home</router-link>
<router-link to: "/products">Products</router-link>
<router-link to: "/about">About</router-link>
</template>
</app-layout>
</template>
<script>
Vue.component('app-layout', {
render: function (createElement) {
var navlinks = this.$slots.navlinks
return createElement('div', [
createElement('ul', navlinks),
createElement('ul', navlinks)
])
}
})
</script>
In this example, second UL will be empty. And unfortunately, from the fact perspective that in Vue all vNodes must be unique, it is perfectly normal, that it will remain empty...
So the result is that I can not use structure as above. I cant have template app-layout for which I have template with wrapping elements. Finally, I rewrote it this way:
<template id="app">
<div class="main-container">
<header class="uk-margin-bottom">
<nav class="uk-navbar uk-navbar-attached">
<div class="uk-navbar-brand uk-hidden-small">My Application</div>
<div class="uk-navbar-flip">
<ul class="uk-navbar-nav uk-hidden-small">
<router-link to="/link1" tag="li" exact><a>Link 1</a></router-link>
<router-link to="/link2" tag="li" exact><a>Link 2</a></router-link>
</ul>
<div class="uk-navbar-toggle uk-button-dropdown uk-visible-small uk-dropdown-close" data-uk-dropdown="{mode: 'click'; justify: 'nav'}">
<div class="uk-dropdown uk-dropdown-navbar uk-dropdown-small">
<ul class="uk-nav uk-nav-dropdown">
<router-link to="/link1" tag="li" exact><a>Link 1</a></router-link>
<router-link to="/link2" tag="li" exact><a>Link 2</a></router-link>
</ul>
</div>
</div>
</div>
<div class="uk-navbar-brand uk-navbar-center uk-visible-small">My Application</div>
</nav>
</header>
<div class="uk-container uk-container-center">
<main>
<transition name="fade" slot="content">
<router-view></router-view>
</transition>
</main>
</div>
<footer class="uk-text-center fixed-bottom">
<p slot="footer">Footer text</p>
</footer>
</div>
</template>
No two separate templates, no slots. But no programmatical template rendering also. So I kept my example application clean and easy to understand. And that was the point...
PS: STILL, I WILL BE VERY HAPPY, IF I AM WRONG AND SOMEONE SHOW ME HOW TO REUSE SLOT WITH ROUTER LINK.
EDIT2: I kicked Uikit and used Bulma instead. This allows me to create better, more readable structure.
<!-- APPLICATION VIEW -->
<template id="app">
<app-layout>
<template slot="navbar-items">
<router-link to="/" class="navbar-item" exact>Home</router-link>
<router-link to="/about" class="navbar-item" exact>About</router-link>
</template>
<transition name="fade" mode="out-in" slot="content">
<keep-alive>
<router-view></router-view>
</keep-alive>
</transition>
<p slot="footer">©2017 Wal De Mar</p>
</app-layout>
</template>
<!-- APPLICATION VIEW -->
<!-- TEMPLATE FOR APP VIEW -->
<template id="app-layout">
<div class="container">
<header>
<nav class="navbar">
<div class="navbar-brand">
<a href="/" class="navbar-item">BRAND</a>
<div class="navbar-burger" data-target="main-menu">
<span></span>
<span></span>
<span></span>
</div>
</div>
<div id="main-menu" class="navbar-menu">
<div class="navbar-end">
<slot name="navbar-items"></slot>
</div>
</div>
</nav>
</header>
<main>
<slot name="content"></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- TEMPLATE FOR APP VIEW -->
<script>
Vue.component('app-layout', {
template: '#app-layout'
})
new Vue({
template: '#app',
router,
}).$mount('#app')
</script>
navlinks-desktop
andnavlinks-mobile
look exactly the same. Why not use just one slot namednavlinks
for example and use it in the different menu wrappers? – Nora