use v-slot to pass property to slots component - Vue 3 - vue.js

I´m struggeling with passing properties into a slots component.
Here some code to explain my problem:
App template:
<template>
<ModalContainer v-model="visibleModal">
<create-dir-modal />
</ModalContainer>
</template>
ModalContainer:
<template>
<div class="modal-container" v-if="modelValue" #click.self="close">
<slot :close="close" />
</div>
</template>
(close is a defined method here)
CreateDirModal:
<template v-slot="slot">
<div class="create-dir-modal-container">
<i class="button" #click="slot.close">close</i>
<div>
<i>folder</i>
<input placeholder="Name" v-model="name" />
<span></span>
<i class="button">done</i>
</div>
</div>
</template>
I am trying to execute the close method from the ModalContainer in the CreateDirModal.
Here is my problem: slot is not defined in CreateDirModal template.
Am I just using it wrong or is there a way to fix my problem?

Related

Default slot or named slot not working on "router-view" in Vue 3

Passing a header component as a named slot into router-view. But router-view not exactly displaying it. Normally this was work in vue 2. But it's not with vue 3. Is the way doing it changed or is it impossible to do it now?
header component
<template>
<header id="g-header">
<figure>
<img id="logo"
src="#/assets/images/logo.jpg"
alt="The beautiful MDN logo.">
</figure>
<nav id="g-nav" class="flex justify-space-between">
<a v-for="(link, idx) in links" :key="idx"
class="flex justify-center align-center capitalize"
:href="link.val">{{ link.text }}</a>
</nav>
</header>
</template>
router-view component
<template>
<div id="moduleA" class="modules">
<router-view class="moduleA-children">
<template #header>
<app-header></app-header>
</template>
</router-view>
</div>
</template>
children view component
<template>
<div id="step1">
<slot name="header"></slot>
<div class="row">the named slot OR default slot not showing</div>
</div>
</template>
Is there a way to accomplish this? Or do I missing something here?
In Vue Router 4, <router-view> exposes the component to render as a v-slot prop, named Component, which would be used with the built-in <component> tag. You could pass any slots to the component within that <component> tag:
<router-view v-slot="{ Component }">
<component :is="Component">
<template #header>
<app-header></app-header>
</template>
</component>
</router-view>
demo

Is this the right approach to create a component?

I made a view to show some contact information for the user:
<template>
<div v-for="user in users" class="user">
<div class="userInformation">
<img :src="user.photo" />
<div class="userName">
<h3>{{ user.age }}</h3>
<p>{{ user.gender }}</p>
</div>
</div>
<div class="button-wrapper">
<a href="#">
<button #click="$router.push(`/user/${user.id}`)">User Profile</button>
</a>
</div>
</div>
</template>
<style>
</style>
users is an array that holds all users which I fetch from the backend.
I want to create a component so that I can re-use the user card in other classes and don´t have to include the markup. I tried it the following way but I'm stuck at the button to redirect the user and the img because I don´t know how to use named slots there.
<template>
<div class="user">
<div class="userInformation">
<img />
<div class="userName">
<h3>{{ age }}</h3>
<p>{{ gender }}</p>
</div>
</div>
<div class="button-wrapper">
<a href="#">
<button>User Profile</button>
</a>
</div>
</div>
</template>
<script>
export default {
name: "UserCard",
props: [
"age",
"gender"
]
};
</script>
Another problem is that I have to re-create the fetch method for my users in other classes to access the user information. Would there be a better way of doing this?
// fetch user data from backend and create users array
...
<div v-for="user in users" :key="user.name">
<UserCard
:age="`${user.age}`"
:gender="`${user.gender}`"
/>
</div>
Is this the right approach to create a reusable component?
You're headed in the right direction for your component. If you wanted a named slot for the button you could use something like this.
Child Component
<template>
...
<slot name="button">
<!-- default/fallback content can be provided, if the parent does
not provide slot content the button-wrapper div will appear -->
<div class="button-wrapper">
<a href="#">
<button>Default Button</button>
</a>
</div>
</slot>
</div>
</template>
Parent
<div v-for="user in users" :key="user.name">
<UserCard
:age="user.age"
:gender="user.gender">
<template v-slot:button>
<div>some custom button here {{ user.phone }}</div>
</template>
</UserCard>
</div>
Also compilation scope (Vuejs v2 guide) is an important thing to keep in mind with slots - "Everything in the parent template is compiled in parent scope; everything in the child template is compiled in the child scope."
In terms of fetching your users, that's a separate issue. Look into something like Vuex or other ways of managing shared state if you find yourself constantly having to fetch users in various components

Calling a method in a modal initially hidden (transition)

I have a simple method "updateTotal" initialized in my app / vue :
// start app
new Vue({
el: '#app2',
data: {
showModal1: false,
showModal2: false,
total : 0
},
methods: {
updateTotal: function (num) {
this.total = this.total + num
}
}
})
When I call this method from by example a button in the HTML code in the app2 section, it's OK (text "total" updated).
When I call this method from a div section hidden at the loading of the page (it's a modal, with vue.js "transition" system), I have this error :
Property or method "updateTotal" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
The code of this modal / transition (it's IN the app2 div) is :
<!-- template for the modal component SERVICES-->
<script type="text/x-template" id="modal2-template">
<transition name="modal2">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
Header
</slot>
</div>
<div class="modal-body">
<!-- LISTE SERVICES -->
<div>
<div>
<div class="control">
<label class="radio">
<input type="radio" name="service1" v-on:click="updateTotal(10)">
Oui
</label>
<label class="radio">
<input type="radio" name="service1" checked v-on:click="updateTotal(-10)">
Non
</label>
</div>
</div>
<div>
<div class="control">
<label class="radio">
<input type="radio" name="service2" v-on:click="updateTotal(20)">
Oui
</label>
<label class="radio">
<input type="radio" name="service2" checked v-on:click="updateTotal(-20)">
Non
</label>
</div>
</div>
</div>
<!-- LISTE SERVICES -->
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
What can I do to be able to call this method from this modal / transition div ?
Thanks !
MAMP MySQL PHP5
The issue occurs since you try to call updateTotal() from within the template of a different component.
The context within the template is always the component itself (this). Since the method updateTotal() is not defined in this component, you cannot call it.
There are several workarounds, but I consider two the cleanest:
For simple projects/apps: emit an event that triggers the method in the parent (like your close event)
For more complex apps: Use shared state with Vuex and make your method an action

How to use conditionals without attaching it to an element?

In vue, we have got directives v-if which needs to be attached to element.
Is there a way to use conditionals without attaching them to anything like the mustachejs way?
I am looping through an array of words and it is adding div's in every word which is annoying
Here is my template
<div v-for="(str, index) in reference" :key="index">
<div v-if="patternIncluded(str)">
<input type="text" v-model="remarks[index]">
</div>
<div v-else>
{{str}}
</div>
</div>
I would be nice if i could do it like this:|
{{if true}}
something goes here
{{else}}
other thing goes here
{{\if}}
Here is your code but using the < template > tag
<template v-if="patternIncluded(str)">
<input type="text" v-model="remarks[index]">
</template>
<template v-else>
{{str}}
</template>
Here is a example from the official Vue documentation:
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</template>
More informations: https://v2.vuejs.org/v2/guide/conditional.html#Controlling-Reusable-Elements-with-key
You can also use a Method and use normal Javascript into the < script > at the bottom of your file
Hope this helped, cheers
you could use the 'template' like so:
<template v-if=patternIncluded(str)>
//code
</template
<template v-else>
//code
</template>
UPDATE so like this
<div-for="(str, index) in reference" :key="index">
<template v-if="patternIncluded(str)">
<input type="text" v-model="remarks[index]">
</template>
<template v-else>
{{str}}
</template>
</div>

Conditionally wrapping child elements in template without duplication

I have a template that needs to be rendered slightly differently depending on a boolean prop. If it's true, the wrapper needs to be outside the child elements, if false, it needs to be wrapping just one of the child elements.
The only way I can think of to do this is to duplicate the child elements in the template like this:
<template>
<div>
<template v-if="flag">
<div class="wrapper">
<component-a />
<component-b />
<component-c />
</div>
</template>
<template v-if="!flag">
<component-a />
<div class="wrapper">
<component-b />
</div>
<component-c />
</div>
</template>
</div>
</template>
Is there any way to do this without duplicating the component-[a/b/c] declarations, which may contain large numbers of props for instance.
How about conditional class binding, ref Class and Style Bindings
<template>
<div :class="{ wrapper: flag}">
<component-a />
<div :class="{ wrapper: !flag}">
<component-b />
</div>
<component-c />
</div>
</template>
Alternative with named slots (untested)
Assuming it's worth the trouble of splitting your template into parent and child, I think you can use named slots with the same name in multiple places, provided you use v-if to compile-in only one instance at a time.
Ref: Conditional slots, is it possible
child
<template>
<div>
<template v-if="flag">
<div class="wrapper">
<slot name="component-a">
<slot name="component-b">
<slot name="component-c">
</div>
</template>
<template v-if="!flag">
<slot name="component-a">
<div class="wrapper">
<slot name="component-b">
</div>
<slot name="component-c">
</div>
</template>
</div>
</template>
parent
<child>
<component-a slot="component-a" />
<component-b slot="component-b" />
<component-c slot="component-c" />
</child>