Reputation: 3403
I am trying to do some conditional templating where I have to separated the opening and closing tags of some elements. But can't get it to work until they are in the same conditional template tag. As soon as I put the opening tag to one conditional template and the closing tag to another conditional template I get an error. For example:
<template>
<div>
<template v-if="show">
<ul>
<li>
one
</li>
</template>
// OTHER CONDITIONAL STUFF IN BETWEEN
<template v-if="show">
<li>
two
</li>
</ul>
</template>
</div>
</template>
<script>
export default {
data() {
return {
show: false
}
}
}
</script>
Here I get an error because the opening <ul>
tag and closing </ul>
tag are in discrete <template v-if="..">
tags. I get this error:
(Emitted value instead of an instance of Error)
Error compiling template:
<div>
<template v-if="show">
<ul>
<li>
one
</li>
</template>
<template v-if="show">
<li>
two
</li>
</ul>
</template>
</div>
- tag <ul> has no matching end tag.
How can I separate any starting and ending tags inside conditional template tags without breaking the code?
This is the routes that I want to use to generate the menu
// routers.js
export let routers = [
{
name: 'Main Menu 1',
parent: 0,
}, {
name: 'Main Menu 2',
parent: 0
children: [
{
name: 'Menu Item 1-1'
},{
name: 'Menu Item 1-2',
children: [
{
name: 'Menu Item 2-1',
},{
name: 'Menu Item 2-2',
},{
name: 'Menu Item 2-3',
children: [{
name: 'SHIT'
}]
}
]
}
]
}, {
name: 'Main Menu 3',
parent: 0
}
];
This is the parent of the recursive component.
// left-side.vue
<template>
<aside class="left-side sidebar-offcanvas">
<section class="sidebar">
<div id="menu" role="navigation">
<navigation-cmp :routes="routes"></navigation-cmp>
</div>
</section>
</aside>
</template>
<script>
import navigationCmp from './navigationCmp';
import {routers} from '../../router/routers';
export default {
name: "left-side",
computed: {
routes(){
return routers;
}
},
components: {
navigationCmp,
},
}
</script>
This is the recurring part with the problem
// navigationCmp.vue
<template>
<ul class="navigation">
<template v-for="item in routes">
<template v-if="item.parent == 0">
<template v-if="!!item.children">
<li class="menu-dropdown">
<a href="javascript:void(0)">
<i class="menu-icon ti-check-box"></i>
<span class="mm-text">{{ item.name }}</span>
<span class="fa arrow"></span>
</a>
<ul class="sub-menu">
</template>
<template v-if="!item.children">
<router-link to="/" tag="li" exact>
<a class="logo"><i class="menu-icon ti-desktop"></i><span class="mm-text">{{ item.name }}</span></a>
</router-link>
</template>
</template>
<template v-if="!!item.children" v-for="child in item.children" >
<template v-if="!!child.children">
<a href="javascript:void(0)">
<i class="fa fa-fw ti-receipt"></i> {{ child.name }}
<span class="fa arrow"></span>
</a>
<ul class="sub-menu form-submenu">
</template>
<template v-if="!child.cildren">
<router-link tag="li" to="/form-elements" exact>
<a class="logo"><i class="menu-icon ti-cup"></i><span class="mm-text"> {{ child.name }} </span></a>
</router-link>
</template>
<navigation-cmp v-if='!!child.children&&child.children.length>0' :routes='[child]'> </navigation-cmp>
<template v-if="!!child.children">
</ul>
</template>
</template>
<template v-if="!!item.children&&item.parent==0">
</ul>
</li>
</template>
</template>
</ul>
</template>
<script>
export default {
name: 'navigation-cmp',
props: {
routes: Array,
}
}
</script>
main.js:43552 [WDS] Errors while compiling. Reload prevented.
main.js:43558 ./~/vue-loader/lib/template-compiler?{"id":"data-v-dfd6e000"}!./~/vue-loader/lib/selector.js?type=template&index=0!./src/components/layout/navigationCmp.vue
(Emitted value instead of an instance of Error)
Error compiling template:
<ul class="navigation">
<template v-if="!item.hidden" v-for="item in routes">
<template v-if="item.parent == 0">
<template v-if="show">
<li class="menu-dropdown">
<a href="javascript:void(0)">
<i class="menu-icon ti-check-box"></i>
<span class="mm-text">{{ item.name }}</span>
<span class="fa arrow"></span>
</a>
<!-- <ul class="sub-menu"> -->
</template>
<template v-if="!item.children">
<router-link to="/" tag="li" exact>
<a class="logo"><i class="menu-icon ti-desktop"></i><span class="mm-text">{{ item.name }}</span></a>
</router-link>
</template>
</template>
</li>
<!-- <template v-if="!!item.children&&item.parent == 0"> -->
<!-- </ul> -->
<!-- </template> -->
</template>
</ul>
- tag <li> has no matching end tag.
@ ./src/components/layout/navigationCmp.vue 5:2-192
@ ./~/babel-loader/lib?cacheDirectory!./~/vue-loader/lib/selector.js?type=script&index=0!./src/components/layout/left-side.vue
@ ./src/components/layout/left-side.vue
@ ./~/babel-loader/lib?cacheDirectory!./~/vue-loader/lib/selector.js?type=script&index=0!./src/components/layout/layout.vue
@ ./src/components/layout/layout.vue
@ ./src/router/routes.js
@ ./src/router/router.js
@ ./src/main.js
@ multi (webpack)-dev-server/client?http://localhost:8080 webpack/hot/dev-server ./src/main.js
errors @ main.js:43558
sock.onmessage @ main.js:43801
./node_modules/sockjs-client/lib/event/eventtarget.js.EventTarget.dispatchEvent @ main.js:22579
(anonymous) @ main.js:23332
./node_modules/sockjs-client/lib/main.js.SockJS._transportMessage @ main.js:23330
./node_modules/sockjs-client/lib/event/emitter.js.EventEmitter.emit @ main.js:22483
WebSocketTransport.ws.onmessage
Upvotes: 6
Views: 9075
Reputation: 3403
Okay I couldn't find out why I get the "- tag has no matching end tag" errors. But there is no doubt that it should work as @RoyJ showed the separating tags with conditional () rendering. My guess it that when multiple recursions happen, some opening and closing tags are created (separated) in different scopes because each recursion starts like a new component. Thus vue can't find the relevant closing tags in the corresponding scope and throws that error.
My workaround is to don't separate the closing tags from the starting tags. Instead I created an additional component that I place inside the main loop and after I enter that component I close the tags without additional conditional renderings. and the new component handles the remaining recursions. I will go with this one but keep an eye on the problem. Thanks for all help effort.
Here my workaround:
// left-side.vue
<template>
<aside class="left-side sidebar-offcanvas">
<section class="sidebar">
<div id="menu" role="navigation">
<navigation-cmp></navigation-cmp>
</div>
</section>
</aside>
</template>
<script>
import navigationCmp from './navigationCmp';
export default {
name: "left-side",
components: {
navigationCmp,
},
}
</script>
I removed the recursion from this part and as I said closed the tags in one single template tag without separating them.
// navigationCmp.vue
<template>
<ul class="navigation">
<template v-for="route in routes">
<li v-if="!!route.children" class="menu-dropdown">
<a href="javascript:void(0)">
<i class="menu-icon ti-check-box"></i>
<span class="mm-text">{{ route.name }}</span>
<span class="fa arrow"></span>
</a>
<ul class="sub-menu">
<navigation-sub :route="route"></navigation-sub>
</ul>
</li>
<router-link v-else to="/" tag="li" exact>
<a class="logo"><i class="menu-icon ti-desktop"></i><span class="mm-text">{{ route.name }}</span></a>
</router-link>
</template>
</ul>
</template>
<script>
import {routers} from '../../router/routers';
import navigationSub from './navigationSub';
export default {
computed: {
routes(){
return routers;
}
},
components: {
navigationSub
}
}
</script>
This is the new component I told. this is now the recurring part.
// navigationSub.vue
<template>
<span>
<template v-for="child in route.children">
<li v-if="!!child.children">
<a href="javascript:void(0)">
<i class="fa fa-fw ti-receipt"></i> {{ child.name }}
<span class="fa arrow"></span>
</a>
<ul class="sub-menu form-submenu">
<navigation-sub :route="child"></navigation-sub>
</ul>
</li>
<a class="logo"><i class="menu-icon ti-cup"></i><span class="mm-text">{{ child.name }}</span></a>
</router-link>
</template>
</span>
</template>
<script>
export default {
name: 'navigation-sub',
props: ['route']
}
</script>
Upvotes: 0
Reputation: 43881
This actually works fine for me. Using templates is a recommended way of circumventing the legal-HTML restrictions. Can you make a snippet that exhibits the problem? And say on what platform you're running it, just in case it's platform-dependent?
var spec = {
template: '#nav-template',
props: {
routes: Array,
}
};
new Vue({
el: '#app',
data: {
routes: [{
parent: 0,
children: 1,
name: 'first'
}, {
parent: 1,
children: 0,
name: 'second'
}]
},
components: {
navigationCmp: spec
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
// navigationCmp.vue
<div id="app">
<aside class="left-side sidebar-offcanvas">
<section class="sidebar">
<div id="menu" role="navigation">
<navigation-cmp :routes="routes"></navigation-cmp>
</div>
</section>
</aside>
</div>
<template id="nav-template">
<ul class=" navigation ">
<template v-for="item in routes ">
<template v-if="item.parent==0 ">
<template v-if="!!item.children ">
<li class="menu-dropdown ">
<a href="javascript:void(0) ">
<i class="menu-icon ti-check-box "></i>
<span class="mm-text ">{{ item.name }}</span>
<span class="fa arrow "></span>
</a>
<ul class="sub-menu ">
</template>
<template v-if="!item.children ">
<router-link to="/ " tag="li " exact>
<a class="logo "><i class="menu-icon ti-desktop "></i><span class="mm-text ">{{ item.name }}</span></a>
</router-link>
</template>
</template>
<template v-if="!!item.children " v-for="child in item.children ">
<template v-if="!!child.children ">
<a href="javascript:void(0) ">
<i class="fa fa-fw ti-receipt "></i> {{ child.name }}
<span class="fa arrow "></span>
</a>
<ul class="sub-menu form-submenu ">
</template>
<template v-if="!child.cildren ">
<router-link tag="li " to="/form-elements " exact>
<a class="logo "><i class="menu-icon ti-cup "></i><span class="mm-text "> {{ child.name }} </span></a>
</router-link>
</template>
<navigation-cmp v-if='!!child.children&&child.children.length>0' :routes='[child]'> </navigation-cmp>
<template v-if="!!child.children ">
</ul>
</template>
</template>
<template v-if="!!item.children&&item.parent==0 ">
</ul>
</li>
</template>
</template>
</ul>
</template>
Upvotes: 1
Reputation: 2398
As per html standard vue complier will check for proper elements opening and closing tag . In your case opening tag <ul>
not properly handled.
You can update the your code as folows
<template>
<div>
<template v-if="show">
<ul>
<li>
one
</li>
</ul>
</template>
// OTHER CONDITIONAL STUFF IN BETWEEN
<template v-if="show">
<ul>
<li>
two
</li>
</ul>
</template>
</div>
</template>
<script>
export default {
data() {
return {
show: false
}
}
}
</script>
Upvotes: 0