How to bind slot with v-bind for url attribute? - vue.js

How to pass attributes from component to slot?
This is in component
<navigation-link url="/profile">
Your Profile
</navigation-link>
Then in the template I want to use url
<template>
<div>
<a
v-bind:href="url"
class="nav-link">
<slot></slot>
</a>
<span class="active></span>
<div>
</template>
according to docs this should work, but instead I get error: Property or method "url" is not defined on the instance but referenced during render.

I think you want to put the text "Your Profile" in <slot></slot>
So, for this, you need to do this.
<navigation-link url="/profile">
<template slot="text">
Your Profile
</template>
</navigation-link>
And in the component, name the slot like this
<template>
<div>
<a
v-bind:href="url"
class="nav-link">
<slot name="text" />
</a>
<span class="active></span>
<div>
</template>
Probably, this work.
EDIT:
according to docs this should work, but instead I get error: Property or method "url" is not defined on the instance but referenced during render.
That is because, you are not declaring url in the component, you probably need to put
props: ['url'],
data() {
return {
propUrl: this.url
}
}
and in the template use this.propUrl
Regards

Related

How to reference a Stenciljs <slot /> from the HTML page?

In case I have multiple <slot />s nested inside my Stencil component, I would need to give them names, and then reference them inside my HTML page when using that component. So, how do I do that?
Example:
render() {
return(
<div>
<button><slot name="one" /></button>
<select>
<option value="one"><slot name="two" /></option>
</select>
<p> <slot name="three" /></p>
</div>
)
}
And then when I am adding this component, how do I add content to each <slot />?
I have tried what is explained here: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot
but it's not working!
I have tried the following in my HTML page:
<span slot="two">dfdf</span>
<slot name="two"><span>gdgdg</span></slot>
Neither work!
The example in the MDN documentation is about as clear as you can get.
I only deleted content from the example <template> to focus on just the <slot> elements:
customElements.define("slot-example", class extends HTMLElement{
constructor(){
super()
.attachShadow({mode:"open"})
.append(document.getElementById(this.nodeName).content.cloneNode(true));
}
});
<template id="SLOT-EXAMPLE">
<style>span { color:red }</style>
<slot name="element-name">NEED NAME</slot>
<span><slot name="description">NEED DESCRIPTION</slot></span>
<slot name="attributes"><p>None</p></slot>
</template>
<slot-example>
<span slot="element-name">slot-example</span>
<span slot="description">This demonstrates slot</span>
</slot-example>
<slot-example>
<b slot="element-name">another slot-example</b>
<b slot="attributes">Explain to yourself why this is bold and not a paragraph</slot>
</slot-example>
For a deep, deep, very deep dive into styling slots with ::slotted see: ::slotted CSS selector for nested children in shadowDOM slot
Addition after comments
Here some inspiration to get you started:
customElements.define("my-select", class extends HTMLElement{
connectedCallback(){
setTimeout(()=>{ // ! wait till innerHTML is parsed
this.innerHTML =
"<SELECT>" +
this.innerHTML
.split("\n")
.filter(Boolean)
.map(company=>`<OPTION>${company.trim()}</OPTION>`);
"</SELECT>";
});
}
});
<my-select>
Apple
Microsoft
Google
Mozilla
</my-select>

My dynamic component (layout) doesn't work with named slots in vuejs

I have problems to combine dynamic generated layouts with named slots.
To define my layouts I'm using "component :is"
//app.vue
<template>
<component :is="layout">
<router-view />
</component>
</template>
<script>
computed: {
layout() {
const layout = this.$route.meta.layout || 'default'
return () => import(`#/app/layouts/${layout}.vue`)
}
},
</script>
//layouts/default.vue
<template>
<div>
<div>
<slot name="header" />
</div>
<div>
<div>
<slot name="sidebar" />
</div>
<div>
<slot name="default"/>
</div>
</div>
</div>
</template>
// views/page.vue
<template>
<div>
<template #header>
<h1>Primitives</h1>
</template>
<template #sidebar>
<ul>
<li v-for="primitive in sections.sections" :key="primitive">
<router-link :to="`/primitives/${primitive}`">{{primitive}}</router-link>
</li>
</ul>
</template>
<template #default>
<router-view :key="$router.path" />
</template>
</div>
</template>
But now I get this error inside my code
'v-slot' directive must be owned by a custom element, but 'div' is not.
and console displays this error
<\template v-slot> can only appear at the root level inside the receiving component
If I remove the main div I get the error
The template root requires exactly one element.
What I'm doing wrong?
This is not easy to explain so please cope with me...
I really understand what you are trying to do but unfortunately it is not possible in Vue.
Reason for that is slots are more template compiler feature than runtime feature of Vue. What I mean by that ? When Vue template compiler sees something like <template #header>, it will take the inner content and compile it into a function returning virtual DOM elements. This function must be passed to some component which can call it and include the result in it's own virtual DOM it is generating. To do that template compiler needs to know to what component it should pass the function (that is the real meaning of 'v-slot' directive must be owned by a custom element, but 'div' is not. error message...ie compiler is "looking" for a component to pass the slot content to...)
But you are trying to use the slots as if they were "discoverable" at runtime. For your code to work the dynamic layout component must at runtime somehow discover that it's child (also dynamic thanks to <router-view />) has some slot content it can use. And this is not how slots work in Vue. You can pass the slot content your component receives from parent to a child components but do not expect that parent component (layout in this case) can "discover" slot content defined in it's child components...
Unfortunately only solution for your problem is to import the layout component in every "page" and use it as a root element in the template. You can use mixins to reduce code duplication (to define layout computed)
#/mixins/withLayout.js
export default = {
computed: {
layout() {
const layout = this.$route.meta.layout || 'default'
return () => import(`#/app/layouts/${layout}.vue`)
}
}
}
views/page.vue
<template>
<component :is="layout">
<template #header>
<h1>Primitives</h1>
</template>
<template #sidebar>
<ul>
<li v-for="primitive in sections.sections" :key="primitive">
<router-link :to="`/primitives/${primitive}`">{{primitive}}</router-link>
</li>
</ul>
</template>
<template #default>
<router-view :key="$router.path" />
</template>
</component>
</template>
<script>
import withLayout from '#/mixins/withLayout'
export default {
mixins: [withLayout]
}
</script>

Vue convert string to dom (Nuxt.js)

I try to add dom in vue element, but it doesnt work.
here is my code,
<div v-bar="{preventParentScroll: true}">
<div>
<el-menu
ref="menu"
default-active=""
class="lnbmenu"
:unique-opened="true">
**{{ this.menuList}}**
</el-menu>
</div>
and next, js code.
computed: {
menuList(){
~~~ reducer ~~~
return dom
*//return
<el-submenu index="M0010">
<template slot='title'>
<span>menu1</span>
</template>
<el-menu-item index="M0018">
<nuxt-link to="tempMenu">
<span>menu2</span>
</nuxt-link>
</el-menu-item>
</el-submenu>*
}
}
I tried to use 'v-html' in el-menu tag, but still not work.
how can I add this thing?
Normally, you manipulate the DOM in Vue with the $refs keyword.
Have a look: https://codingexplained.com/coding/front-end/vue-js/accessing-dom-refs

control over inherited attributes by vue component

Is there a way to have control over attributes provided through the component tag?
For example:
<my-component class="myClass" style="myStyle"></my-component>
My component:
<template>
<div>
<div>
</div>
<div>
</div>
</div>
</template>
At render Vue applies given attributes on the root:
<div class="myClass" style="myStyle">
<div>
</div>
<div>
</div>
</div>
I want to control where those attributes are applied like so:
<div>
<div>
</div>
<div class="myClass" style="myStyle">
</div>
</div>
#Boussadjra Brahim answer is definitely one way to handle it, however this will require you to pass in all of the class attributes you want everytime you define the component.
This question is answered in this SO post already as well.How to style a nested component from its parent component in Vuejs?
If you want a bit more flexibility I would suggested using interpolation and properties as below. This will let you define some default classes and pass in whatever else in addition.
<app-header :headerclass="parent-header-class"> </app-header>
Inside of your child component, you can use these properties and v-bind the class inside the HTML, as shown in the example below:
<template>
<div :class=`${headerClass} internal-class-example button`> </div>
</template>
Note: This does not allow you to use any scoped parent CSS to pass to the child. The classes you pass down must be global. Otherwise, the child component will not know what it is.

Vue.js: Loading template (or div) when user clicks button?

So I currently have a template sitting in a ".vue" file like so:
<template>
<div id="dataAttachToMe"></div>
</template>
I don't want this to load, unless a user clicks a button, something like
<button #click="loadTheTemplateAbove">See Data</button>
I've tried using this example:https://v2.vuejs.org/v2/guide/conditional.html#Controlling-Reusable-Elements-with-key. But it says something like "Component template should contain exactly one root element" in the error message.
I need more than a show/hide here I think, something that can initiate the template dynamically.
<template>
<div id="data">
<button #click="loadTemplate">Load the template</button>
<div v-if="buttonClicked">
<div id="dataAttachedToThisDiv"></div>
</div>
</div>
</template>
The error you are getting, means that there is more than one root element inside <template></template> tag.
It is required in Vue.js (and other template based frameworks/libraries) to have only one root element.
This will NOT work:
<template>
<div id="dataAttachToMe"></div>
<button #click="loadTheTemplateAbove">See Data</button>
</template>
This will work:
<template>
<div id="someRootDiv">
<div id="dataAttachToMe">Some data</div>
<button #click="loadTheTemplateAbove">See Data</button>
</div>
</template>
Here is a code example (App.vue) of what you are trying to achieve:
Basic idea: we have to create a variable, that will be changed upon button click. We add v-if directive that depends on that variable and will handle element's visibility.
Welcome to StackOverflow. When you get the error Component template should contain exactly one root element it means that you can only have one root element in your template. You can fix that error by wrapping everything in a blank div like so
<template>
<div>
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</template>
</div>
</template>
Please edit your post and place you <script> tag. Conditional Rendering requires a data field of a boolean that you can place in your if statement on your template
<template>
<div>
<div v-if="show">{{message}}</div>
<div v-if="#show">Not Showing when show is set to false</div>
<button v-on:click="show = true">Show</button>
</div>
</template>
<script>
module.exports {
data: function () {
message: 'Hello Vue!',
show: false
}
}
</script>