I'm familiar with ReactJS, but not with VueJS.
Where can I place the component children at the parent component.
I have this example in ReactJS, how can I create the same using VueJs:
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
What is the {props.children} in VueJS ??
The Vue analogy to the React "children" concept is the slots.
https://v2.vuejs.org/v2/guide/components.html#Content-Distribution-with-Slots
https://v2.vuejs.org/v2/guide/components-slots.html
Slots can be used like:
// FancyBorder.vue
<div class="FancyBorder">
<slot/>
</div>
// App.vue
<FancyBorder>
Contents!
</FancyBorder>
Vue slots also have an advantage over the React children, as you can have multiple "groups" of elements, using named slots, this can make the code less reliant on "magic" classnames:
// FancyBorder.vue
<div class="FancyBorder">
<h1 className="Dialog-title">
<slot name="header"></slot>
</h1>
<slot/>
</div>
// App.vue
<FancyBorder>
<template slot="header">
Header
</template>
Contents!
</FancyBorder>
Related
I want to change the body dynamically, how can i do this?
import React from 'react'
// import { Link } from 'react-router-dom'
export default function ProjectTemplate(props) {
const Css= {
"--background":`${props.isValue ? props.mode : 'red'}`
}
return (
<>
<div className="bodyCon projectCon">
<div className="bodyComponent">
<div className="aboutHeading projectHeading" style={Css}>
<h1>{props.name}</h1>
<div className="container">
<div className="projects">
</div>
</div>
</div>
</div>
</div>
</>
)
}
this is a custom component
import React from 'react'
import ProjectTemplate from '../Projects/ProjectTemplate/ProjectTemplate'
export default function Blog(props) {
return (
<>
<ProjectTemplate name='Blog' mode={props.mode} isValue={props.isValue} >
hhhh
</ProjectTemplate>
</>
)
}
this is the another component where i want to add the body of previous component dynamically, then the hhh is not display in browser
output in browser:
<div className="bodyCon projectCon">
<div className="bodyComponent">
<div className="aboutHeading projectHeading" style={Css}>
<h1>{props.name}</h1>
<div className="container">
<div className="projects">
hhh
</div>
</div>
</div>
</div>
</div>
but hhh is not visible in browser, how can i do for this output
You should read this docs, all you need here is children props
The word hhh will not be displayed because you are calling the custom component in the project template with no props name which you want to display. In your code if you render the component the Blog name will displayed only.
use this in the project template and pass the props to the childrenanme
<div className="projects">
{props.childrenanme}
</div>
To Answer your Question: Yes we can use component inside nested component in react.
In JSX expressions that contain both an opening tag and a closing tag,
the content between those tags is passed as a special prop React Documentation.
And to use these special props you have to use {props.children}.
In your example, you have passed the content b/w opening and closing tag of a custom component but forgot to use it.
In projectTemplate component use the children that you have passed while invoking the component, like this:
<div className="projects">
{props.childrenanme}
</div>
In my vue application, i am using slots for some block of contents. Now, i have to migrate my application into react. While exploring react, i got to know props.children will work similar as slot works.
But, i am not sure what will be the proper way to use this pattern in react.
Here is the sample of code in vue
<template>
<div class="badge-box">
<span :class="badgeClass" :style="badgeStyle">
<span v-if="shape !=='dot'" class="line-break">
<slot>
{{text}}
</slot>
</span>
</span>
<span v-if="shape ==='dot'" class="line-break" style="margin-left: 8px;">
<slot name="dotShape">
{{text}}
</slot>
</span>
</div>
</template>
<script>
export default {
name:'sample'
props: {
text: { type: string }
}
}
</script>
How to change this vue slot pattern into React using props.children?
There are several patterns in React that correlate closely with Vue slots.
props.children can be used, but only for default slot with no slotProps. For named slot additional props can be used. Default slot content <slot>{{text}}</slot> can be conditionally rendered when no children are provided:
let MyComp = props => (
...
<div class="default-slot">{{props.children ?? props.text}}</div>
...
<div class="named-slot">{{props.named ?? props.text}}</div>
...
)
and
<MyComp named={<p>Named content</p>}>
<p>Default content</p>
</MyComp>
Function as child and function as prop patterns serve the same purpose but allow to replace slots with slotProps. A child can pass parameters to parent scope through a callback:
let MyComp = props => (
...
<div class="default-slot">{{props.children?.('foo') ?? props.text}}</div>
...
<div class="named-slot">{{props.named?.('bar') ?? props.text}}</div>
...
)
and
<MyComp named={param => <p>Named content {{param}}</p>}>{
param => <p>Default content {{param}}</p>
}</MyComp>
Assuming you are using JSX/TSX in functional React
const Component ({text}) => {
return (
<div class="badge-box">
<span :class="badgeClass" :style="badgeStyle">
<span v-if="shape !=='dot'" class="line-break">
{{text}}
</span>
</span>
<span v-if="shape ==='dot'" class="line-break" style="margin-left: 8px;">
{{text}}
</span>
</div>
)
}
will do what you want. If you are using class component, put it in the render method.
So I've created a simple wrapper component with template like:
<wrapper>
<b-table v-bind="$attrs" v-on="$listeners"></b-table>
</wrapper>
using $attrs and $listeners to pass down props and events.
Works fine, but how can the wrapper proxy the <b-table> named slots to the child?
Vue 3
Same as the Vue 2.6 example below except:
$listeners has been merged into $attrs so v-on="$listeners" is no longer necessary. See the migration guide.
$scopedSlots is now just $slots. See migration guide.
Vue 2.6 (v-slot syntax)
All ordinary slots will be added to scoped slots, so you only need to do this:
<wrapper>
<b-table v-bind="$attrs" v-on="$listeners">
<template v-for="(_, slot) of $scopedSlots" v-slot:[slot]="scope"><slot :name="slot" v-bind="scope"/></template>
</b-table>
</wrapper>
Vue 2.5
See Paul's answer.
Original answer
You need to specify the slots like this:
<wrapper>
<b-table v-bind="$attrs" v-on="$listeners">
<!-- Pass on the default slot -->
<slot/>
<!-- Pass on any named slots -->
<slot name="foo" slot="foo"/>
<slot name="bar" slot="bar"/>
<!-- Pass on any scoped slots -->
<template slot="baz" slot-scope="scope"><slot name="baz" v-bind="scope"/></template>
</b-table>
</wrapper>
Render function
render(h) {
const children = Object.keys(this.$slots).map(slot => h('template', { slot }, this.$slots[slot]))
return h('wrapper', [
h('b-table', {
attrs: this.$attrs,
on: this.$listeners,
scopedSlots: this.$scopedSlots,
}, children)
])
}
You probably also want to set inheritAttrs to false on the component.
I have been automating the passing of any (and all) slots using v-for, as shown below. The nice thing with this method is that you don't need to know which slots have to be passed on, including the default slot. Any slots passed to the wrapper will be passed on.
<wrapper>
<b-table v-bind="$attrs" v-on="$listeners">
<!-- Pass on all named slots -->
<slot v-for="slot in Object.keys($slots)" :name="slot" :slot="slot"/>
<!-- Pass on all scoped slots -->
<template v-for="slot in Object.keys($scopedSlots)" :slot="slot" slot-scope="scope"><slot :name="slot" v-bind="scope"/></template>
</b-table>
</wrapper>
Here is updated syntax for vue >2.6 with scoped slots and regular slots, thanks Nikita-Polyakov, link to discussion
<!-- pass through scoped slots -->
<template v-for="(_, scopedSlotName) in $scopedSlots" v-slot:[scopedSlotName]="slotData">
<slot :name="scopedSlotName" v-bind="slotData" />
</template>
<!-- pass through normal slots -->
<template v-for="(_, slotName) in $slots" v-slot:[slotName]>
<slot :name="slotName" />
</template>
<!-- after iterating over slots and scopedSlots, you can customize them like this -->
<template v-slot:overrideExample>
<slot name="overrideExample" />
<span>This text content goes to overrideExample slot</span>
</template>
This solution for Vue 3.2 version and above
<template v-for="(_, slot) in $slots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope || {}" />
</template>
I would like to scroll to menu element in a page.
I have the menu component which is not a parent of components to which I would like to scroll.
I have found this post that describe a similar problem
Passing ref to a child We want the ref to be attached to a dom element, not to a react component. So when passing it to a child
component we can't name the prop ref.
const myRef = useRef(null)
return <ChildComp refProp={myRef}></ChildComp> } ```
Then attach the ref prop to a dom element. ```jsx const ChildComp =
(props) => {
return <div ref={props.refProp} /> } ```
Here's my app structure
Menu component:
const MenuApp = () => {
return (
<div>
<div className="desktop-menu">
<div className="menu-item a-propos">
<p className='button'>Me découvrir</p>
</div>
<div className="menu-item competences">
<p className='button'>Compétences </p>
</div>
<div className="menu-item experiences">
<p className='button'>Experiences</p>
</div>
<div className="menu-item formation">
<p className='button'>Formation </p>
</div>
</div>
</div>
)
}
The parent is app component
<div className="App">
<div className="hero">
<HeaderApp />
<ApprochApp />
</div>
<Apropos />
<Competences />
<Experiences />
<Formation />
<Recom />
<Contact />
<Footer />
</div >
I would like that mu menus scrolls to the react components in the main App component
So how can I passe the reference from the menu component to the app and use it in components to scroll ?
I do not understand your problem completely though. However, one thing I can see from your question is that you're not forwarding the ref properly.
What you need in this case is forwardRef.
Basically, what you need to do is to create the childComponent as something like this:
const childComponent = React.forwardRef(({...otherProps}, ref) => {
return (<><div ref={ref}>Component content </div></>)
})
Where you need to use the component all you need to do is this:
const parentComponent = () => {
const reveiwsRef = React.useRef("");
return (
<div>
<childComponent ref={reviewsRef} />
</div>
);
}
You can find more info about this on the react documentation: Forwarding-Refs
I have hope this helps though
I'm familiar with the concept of ngTransclude via AngularJS and this.props.children via ReactJs, however does Aurelia support transclusion, that is, the notion of inserting, or transcluding, arbitrary content into another component?
Transclusion in AngularJS (https://plnkr.co/edit/i3STs2MjPrLhIDL5eANg)
HTML
<some-component>
Hello world
</some-component>
JS
app.directive('someComponent', function() {
return {
restrict: 'E',
transclude: true,
template: `<div style="border: 1px solid red">
<div ng-transclude>
</div>`
}
})
RESULT
Transclusion in ReactJs (https://plnkr.co/edit/wDHkvVJR3FL09xvnCeHE)
JS
const Main = (props) => (
<SomeComonent>
hello world
</SomeComonent>
);
const SomeComonent = ({children}) => (
<div style={{border: '1px solid red'}}>
{children}
</div>
);
RESULT
Several ways to do transclusion: Official docs
1. content slot <slot></slot>
The <slot> element serves as a placeholder in a component's template for arbitrary content. Example:
some-component.html
<template>
<div style="border: 1px solid red">
<slot></slot>
</div>
</template>
usage:
<template>
<require from="some-component"></require>
<some-component>
hello world
</some-component>
</template>
result:
2. named slots
A component can contain several replaceable parts. The user of the component can replace some or all of the parts. Parts that aren't replaced will display their default content.
blog-post.html
<template>
<h1>
<slot name="header">
Default Title
</slot>
</h1>
<article>
<slot name="content">
Default content
</slot>
</article>
<div class="footer">
<slot name="footer">
Default footer
</slot>
</div>
</template>
usage:
<template>
<require from="blog-post"></require>
<blog-post>
<template slot="header">
My custom header
</template>
<template slot="content">
My custom content lorem ipsum fla fla fla
</template>
<template slot="footer">
Copyright Megacorp
</template>
</blog-post>
</template>
3. template parts
The slots spec has limitations: http://aurelia.io/hub.html#/doc/article/aurelia/templating/latest/templating-content-projection/5
Use template-parts for dynamically generated slots: https://github.com/aurelia/templating/issues/432
Yes, Aurelia supports the concept of transclusion through use of the <content /> component. Per the below, any content nested within <some-component> be it HTML, a String, or another component, will be rendered within this component.
app.js
export class App {}
app.html
<template>
<require from="some-component"></require>
<some-component>
hello world
</some-component>
</template>
some-component.js
export class SomeComponent {}
some-component.html
<template>
<div style="border: 1px solid red">
<content />
</div>
</template>
RESULT
UPDATE: USE SLOT INSTEAD OF CONTENT
// Actual component
<your-component>
Just some content
</your-component>
// Template of the component
<template>
<div class="some-styling">
<slot></slot> // <-- "Just some content" will be here!!
</div>
</template>