Creating a Vue 3 component and embedding it as an HTML string

Currently, I am tackling a project in vue.js (version 3) and find myself in a situation where I need to incorporate the HTML output of one component into another component's method.

In my Vue project, I have developed an SVG component as shown below:

CircleWithTextSvg.vue

<template>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" width="20" height="20">
        <g id="UrTavla">
            <circle cx="10" cy="10" r="10" stroke="gray" stroke-width="1" :fill="fill" />
            <text x="50%" y="50%" text-anchor="middle" stroke="black" stroke-width="1px" dy=".3em">{{text}}</text>
        </g>
    </svg>
</template>

<script>
    export default {
        props: {
            text: { 
                type: String, 
                default: "1"
            },
            fill: { 
                type: String, 
                default: "white"
            }
        }
    }
</script>

This specific component displays a circle with text inside it. When added to the template section of my main component, as shown below, it functions correctly.

MainComponent.vue

<template>
    <circle-with-text-svg />
</template>

However, I am looking to provide this rendered SVG component output as a parameter to a third-party entity.

Real Use Case:- The intention behind creating this separate component was to showcase it as a marker on my leaflet map. The issue arises when I try to embed this SVG component within the method of my MainComponent so I can utilize it as an option for L.divIcon

When attempting the following:

export default {
    methods: {
        generateMap(element) {
            // ...
            let icon = L.divIcon({ 
                html: <circle-with-text-svg 
                    fill="'#D3D5FF'"
                    text="1" />, 
                iconSize: [10, 10]
            });
            // ...
        }
    }
}

I encounter the errors:

The experimental syntax 'JSX isn't currently enabled

In React, incorporating components within another component's template is straightforward. Nevertheless, how can we accomplish this in vue.js?

Based on the error message, it appears that JSX experimental mode has not been activated.

Could someone guide me on how to resolve this challenge?

Answer №1

After recommending a solution for a question about how to retrieve compiled HTML content of a Vue component in Vue 2, I decided to test if the same approach works in Vue 3.

Here are the changes needed to adapt the solution for Vue 3:

  1. The most obvious change is replacing new Vue with createApp and using global h() instead of the one passed into render().
  2. In Vue 2, calling the $mount() function without arguments worked because Vue created a DOM element to mount to in memory. In Vue 3, you need to provide the element yourself.
  3. An important change in Vue 3 is that components registered globally with app.component() are not accessible in another instance used for rendering HTML. All components must be registered in the appropriate instance - refer to the migration guide for more details.

// Use component options object with string template
// In a proper Vue app (with Webpack and Vue SFC), this block can be replaced with "import CircleWithTextSvg from CircleWithTextSvg.vue"
const CircleWithTextSvg = {
  name: 'CircleWithTextSvg',
  props: {
    text: {
      type: String,
      default: "1"
    },
    fill: {
      type: String,
      default: "white"
    }
  },
  template: `
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" width="20" height="20">
        <g id="UrTavla">
            <circle cx="10" cy="10" r="10" stroke="gray" stroke-width="1" :fill="fill" />
            <text x="50%" y="50%" text-anchor="middle" stroke="black" stroke-width="1px" dy=".3em">{{text}}</text>
        </g>
    </svg>
  `
}

const app = Vue.createApp({
  mounted() {
    console.log(this.getIconHtml('Hello!'))
  },
  methods: {
    getIconHtml(text, type) {
      const tempApp = Vue.createApp({
        render() {
          return Vue.h(CircleWithTextSvg, {
            text,
            type
          })
        }
      })

      // in Vue 3 we need real element to mount to unlike in Vue 2 where mount() could be called without argument...
      const el = document.createElement('div');
      const mountedApp = tempApp.mount(el)

      return mountedApp.$el.outerHTML
    }
  }
})

app.mount('#app')
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="cbbdbeae8bf8e5fae5ff">[email protected]</a>/dist/vue.global.js"></script>
<div id='app'>
</div>

Please note: 1. The code above is designed to run directly in the browser where import is unavailable. Therefore, we use the Vue global build and access Vue global APIs through functions like Vue.createApp or Vue.h. In a regular Vue app, these functions should be imported as

import { createApp, h } from 'Vue'
. 2. If the HTML fragments used with Leaflet components are as simple as your CircleWithTextSvg component, consider defining them as template literals for better performance.

Answer №2

I attempted the suggested solution mentioned above, but unfortunately it didn't work for me. The $el.outerHTML seemed off and I kept encountering null and undefined errors due to the lifecycle in the rest of my application. In Vue3, I tried a different approach which still doesn't feel quite right, but I'm making progress :)


const getIconHtml = (item, element) => render(h(CircleWithTextSvg, {
  onSomeEmit: (ev) => {
    
  },
  text: item, //props
}), element //element to render to
)

In my scenario, I needed to generate a template for integration with a third-party JavaScript library (visjs).

This is how I implemented it:

import {render, h} from 'vue';

const itemTemplate = async (item, element) => render(h(bar, {
  onDelete: (ev) => {
    vis.removeItem(item.id)
  },
  item: item,
}), element
)

//note this template key does not refer to vuejs template
  template: (item, element) => {
    if (!item) return;
    return itemTemplate(item, element);
  }


When using this approach, consider the following: This method essentially creates a new Vue instance, so any additional apps, props, components, or directives you wish to include may need to be manually registered within this component. While some globals might function, I encountered issues with directives due to a 'deep' problem.

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

Unveil the content of a string by applying Base64 decoding in AngularJS

After encrypting a token sent from JAVA code to Angular using Base64 encryption, the next step is decryption: String token = "1345BCHCNB"; Cipher ecipher = Cipher.getInstance("AES"); String mykey = "1234567891234567"; SecretKey key = new SecretKey ...

What is the best method for applying an active class to a particular element?

I have the following methods: methods: { replyBox: function(event){ event.preventDefault(); this.isActive = !this.isActive; ); }, In my view, I have this: <div class="comment_list" v-for="comme ...

Guide to streaming audio files using vue.js

I am attempting to incorporate an audio file into my vue.js project using the following code: import sound from '../../recordings/sound.mp4' const audio = new Audio(sound) audio.play() Although this method works perfectly fine, I have encounter ...

Anomalies encountered during the iteration of a table

As I work on building a table by looping through an API array, I've encountered a few obstacles. Here is the code snippet that's causing me trouble -> $html = " <tr class='mt-2'> <td>{$rank}.</td> ...

Exploring the world of jQuery and JavaScript's regular expressions

Looking to extract numeric characters from an alphanumeric string? Consider a scenario where the alphanumeric string resembles: cmq-1a,tq-2.1a,vq-001,hq-001a... Our goal is to isolate the numeric characters and determine the maximum value among them. Any ...

How can one selectively apply a class to the initial DIV within a collection of dynamically generated DIV elements without relying on jQuery?

I am currently working on implementing a slideshow within a modal using AngularJS and Bootstrap. My challenge lies in dynamically creating DIVs, but specifically adding the active class to only the first DIV. Is there a way to achieve this without relying ...

Removing a hyperlink and adding a class to a unordered list generated in JavaScript - here's how!

In my JavaScript code, I have the following implementation: init: function() { var html = []; $.each(levels, function(nr) { html.push('<li><a href="#">'); html.push(nr+1); ...

Storing approximately 1 kilobyte of information throughout various pages

Is it feasible to store approximately 1kb of data while transitioning between two pages on the same domain using Javascript, Jquery (1.7), and Ajax? For instance, a user inputs data into a textbox on one page and then moves to another specific page. Can ...

NodeJS web crawler facing difficulty in retrieving the tagname linked with the specified search term

I successfully developed a web crawler using NodeJS The specific website I targeted was "http://www.google.com" Technologies used include NodeJS and Cheerio One of my notable achievements is the ability to search for specific text on a webpage, such as ...

What is the method for sending an array in x-www-form-urlencoded format using react-native?

this function is used for sending the API request export const socialPostMurmur= (publicKey,content,sig,visibility,video,photo)=>{ // console.log(publicKey,content,sig,visibility,video,photo); console.log('photo',photo); let data; da ...

Unable to send information to a function (using jQuery)

I'm struggling to pass the result successfully to another function, as it keeps returning an undefined value: function tagCustomer(email, tags) { var o = new Object(); o.tags = tags; o.email = email; o.current_tags = getCustomerTags(email ...

Is it possible to utilize this Javascript / jQuery function repeatedly within a single webpage?

<script> document.addEventListener('DOMContentLoaded', function() { jQuery(function($){ $('.clicktoshow').each(function(i){ $(this).click(function(){ $('.showclick').eq(i).show(); ...

What are the best practices for managing DOM manipulation with TypeScript instead of plain JavaScript?

I'm a beginner in Typescript and I want to incorporate the following JavaScript functionality into a Typescript file. http://jsfiddle.net/SgyEW/10/ $('.toggler').live('click', function() { var idname = ...

The production Vue app does not receive the Docker-compose variable

I am experiencing an issue where Docker-compose environment variables do not appear to be set as expected. Despite trying to utilize env_file and environment fields, only NODE_ENV and BASE_URL variables are visible when printing process.env in my Vue app. ...

How can I represent the square bracket character using regex in JavaScript?

I'm trying to figure out how to create a regex for square brackets ( [ ] ) in JavaScript. Any advice? ...

The updates made to a variable within an ajax request are not immediately reflected after the request has been completed

My global variable is not displaying the expected result: function checkLiveRdv(salle, jour, dateus, heure) { var resu; var urlaction = myUrl; $.ajax({ type: "POST", dataType: "json", url: urlaction, data: myDatas, suc ...

Ensuring grid columns are equal for varying text sizes

I am looking to achieve equal width and spacing for columns without using the width, min-width, max-width properties. Can anyone help me accomplish this using flex or any other method? https://i.sstatic.net/xo56M.png .d-flex { display: flex; } .d-fl ...

underscore's _.each() method for callback functions

I've been struggling with implementing my custom _.each() function within another function and keep encountering the issue of getting "undefined" returned. My goal is to utilize _.each() to apply a test function to an array. Despite being aware that t ...

Receiving the result of a processed aspx page with a slight delay

My ASPX page contains some JavaScript code: <script> setTimeout("document.write('" + place.address + "');",1); </script> The issue arises when trying to retrieve the output from another page using a query string. Instead of gett ...

Deselect a checkbox by selecting a radio button with just Javascript code

Hey there! I'm currently delving into the world of pure JavaScript and could really use some guidance on a particular issue. Here's what I'm aiming to accomplish: I'd like to design a checkbox that triggers the opening of a window whe ...