In Vue 3 it's possible to Teleport a component to the body tag like so:
<template>
<button #click="modalOpen = true">
Open full screen modal! (With teleport!)
</button>
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
I'm a teleported modal!
(My parent is "body")
<button #click="modalOpen = false">
Close
</button>
</div>
</div>
</teleport>
</template>
<script>
export default {
data() {
return {
modalOpen: false
}
}
};
</script>
This causes the modal dialogue above to be rendered in the body tag. How can I achieve a similar thing in Vue 2?
Because Vue 2 doesn't support teleports, I recommend to use portal-vue component made for vue 2 :
Installation :
npm i portal-vue --save
Usage :
main.js
import Vue from "vue"
import PortalVue from 'portal-vue'
Vue.use(PortalVue)
...
inside some child component :
<portal to="destination">
<p>This slot content will be rendered wherever the <portal-target> with name 'destination'
is located.</p>
</portal>
in other place :
<portal-target name="destination">
<!--
This component can be located anywhere in your App.
The slot content of the above portal component will be rendered here.
-->
</portal-target
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.
I am working on a Vue 3 app. I have run into a problem working with a <Navbar /> component and one of its sub-components.
In App.vue:
<template>
<Navbar />
<Content title="Home" />
<Footer />
</template>
<script>
import Navbar from './components/Navbar.vue'
import Content from './components/Content.vue'
import Footer from './components/Footer.vue'
export default {
name: 'App',
components: {
Navbar,
Content,
Footer
}
}
</script>
Within the Content.vue component, I can display the title this way:
<h1>{{ title }}</h1>
<script>
export default {
name: 'Content',
props: {
title: String
}
}
</script>
But displaying buttons with their labels by the same pattern as above does not work:
// Button.vue component
<template>
<button class="btn btn-sm btn-generate">
{{ label }}
</button>
</template>
<script>
export default {
name: 'Button',
props: {
label: String
}
}
</script>
// Navbar.vue component
<template>
<div class="navbar">
<Button label="Button 1" />
<Button label="Button 2" />
</div>
</template>
<script>
import Button from './Button'
export default {
name: 'Navbar',
components: {
Button
}
}
</script>
The problem
As a result of the code above, inside <div class="navbar"> there is only one button with no label, instead of 2 buttons labeled "Button 1" and "Button 2".
Where is my mistake?
I think that naming your component just as the already existing HTML element is not a good idea. Try changing it to MyButton and use <my-button>... in the navbar component (you still want to use <button> in MyButton, as you want to build upon it).
Most probably browser still picks just a default button instead of yours.
The first essential rule mentioned in Vue docs is Use multi-word component names.
You need to bind them so :label="button 1"
So in my App.vue component, I have two other components being rendered, and what I want to achieve is this, when the user clicks on the play button, animateRing() method from the component ProgressRing should be used, my code looks the following:
App.vue
<template>
<div id="app">
<ProgressRing>
<PlayButton
#click="animateRing(), active = !active"
:class="{active: active}"
/>
</ProgressRing>
</div>
</template>
PlayButton.vue
<template>
<button class="play-button" #click="$emit('click')">
...
some irrelevant code here
...
</button>
</template>
ProgressRing.vue
<template>
...some code...
</template>
<script>
export default {
methods: {
animateRing(){
console.log('I'm animated');
}
}
};
</script>
I can't seem to figure out the right way to do it. What is the best way to make the method to work in the App component? I'm relatively new to Vue and didn't really figure it out yet.
Here's one way you can accomplish this:
App.vue
<template>
<div id="app">
<ProgressRing ref="progressRing">
<PlayButton
#click="animateRing(), active = !active"
:class="{active: active}"
/>
</ProgressRing>
</div>
</template>
<script>
export default {
methods: {
animateRing(){
this.$refs.progressRing.animateRing()
}
}
};
</script>
I'm curious about the reason for PlayButton being a child of ProgressRing, but not defined in ProgressRing.vue.
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>