Attach a sibling component using ref of a component - javascript

I am using a library in my page, I wish to update the input component of this library when it is rendered. More specifically I wish to add an icon to the input box of this library.
The library doesn't provide any slots to perform the same but what we it does give us a ref for the input element. I wish to use this to update the html by attaching an icon as a sibling of the ref.
I want to know if this is possible and a base guide as to how I achieve it
I have tried to use document to perform the same but I feel vue should be able to do it and the state will be consistent post that.
App.Vue
// App.vue (Parent)
<template>
<Component ref="componentRef" />
</template>
//Component.vue (Child)
<template>
<div id="iconMountPoint">
</div>
<input ref="inputEleRef">
</template>

Related

How to render VueJS Slot in a another component

VueJS slot could be placed anywhere inside a parent component.
However, I have a use case where I need to show a slot in another (foreign) component outside the parent component.
For example, I have component Student:
<template>
<slot name="detail" />
<slot name="status" /> <!-- I don't want to render this slot inside this component, but somewhere else -->
</template>
I want to display the student's status in a header section outside the student component as below:
<div class="Header">
<!-- student status slot should go here-->
<student-status-slot></student-status-slot>
</div>
<div class="InputForm">
<student>
<template #detail>This the detail information about student</template>
<template #status>Grade A</template>
</student>
</div>
You may suggest to create a new "student-status-slot" component. However, the idea is to have all student related information, including "status" declared under the component, but render some of the slot somewhere else.
I have tried another approach creating a separate component and pass the status data via prop. But That is not what I wanted to achieve. I don't want to pass data, but re-use the student status component, which can be dynamic.
In a parent component you can access child's slots by adding ref attribute to child, and then render the VNode obtained. See the demo here https://codesandbox.io/s/happy-einstein-9f5rke?file=/src/components/StudentPage.vue
But actually I wouldn't recommend this way such it's not conventional and slots were not designed for this. I think you should rather consider using portals (Vue 3 portals or https://www.npmjs.com/package/portal-vue if you use Vue 2) to achieve your goal

Reload/reinit renderless vue component after morph dom node

I'm building at the moment a frontend editor.
When I'm logged in, elements are getting rendered by server (laravel) and are enclosed in a renderless vue component:
<vue-element>
<template v-slot="{ editing }">
<div class="element">
// other HTML
</div>
</template>
</vue-element>
The problem is, when I'm editing this element, css classes and other stuff are getting changed and I have to swap (morph dom) the element with a new element from the server. But when I do that, the element disappears and vue has lost track of the component.
How can I reinit the component or are there other solutions to my problem?

Vue - Surround a component with a tag dynamically

What I'm trying to say is the v-if directive can make an entire component (and all its content) disappear based on a condition.
My question is:
is there a way to make only the surrounding tag or component disappear, without removing its content?
You can use dynamic component and :is prop with vue-fragment when you need a root-less component, or directly vue-fragment if that's just what you need.
Another option is to manipulate the DOM directly.
No, you can't do it. The first thing came to my mind is to move your content in separate component to avoid code duplicating and do something like this
<wrapper v-if="condition">
<child />
</wrapper>
<child v-else />
If you provide some details on why do you need this, probably we can find a better solution.
2022 Working solution:
Create a universal "template wrapper" component:
<!-- VTemplate.vue -->
<template>
<slot></slot>
</template>
Use it in pair with the <component> component:
<script setup>
import VTemplate from './VTemplate.vue'
</script>
<component :is="condition ? 'li' : VTemplate">Your content</component>
<!-- Where 'li' is your wrapper tag/component that you want to conditionally hide but not its content -->
Bonus:
<component :is="condition ? 'li' : VTemplate" :class={'list-item': condition}>
Your content
</component>
<!-- You can assign attributes like the class to it as well -->
P.s. Also, if anyone knows how to import the <template> component directly or at least how to use it in <component :is> please enlighten :)

How to pass ref from parent to child component in template

I am using this component https://element.eleme.io/#/en-US/component/popover
Issue related to triggering element (it is used to calculate where is popover located)
For the triggering element, you can write it in two different ways: use the slot="reference" named slot, or use the v-popover directive and set it to Popover's ref.
Everything ok with default examples. But I am using transparent wrapper for el-popover component like so.
<script id="my-popover" type="x-template">
<el-popover
ref="mypopover"
transition="el-zoom-in-top"
v-bind="$attrs"
v-on="$listeners"
>
<!-- Pass on all named slots -->
<slot
v-for="slot in Object.keys($slots)"
:slot="slot"
:name="slot"
/>
<span> My popover </span>
</el-popover>
</script>
It works ok with slot="reference" named slot.
<my-popover
placement="bottom"
title="Title"
width="200"
trigger="manual"
v-model="visible"
>
<el-button
slot="reference"
#click="visible = !visible"
>
Click me
</el-button>
</my-popover>
But due to complex layout I need to use v-popover directive. Got no luck with wrapped component.
<my-popover
ref="popover"
placement="right"
title="Title2"
width="200"
trigger="manual"
v-model="visible2"
>
</my-popover>
<el-button
v-popover:popover
#click="visible2 = !visible2"
>
Click me too
</el-button>
So I need somehow to pass in v-popover reference to ref="mypopover" from wrapped component. I.e. pass ref to child directly in template.
I've tried v-popover:popover.$refs.mypopover but that doesn't work.
Related codepen https://codepen.io/anon/pen/rgRNZr
Click on button Click me too should show popup connected to that button.
The problem is that the ref you want is the one that is on the el-popover but you are using the ref that is set on the my-popover component instead of the one you want.
This is kind of a wierd thing since that component wants a ref but it will be annoying to get one from the component inside your component.
I would put the button and popup with a slot in the same component.

Include component from parent app in component contained in node_modules directory

I am working on Vue app that incorporates Vue Bootstrap Calendar, and I would like to be able to override the content of the day cell (handled by the Day.vue component) to add my own custom content inside. My thought was initially to modify the Day component to include <slot></slot> tags and pass in the custom content that way.
The problem has to do with accessing the Day component. To include the calendar in your app, you include the Calendar.vue component, which includes Week.vue, which in turn includes Day.vue. As I understand slots, I have to have the child component (Day.vue in this case) included in the component where I'm passing the data, which means it would need to be included in my own component.
If this is not possible, my other thought is to perhaps modify the library by adding another configuration prop (something like dayCustomContent) to the Calendar.vue that indicates that the Day cell content is custom content, pass that in to Calendar.vue, and then down to Day.vue, and then in the Day.vue template, have a v-if conditional based on this prop that either displays the custom content or the default cell content, something like:
<template>
<div class="day-cell" v-if="dayCustomContent">
...my custom content here...
</div>
<div class="day-cell" v-else>
...default events from my.events goes here...
</div>
</template>
I would probably then need to define a custom component to render whatever custom content I want to display, and somehow include that component within Day.vue.
So to sum up, my questions are these:
1) Is there a way to do what I need with slots?
2) For my second option, am I going down the right path? I'm open to suggestions.
UPDATE: I was able to get this done by adding a boolean customDayContent prop in Calendar.vue like so and passing it down to Week.vue and then to Day.vue:
<template>
...
<div class="dates" ref="dates">
<Week
v-for="(week, index) in Weeks"
:firstDay="firstDay"
:key="week + index"
:week="week"
:canAddEvent="canAddEvent"
:canDeleteEvent="canDeleteEvent"
:customDayContent="customDayContent"
:displayWeekNumber="displayWeekNumber"
#eventAdded="eventAdded"
#eventDeleted="eventDeleted"
></Week>
</div>
...
</template>
<script>
export default {
...
props: {
...
customDayContent: {
type: Boolean,
default: false
}
},
}
</script>
and then in Day.vue, do like I had suggested with v-if:
<template>
<div class="day-cell" v-if="customDayContent">
<custom-day></custom-day>
</div>
<div
class="day-cell"
:class="{'today' : day.isToday, 'current-month' : day.isCurrentMonth, 'weekend': day.isWeekEnd, 'selected-day':isDaySelected}"
#click="showDayOptions"
v-else
>
... existing code goes here...
</div>
</template>
The last part is referencing the CustomDay.vue component referenced in my v-if block. I want the user to be able to define the content of their own custom CustomDay.vue template in their own parent app. However, I'm having trouble trying to figure out how to do that. Following the pattern of including components already in this component, I added this in the components section of Day.vue:
CustomDay: require("../../../../src/Components/CustomDay.vue").default
? require("../../../../src/Components/CustomDay.vue").default
: require("../../../../src/Components/CustomDay.vue")
However, no matter what I try along these lines, I get an error that the relative module was not found. On top of that, I need to add it to the componentsarray only if customDayContent is true. What is the best way to do that? In a watcher or computer property, perhaps? Or another way?

Categories

Resources