StencilJS Rendering nested componentes - javascript

I have created two web components that are nested together. The main problem that I am facing is when I try to render the child component, everything from the parent gets overlapped.
I think that the issue is that both parent component and child component are rendering <slot/>.
As you can see the parent component renders <slot></slot>. And the child component renders each of its slots by there name as well, the only "solution" that I found is to set shadow to true(shadow:true) in the child element, that way only the child <slot/> is shown and it doesn't get overlapped by the parent. But that is not working for me because inside my child component I would like to render another component, but since this is shadow DOM it does not show anything.
Any ideas?, thanks.
<parent-component>
<child-component>
<div slot='title'>title</div>
<div slot='subtitle'>subtitle</div>
</child-component>
</parent-component>
#Component({
tag: 'child-component'
shadow: false
})
export class ChildComponent{
//my-logic
render(){
return(
<div>
<slot name='title'/>
<slot name='subtitle'/>
<external-component></external-component>
</div>
)}
}
}
#Component({
tag: 'parent-component'
shadow: false
})
export class ParentComponent{
//my-logic
render(){
return(
<div>
<slot></slot>
</div>
)}
}
}

Related

Extraneous non-props attributes (title) were passed to component but could not be

runtime-core.esm-bundler.js?d2dd:38 [Vue warn]: Extraneous non-props attributes (title) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.
at <ProductTable title="Product List" >
at <Home onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > >
at <RouterView>
at <App>
This is the error I get in the chrome console of my Vue app. Below is my parent view component. I am trying to add multiple components to it, e.g home content and footer.
<template>
<div class="home">
<ProductTable title="Product List"/>
<Footer title="I am the child"/>
</div>
</template>
<script>
import ProductTable from '#/components/ProductTable.vue'
import Footer from '#/components/Footer.vue'
import Functions from '#/components/ProductListFunctions.js'
export default {
name: 'Home',
components: {
ProductTable,
Footer
}
}
</script>
Any help is appreciated as I cant figure it out. The error is just a warning and doesn't effect any of the page. But it would be nice to be gone. Cheers.
You should add inheritAttrs:false to the child components:
export default{
inheritAttrs:false
...
}

use same component with same props name in on page not works vuecli

Well. i have a component called Logo and i am using it in nearly every views and even in other components. Logo component takes only 1 props: "size" and i use javascript to mack it responsive depend on its font size but => for example i have a landing:
in landing i have component renderd in landing: navbar that contains a "logo component" inside OF HIM. and same time i use logo component in my landing view as well:
LOGO COMPONENT
<template lang="html">
<div class="logoCon">
<a class="logo">Some name for Logo</a>
</div>
</template>
<script>
export default{
props: ['size'],
name: 'Logo',
methods: {
logoSizing(size){
// java script code for make the sizing right.
}
},
created(){
// calling logoSizing function.
this.logoSizing(this.size);
// for adding the font size.
document.querySelector(".logo").style.fontSize = (this.size + "px");
}
}
</script>
NAVBAR COMPONENT
<template lang="html">
<div class="navbarCon">
//some code for navbar. and inside the navbar we have logo component:
<logo :size="logoSize" />
</div>
</template>
<script>
export default{
name: 'Navbar',
data: () => ({
logoSize: "20"
})
}
</script>
and the last one LANDING COMPONENT
<template lang="html">
<div class="navbarCon">
// navbar component
<navbar />
// we have logo component:
<logo :size="logoSize" />
</div>
</template>
<script>
export default{
name: 'Landing',
data: () => ({
logoSize: "400"
}),
components: {
navbar,
logo
}
}
</script>
so now if i run the code the "logoSize" varible dont work for each in seperate ways and get only one of the logo components, mostly just the navbar and the logo component in landing it gets no style at all from my java script.
=>how can i use the logo component multiple times in one page and works for each in seprate ways
THIS ISSUE IS NOT ONLY FOR MY LOGO COMPONENT ALL OF MY COMPONENTS ARE HAVE THE SAME PROBLME PLEASE HELP ME SOLVE IT.... I WANT TO CRY
So considering that your logoSizing(size) doesn't change the size value (because you souldn't mutate props because when you do, you change it in the parent component as well and may lead to inconsistent changes), I would say that maybe your components are being renderes with the same id (which sound kinda weird).
To solve that "problem", (and again this shouldn't happen unless you're forcing it somehow) give to the components different key's like
<logo :size="400" :key="navbarLogo"/>
<logo :size="300" :key="appbarLogo"/>
This forces your components to have a different ID in DOM.
But a codepen would be really handy

Render external element inside Vue component

I have an external div that I need to render inside my Vue app. I'm trying to use a slot, like but that's a no go as nothing renders.
Any ideas?
Goal is to have HTML like this (Vue mounts on #app):
<div id="app" data-slot-header="#header"></div>
<div id="header">
<h1>Title here</h1>
</div>
Then the Vue component
<template>
<div>
<slot name="header"></slot>
</div>
</template>
You can use a dynamic <component> and refer to your #header element as a template reference.
For example
new Vue({
data: () => ({
headerComponent: {
template: '#header' // refer to template element by selector
}
}),
}).$mount('#app')
#app:before,#header:before{position:absolute;top:0;right:0;color:rgba(1,1,1,.5);font-size:.8rem}#app{border:1px solid #666;position:relative}#app:before{content:'Vue app'}#header{position:relative;opacity:.5}#header:before{content:'Original header'}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<div id="app">
<p>Dynamic component rendered here 👇</p>
<component :is="headerComponent"></component>
</div>
<div id="header">
<h1>Title here</h1>
</div>
Slots are mainly used with reusable Vue components so that the parent component can render custom stuff inside designated sections of the child. The root component does not have a parent, so it doesn't make sense to use slots for this.
Why can't you just hard-code the div in the template? Or do you need it to be dynamic; will you be swapping out the header contents in some situations? Please provide more information about what your use-case is, otherwise my answer is "just hard-code it".
Take a look at portal-vue. It allows child components to render templates anywhere in the DOM. This might work for your situation.

How to access elements in a child component <slot>?

I have a component which uses named slots from <another-component>. I was trying to get the <span> by calling this.shadowRoot.getElementById('title') in <another-component>, it always returns null:
render() {
return html`
<another-component>
<h2 slot="header">
<span id="title"></span>
</h2>
<div slot="footer">
${this.renderFooter()}
</div>
</another-component>
`;
}
Any ideas?
After some digging, i found this is by design. Please correct me if i'm wrong. Basically, you can only access a DOM element under a shadow root where it's defined no matter if it's in a <slot> or not.
For example, let's say in your main component, you have a component A with an element with an ID:
// main component's render
<a>
<p id="title"></p>
</a>
Component A uses component B, and maps its slot content to another slot in component B:
// component A's render
<b>
<p slot="header">blabla</p>
<slot slot="content"></slot>
</b>
Neither A nor B can access the element via shadowRoot.getElementById. Only the main component can get the element by ID.

Vue - Passing slot to child component

The image I use below are svg component loaded with vue-svg-loader (see app.vue)
I have 2 components parent and child.
The parent use the child and display an image from the child with a text.
The child can be used without the parent and only display an image.
Both use a named slot to display the image, the issue is I want to pass a named slot from the parent component to the child one, but seems like to not work the way I want with vuejs named slot and start to be very confusing, is there another way to solve this issue ?
Parent.vue
<template>
<child>
<label>Text with image</label>
<slot name="img">
</child>
<template>
Child.vue
<template>
<slot name="img">
<template>
App.vue
<template>
<parent>
<mySvg slot="img"/>
</parent>
<template>
<script>
import mySvg from 'pathToSvg';
export default {
components: {
mySvg,
}
}
</script>
Try this for the Parent component
<template>
<child>
<label>Text with image</label>
<slot slot="img" name="img">
</child>
<template>

Categories