Composition API + script setup Dynamic component - vuejs3

I want to implement simple tabs using script setup and composition API
<script setup>
import Timeline from './Timeline.vue';
import Profile from './Profile.vue';
import Groups from './Groups.vue';
const currentTab = ref('Timeline')
const tabs = ref(['Timeline', 'Profile', 'Groups'])
</script>
<template>
<div class="tabs">
<div v-for="tab in tabs"
:key="tab"
#click="currentTab = tab" v-text="tab"
<component :is="currentTab"></component>
</div>
</template>
But this code will only result <timeline></timeline> instead of the actual content of the Timeline component.

As mentioned here when using the <script setup>-method you need to pass the actual object of the component to the :is instead of the string.
See this Example here
Here is also the code itself:
<script setup>
import Timeline from './Timeline.vue';
import Profile from './Profile.vue';
import Groups from './Groups.vue';
import { shallowRef } from 'vue';
const currentTab = shallowRef(Timeline);
const tabs = [Timeline, Profile, Groups];
</script>
<template>
<div class="tabs">
<div v-for="tab in tabs"
:key="tab"
#click="currentTab = tab" v-text="tab.__name">
</div>
</div>
<keep-alive>
<component :is="currentTab"></component>
</keep-alive>
</template>

Related

Vue3 dynamic import from js

hello i just joined you,
I want to use heroicons dynamically with vue 3 js,
What I want to do is on the Home page you see below; atrribute I want that icon to be called according to the icon name I wrote.
For example in Home.vue
There are 2 examples available.
Please I would be happy if you take into account that I am new here and have little experience with vue js while writing an answer. Thank you from now.
This is what I want to do, it doesn't have to be in a .vue template, it can be given as an example as .js.
heroicons
<my-icon svgIcon="heroicons_outline:menu"></my-icon>
<my-icon svgIcon="heroicons_solid:menu"></my-icon>
<template>
<my-icon svgIcon="heroicons_outline:menu"></my-icon>
<my-icon svgIcon="heroicons_solid:menu"></my-icon>
</template>
<script>
import MyIcon from "./MyIcon";
export default {
name: "Home",
components: {MyIcon},
};
</script>
MyIcon.vue
<template>
<label class="block font-bold text-md text-gray-700">
<svgIcon></svgIcon>
</label>
</template>
<script>
import {svgIcon} from '#heroicons/vue/outline'
import {defineComponent} from 'vue';
export default defineComponent({
props: ['svgIcon']
})
</script>

Vue 3 Compositions API ref in v-for

Hej!
I'm trying to improve my due skills. I decided to make a simple dice game but I'm failing at the beginning.
I have two components. The Main Game and a dice component. In the main App component, I include the dice component and iterate over it 5 times to create 5 dices. I also add a button to roll all the dices:
<template>
<div class="center">
<Dice v-for="(dice, i) in 5" ref="diceChildren" :initialNumber="i" />
</div>
<div class="center">
<button #click="rollDice">Würfeln</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import Dice from './components/Dice.vue'
const diceChildren = ref([]);
const rollDice = () => {
console.log(diceChildren);
}
</script>
I followed the official instructions here: https://vuejs.org/guide/essentials/template-refs.html#refs-inside-v-for and I expected a filled array with dice refs but the diceChildren array is empty.
I need the reference to the dices to roll the dice and to get rolled number.
The get things completed, this is the code of the dice component:
<template>
<div class="dice">
<img class="dice-image" :src="getImagePath()" alt="Bild eines Würfels"/>
</div>
</template>
<script setup>
import { defineProps, ref } from 'vue'
const props = defineProps({
initialNumber: Number,
});
const number = ref(props.initialNumber + 1);
const rollDice = () => {
number.value = Math.floor(Math.random() * 6 + 1)
}
const getImagePath = () => `dice_${number.value}.png`;
defineExpose({
rollDice,
});
</script>
Thank you so much for help. I exhausted many hours hitherto.
I also tried to take the refs insight template:
<template>
<div class="center">
<template v-for="(dice, i) in 5" ref="diceChildren" />
<Dice :initialNumber="i" />
</template>
</div>
<div class="center">
<button #click="rollDice">Würfeln</button>
</div>
</template>

How to access slot dynamic component in vue3?

// main.vue
<template>
<window v-for="app in apps">
// `app` is defined by `defineAsyncComponent(()=> import('path/to/app.vue'))`
<component :is="app"></component>
</window>
</template>
//window.vue
<template>
<div class="window" #click="click">
<slot/>
</div>
</template>
<script>
...
methods:{
click (){
// I need to access the `app` dynamic component here!
const app = this.$slots.default()[0];
}
}
...
</script>
I dont know how to access <slot/> component which is dynamic loaded.
In above case,this.$slots.default()[0] will be an object and it hasn't contain the dynamic component,just a AsyncComponentWrapper

Integrating modal component onto page on load React JS

I have webpage with React JS. The structure of site is MainContent.js --> Main.js -->ReactDOM render. I wanted to have modal popup when page opens so built modal component Modal.js--> DashModal.js which worked on its own project but not when imported to my site.
How do I import it correctly to have modal popup on load like it does on its own project? Thanks. I provided code below.
**Index JS**
import React from "react";
import ReactDOM from "react-dom";
import Main from "./containers/Main";
import { CookiesProvider } from "react-cookie";
import DashModal from "./components/DashModal"
// Import main sass file to apply global styles
import "./static/sass/style.scss";
ReactDOM.render(
<CookiesProvider>
<Main />
</CookiesProvider>,
document.getElementById("app")
);
**DashModal.js - the modal component**
import React from 'react'
import ReactDOM from 'react-dom'
import Modal from "../components/modal"
import "../components/modal.css"
import AppStore from "../static/images/AppLogoBlue.png";
class DashModal extends React.Component {
constructor(props) {
super(props)
this.state = {show:true}
}
showModal = () => {
this.setState({show: true});
};
hideModal = () => {
this.setState({show:false});
};
render() {
return(
<main>
<h1> React Modal </h1>
<Modal show = {this.state.show} handleClose ={this.hideModal}>
<div className = "left">
<a>
<img src={AppStore} alt= ""></img>
</a>
</div>
<div className = "left">
<button className= "button" onClick={this.hideModal}>Regular site </button>
</div>
</div>
</Modal>
<button type = "button" onClick = {this.showModal}>
open
</button>
</main>
);
}
}
const container = document.createElement("div");
document.body.appendChild(container);
ReactDOM.render(<DashModal/>, container);
export default DashModal;
**modal.js- part of modal which goes into DashModal.js**
import React from 'react';
import "./modal.css"
const Modal = ({ handleClose, show, children}) => {
const showHideClassName = show? "modal display-block" : "modal display-none"
return(
<div className={showHideClassName}>
<section style={ModalBox} className= "modal-main">
{children}
<button onClick={handleClose}>close</button>
</section>
</div>
);
};
export default Modal;
Thanks in advance
From my understanding, you want to open the modal by default when the <DashModal /> was imported into another component.
You can pass a prop show={true|false} to DashModal component <DashModal show={true} /> and inside that component add a componentDidMount lifecycle method
componentDidMount = () =>{
const {show} = this.props;
if(show){
this.showModal()
}
}
This will check the props and call the showModal when the component is loaded.

Passing props that is an array to 2 different components

This is my 3rd day working with Vue CLI, and I want to pass props that is an array to 2 different components. The components are called Products and Modal. The Modal component is dependent on the Products component. When I pass props to both components inside my App.vue, I don't want both components to render in my App.vue. Like I said, my Modal component is dependent on my Products component.
My overall goal is to display the product title, photo, and description in my modal.
The issue I have is I make a get request in my App.vue, which i fill an empty array and then pass that to Products component. If i pass the array as props to both Products and Modal, I will get an extra modal to render at app level which does make sense, but I don't want that.
Im still getting used to how Vue works and tips or help would be much appreciated.
This could be a very obvious answer that i'm just overlooking, but i'm learning, so please understand that.
App.Vue (I make my get request here and fill an empty array to then have that array passed down as props to the two components):
<template>
<div id="app">
<all-products v-bind:newProductsArray="newProductsArray"></all-products>
</div>
</template>
<script>
//imports
import Vue from 'vue'
import AllProducts from './components/AllProducts.vue'
import Modal from './components/Modal.vue'
//import so I can use vue resource for an http request
import VueResource from 'vue-resource'
Vue.use(VueResource)
//export components to be rendered by main.js then by the DOM
export default {
components: {
'all-products': AllProducts
},
data() {
return {
//empty products array to be filled after get request
products: [],
//declare new array to be set to products array, then passed as props. I do this because without another array,
//all products component will receiev an empty array before it can be filled by the http request.
newProductsArray: []
}
},
//request to retrieve all products from API using Created
created() {
//http request using vue resource from dependencies
this.$http.get('https://tap-on-it-exercise-backend.herokuapp.com/products').then(function(data) {
//fill products array by grabbing the data, slicing ot, then setting it to products array
this.products = data.body.slice(0, data.body.length)
//set products array to the new array to be passed down as props to all products component.
this.newProductsArray = this.products
})
}
}
</script>
Product component (This component receives props from app.vue):
<template>
<div class="products">
<h1 class="all-products">All Products</h1>
<div v-for="product in newProductsArray" :key="product.id" class="single-product">
<div class="product-container">
<div class="row">
<div class="col-md-8 center-block">
<div class="title">{{product.title}}</div>
<img class="images" :src="product.photo_url">
<div class="price">${{product.price}}</div>
<modal/>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
//imports
import Vue from 'vue'
import Modal from './Modal.vue'
//import bootstrap to be used to style buttons and modal.
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue)
export default {
//receive new products array as props from app
props: ['newProductsArray'],
components: {
'modal': Modal
},
data() {
return {
//set modal show to false by default
modalShow: false
}
},
methods: {
showModal() {
//toggle modal to show
this.modalShow = true
},
closeModal() {
//toggle modal to close
this.modalShow = false
}
},
}
</script>
Modal component (I want to receive the same props that products received)
<template>
<div>
<b-button #click="modalShow = !modalShow">Show Product Info</b-button>
<b-modal v-model="modalShow" title="title">
<div class="modal-body">
<img class="img-responsive" style="margin:0 auto;" src="http://placehold.it/300x340" alt="">
</div>
<p class="product-description">Description</p>
<div slot="modal-footer" class="w-100">
<b-button size="md" class="float-right" variant="warning" #click="modalShow=false">Close</b-button>
<b-button size="md" class="float-right" variant="primary" #click="modalShow=false">Like</b-button>
</div>
</b-modal>
</div>
</template>
<script>
export default {
props: ['newProductsArray'],
data() {
return {
modalShow: false
}
}
}
</script>

Resources