How to use children in vue? - vue.js

I am using React.js and Vue.js for frontend in my different projects.
In React, I am able to wrap templates with MyComponent like this.
<MyComponent>
<div>Here</div>
</MyComponent>
And in MyComponent file
const MyComponent = ({children}) {
return (
<div className="my-component">{children}</div>
)
}
How can I use this simple technique in Vue.js???

You will want to use Slots.
Here is an example taken from vuejs doc
Component template:
<a :href="url">
<slot></slot>
</a>
The code:
<navigation-link url="/profile">
Your Profile
</navigation-link>
<slot></slot> will be replaced by what's inside the component tags. It will be rendered as:
<a url="/profile">
Your Profile
</a>

Related

Insert 2 components in nuxt.js page

i'm new of this framework :(
the problem is here because i've tried to put the component in another page and work it.
It sign error the component
this is my index.vue page
If you're using nuxt2.0, you should wrap them in a container but this is not needed in nuxt3.0.
<template>
<main>
<navbar />
<slideshow />
</main>
</template>
If this is nuxt2.0, then you should also import the component and register it but you haven't done it here. The path you've given to the component is not correct also.
<script>
import Slideshow from '~/components/slideshow.vue';
export default {
components: { Slideshow }
}
</script>
You need to wrap the into a div or any other tag (to not have multiple tags at the root of the template) like that
<template>
<div>
<navbar></navbar>
<slideshow></slideshow>
</div>
</template>
And you can also skip the import part because Nuxt is already doing that for you as explained here: https://nuxtjs.org/tutorials/improve-your-developer-experience-with-nuxt-components/

Vue Composition API with "is" attribute for conditional rendering

I'm working on an onboarding process that will collect a users name, location, job , etc. It needs to be one question per page but as an SPA so I currently have around 20 components to conditionally render.
Same problem as this but I've been asked change to Composition API and now I can't get this to work.
Vue - Render new component based on page count
The solution in the above was to make an array with all the page titles, a for loop and use :is to render each page as needed.
My components are named in this format: MLandingPage.vue, MFirstName.vue, etc.
I also have buttons that add or minus 1 from onboardingStep to go forward or back a step.
I have tried this:
const onboardingPages = ref(["MLandingPage", "MFirstName", 'MWelcome', 'MLastName', 'MAge' ]);
const onboardingStep = ref(0);
<template v-for="(onboardingPage, index) in onboardingPages" :key="index">
<component :is="onboardingPage" v-if="index === onboardingStep"/>
</template>
This doesn't render anything on the page and when I inspect, it just has <mlandingpage></mlandingpage> with no content.
I tried this instead:
const onboardingPages = ref(["m-landing-page", "m-first-name", 'm-welcome', 'm-last-name', 'm-age' ]);
Still nothing and I get this when I inspect the page: <m-landing-page></m-landing-page>
As a test, if I just write <m-landing-page></m-landing-page> in the code, it works.
Totally new to Composition API and the "is" attribute so any help would be great.
Edit - added more code for context:
<script setup>
import MLandingPage from "~~/components/onboarding/MLandingPage.vue";
import MFirstName from "~/components/onboarding/MFirstName.vue";
import MLastName from "~/components/onboarding/MLastName.vue";
import MAge from "~/components/onboarding/MAge.vue";
import { ref } from "vue";
const onboardingPages = ref(["m-landing-page", "m-first-name", 'm-welcome', 'm-last-name', 'm-age' ]);
const onboardingStep = ref(0);
function prevStep() {
onboardingStep.value -= 1;
}
function nextStep() {
onboardingStep.value += 1;
}
</script>
<template>
<div class="flex items-center justify-end gap-2 md:gap-10 mt-16 px-5 md:px-20">
<h1>Example Page Title</h1>
<div class="mt-16 p-5 pr-8 md:px-20 md:mt-24">
<template v-for="(onboardingPage, index) in onboardingPages" :key="index">
<component :is="onboardingPage" v-if="index === onboardingStep"/>
</template>
</div>
<button #click="prevStep">Back</button>
<button #click="nextStep">Next</button>
</div>
</template>
Thanks!
Found a much simpler way around this
Key issue was that I had the page names as strings
const onboardingPages = ref([MLandingPage, MFirstName, MWelcome, MLastName, MAge]);
<div class="mt-16 p-5 pr-8 md:px-20 md:mt-24">
<transition name="fade">
<component :is="onboardingPages[onboardingStep]" />
</transition>
</div>

How to use Vue I18n translation in component attribute/property

How do I translate the passed-in attribute/property of a component? For instance, I have a card component with a title and description prop defined like this.
<!-- my-card component -->
<template>
<div>
<h2>{{title}}</h2>
<span>{{description}}</span>
</div>
</template>
<script>
export default {
props: {
title: String,
descritpion: String
}
}
</script>
Then using the my-card component in another page/component like this
<template>
<div>
<header>Page header</header>
<my-card :title="the best card title" :description="the best description" />
<footer>Page footer</footer>
</div>
</template>
How do I us vue I18n to translate the component props?
<template>
<div>
<header>Page header</header>
<my-card :title="{{ $t('myCard.title')}}" :description="{{$t('myCard.description')}}" />
<footer>Page footer</footer>
</div>
</template>
I can't seem to get the translation to work with passed-in props.
PS: I know I could add the translation in the place I defined my-card component but the issue here is that the components are third-party components from NPM library.
I know some packages in React.js has this feature.
Just bind the translation without using {{}} :
<my-card :title="$t('myCard.title')" :description="$t('myCard.description')" />
You can use I18n translation in component props like this.
<my-card
:title="$t('myCard.title')"
:description="$t('myCard.description')"
/>

How to get access to slotted component / template in vue-test-utils?

Component:
<template>
<MyComponent>
<div class="in-slot">Hello world</div>
</MyComponent>
</template>
How to get access to div.in-slot and test his text?
If we suppose that you imported the parent component as MyParentComponent in your test file, this should work:
const wrapper = mount(MyParentComponent, {options})
expect(wrapper.find('.in-slot').text()).toBe('Hello world')

v-if on a component template tag

From the docs:
Because v-if is a directive, it has to be attached to a single
element. But what if we want to toggle more than one element? In this
case we can use v-if on a element, which serves as an
invisible wrapper. The final rendered result will not include the
element.
But on my template in my component:
<template v-if="false">
<div>
....
</div>
</template>
But the component still renders.
I ask because I want a hook on the component so if v-if is true, I can do some code in beforeMounted and beforeDestroyed if false.
If I undestood what are you doing...
You're putting v-if int the template tag ina .vue file right?
Like this
// component.vue
<template v-if="false">
<div>
My Component
</div>
</template>
<script>
export default {
name: 'my-component'
};
</script>
<styles>
</styles>
Right?
If YES, you are doing it wrong.
The template there is a tag for Webpack Vue Loader to load the component template.
So the if must go inside the template tag.
// component.vue
<template>
<div v-if="false">
My Component
</div>
</template>
<script>
export default {
name: 'my-component'
};
</script>
<styles>
</styles>
If you need to "hide" multiple elements, just encapsulate into another div.
As Lucas Katayama said, you cannot use v-if inside SFC, but another way to hide you component is use v-if on this component in its parent component.
Your reference to the docs is correct, you can use a v-if on a template tag. However, I believe conditionals on the top-level <template> in a Single File Component are ignored.
To achieve the effect showed in the docs (conditional render a template) that template needs to be within the top-level template section.
Example:
<script>
// your script section
</script>
<template>
<template v-if="false">
<div>
....
</div>
</template>
</template>
<style>
// your style section
</style>