Contingent upon the prop in Vue 2, determine whether to render the slot with a wrapper or

My component is simple - it just adds styling to a slot. The styling part is straightforward: it adds padding, margin, border, and background color based on the props passed. Right now, there is a wrapper component with a default slot inside. We bind classes and inline styles to the component, and its :is prop is set to div. While this setup gets the job done, adding an extra div is not ideal as we use this component frequently and it clutters the DOM.

I want to use the div wrapper only if the needContainer prop is set to true. To achieve this, I plan to implement a render function which will render the wrapper only when necessary. However, I am unsure how to render slots as the first parameter of the function needs to be a wrapper element. Additionally, I am unsure if it is possible to apply classes and inline styles directly to the slot content.

Below is my initial code:

<template>
    <component
        :is="div"
        :class="classes"
        :style="styles"
    >
        <slot />
    </component>
</template>

And here is what I have attempted so far:

render (createElement) {
        if (this.needContainer) {
            return createElement('div', { attrs: { class: this.classes }, style: this.styles }, this.$scopedSlots.default())
        } else {
            return createElement('template', {}, this.$scopedSlots.default())
        }
    }

UPD

I have also tried using a functional component instead of the component wrapper. While this functional component renders the slot content correctly and without a wrapper, I am struggling to apply styling directly to the slot element inside the render function.

Here is the updated template:

<template>
    <ConditionalWrapper
        :tag="div"
        :classes="classes"
        :styles="styles"
        :add-container="addContainer"
    >
        <slot />
    </ConditionalWrapper>
</template>

Functional component added:

    components: {
        ConditionalWrapper: {
            name: 'ConditionalWrapper',
            functional: true,
            render: (h, ctx) => {
                if (ctx.props.addContainer) {
                    return h(ctx.props.tag, { class: ctx.props.classes, style: ctx.props.styles }, ctx.scopedSlots.default())
                } else {
                    return ctx.children[0]
                }
            },
        },
    },

Answer №1

template is unnecessary in the render function. In Vue 3, you can simply use return slots.default(), but in Vue 2, a component cannot have multiple root nodes. Vue 2 does not have built-in fragments to work around this limitation, but the vue-fragment library offers a solution with some existing issues.

For a wrapper that allows only one child, you can use the following:

props: ['class', 'style'],
render(h) {
    const children = this.$scopedSlots.default?.();

    if (!children) {
        return null;
    }

    if (this.needContainer) {
        return h('div', { class: this.class style: this.style }, children); 
    } else {
        const [child] = children;

        child.data.class = [child.data.class, this.class];
        child.data.style = [child.data.style, this.style];
        
        return child;
    }
}

It is not advisable to patch a vnode instead of creating a new one as it can lead to reactivity issues and bugs when reusing the same vnode. Vue 3 offers the convenient cloneVNode utility to address this. In Vue 2, manual cloning may be necessary, as demonstrated here. Full cloning can lead to bugs because internal data specific to a vnode may be lost. The solution mentioned above should work in Vue 2, considering the mentioned caveats.

It is common practice to use style and class props since they are considered as fallthrough attributes.

class and style can be enclosed in another array for merging to handle all possible formats of their values in Vue.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

AngularJS's support for html5mode on GitHub pages is now available

Is it feasible for GitHub pages to accommodate AngularJS in html5mode? I came across a source online suggesting that it can be done with a fallback page for 404 errors. However, this solution seems flawed as it may result in multiple 404 errors, which wou ...

NodeJS constantly communicating with Rest API

Entering the world of Node.js is a new journey for me. I have a service with two endpoints available. The first endpoint is a post method that takes in a payload, processes it asynchronously, and immediately sends an acknowledgment to the caller. The secon ...

Triggering the AJAX function in the main window

My HTML webpage has a hyperlink that, when clicked, opens the same page in another window with a hash value appended to the URL using window.open. For example, the URL could look like this: http://mywebsite.com#hash=value The webpage contains a JavaScript ...

Avoiding layout shift when a button is clicked in React JS

I am working on a Drawer example and following the instructions provided at https://mui.com/material-ui/react-drawer/ Everything is functioning as expected, but I am encountering an issue where my content shifts to the right when the drawer opens, and ret ...

Exploring the Power of Map with Angular 6 HttpClient

My goal is to enhance my learning by fetching data from a mock JSON API and adding "hey" to all titles before returning an Observable. Currently, I am able to display the data without any issues if I don't use the Map operator. However, when I do use ...

Fetching content-type in React code locally executed

I am a beginner in front end development and I need help with setting the "application/json" content-type and gzip content-encoding in the fetch call for my locally run React code. Can anyone provide guidance on this? const data = await fetch(url, { ...

Puppeteer throwing an error when querying selectors cannot be done (TypeError: selector.startsWith is not a supported function)

I recently installed Puppeteer and ran into an issue when trying to query a selector. I keep receiving a TypeError: selector.startsWith is not a function error. I attempted to resolve the problem by tweaking the core code and adding toString(), but unfort ...

Switch button - reveal/conceal details

I am looking for assistance in toggling the visibility of information when clicking on an arrow icon. I have attempted to use JavaScript, but it was unsuccessful. My goal is to hide the information below by clicking on the downward-facing arrow image , an ...

Tips for using jQuery to identify the most recently modified row in an HTML table

Is there a way to identify the most recently modified (either newly added or changed) row in an HTML table? ...

Mastering the Art of Content Swapping in SPA

Hey there! I'm in the process of creating a webpage using tornado io and incorporating various graphs. To add some single page app magic, I decided to swap out content within a div like so: <div id="chartType"> Chart goes here</div> <a ...

The property 'clone' is undefined and cannot be read

Currently, I am using Fullcalendar to update events. My goal is to execute an ajax callback to retrieve the edited version of a specific event. The endpoint for this request should be at /controls/:id/edit. To achieve this functionality, I have implemented ...

Incorporating external files into Javascript code may cause issues with its functionality

Currently, I have a fully developed PHP theme that I am in the process of designing. Within this theme, I have integrated an image slideshow plugin to enhance its functionality. The following code represents the implementation of the image slideshow: &l ...

Identify all td inputs within a TR element using jQuery

Is there a way to retrieve all the input values within each table cell (td) of a table row (tr) using jQuery? Suppose I have a tr with multiple td elements, and some of these tds contain inputs or select elements. How can I extract the values from these in ...

Is there a way to show the keyboard on Chrome for Android without displaying the address bar?

One of the key features of Chrome on Android is that the address bar disappears when scrolling, allowing for more screen space. However, when you click on a text input field and the keyboard pops up, the address bar reappears. It seems like Google intenti ...

Using Javascript to Trigger Events on Selection

I have a unique situation: 1) One of my dropdown's choices features various car names. 2) Another dropdown has two options: When selecting each option in the second dropdown, the following should occur: a) Choose UserInput - This action will hide ...

Make a copy of an array and modify the original in a different way

Apologies for my poor English, I will do my best to be clear. :) I am working with a 3-dimensional array which is basically an array of 2-dimensional arrays. My task is to take one of these 2-dimensional arrays and rotate it 90° counterclockwise. Here is ...

Tips for enabling both vertical and horizontal scrolling using the mousewheel on a webpage

Our website features a unique scrolling functionality where it starts off vertically and then switches to horizontal once the user reaches the bottom. This allows for a seamless transition between scrolling directions. In addition, users can easily naviga ...

Tips for partitioning an element using Selenium and Appium when it lacks a distinctive ID, class, package, or resource identifier:

I am currently trying to determine if I am receiving the expected response from a text message. In order to do this, I need to access the text view of the incoming message. The challenge I am facing is how to distinguish between the received text and the s ...

obtaining data from an HTML input field

I'm struggling to retrieve the value from an HTML input element, and despite my own research, it seems like I have the correct syntax. However, I'm still unable to get it to work, leaving me confused. When I log the value to the console, it appe ...

Encountering an issue with HTMLInputElement when trying to input an integer value into a label using JavaScript

script code intext = document.getElementById("intext"); totalin = document.getElementById("totalin"); function myFunction() { var x = document.getElementById("totalin"); x.innerHTML = intext.toString(); } The snippet above shows a JavaScript functio ...