Incorporating HTML content into a Vue component

I'm running into issues trying to display the content of an HTML file within a Vue component. Essentially, I have a Django backend that generates an HTML file using Bokeh and backtesting.py library. On the frontend side, I'm utilizing Nuxt/Vue, which makes it challenging to dynamically load the HTML content onto the page.

If you'd like to view how the HTML file appears, you can check it out here:

The goal is to load the content from that HTML file in a basic Vue component:

<template>
  
  <div>
    <h1>Some content here</h1>
  </div>

</template>


<script>


export default {
    components: {

    },
    data() {
        return {
      
        }
    },

    mounted() {      
    },
    methods: {

    }
}
</script>

However, I've been struggling to figure out a solution. Simply copying and pasting the content into the Vue component results in numerous errors due to the use of <script> tags within the component. I did try loading the BokehJS CDN in my index.html file, but this still led to a Bokeh is undefined error within the component.

If anyone has any advice or suggestions on how to resolve this issue, it would be greatly appreciated. Thank you!

Answer №1

Jack's response is right on target and closely aligns with my own solution to this problem in the past.

However, I have an alternative approach using an iframe that may be useful if reactivity is a priority. You can find a codesandbox link here.

The key difference is that this method loads the code/HTML via XHR and manually inserts it into the iframe. By using this technique, you can introduce some reactivity as needed.

<script>
export default {
  components: {},
  data() {
    return {};
  },
  async mounted() {
    this.initialize();
  },
  methods: {
    async initialize() {
      const html = await this.loadHTML();
      const doc = this.htmlToDocument(html);
      this.updateIframe(doc);
    },
    async loadHTML() {
      const response = await fetch("/plot");
      const text = await response.text();
      return text;
    },
    htmlToDocument(html) {
      const parser = new DOMParser();
      const doc = parser.parseFromString(html, "text/html");
      return doc;
    },
    updateIframe(doc) {
      const iframe = this.$refs.frame;
      const iframeDocument = iframe.contentWindow.document;
      iframeDocument.open();
      iframeDocument.write(doc.documentElement.innerHTML);
      iframeDocument.close();
    }
  },
};
</script>

I've included two additional methods in the codesandbox to demonstrate how reactivity can be implemented using this approach:

    modify() {
      if (this.orig) {
        // Only for the purpose of this example.
        // It's already been modified. Just short-circuit so we don't overwrite it
        return;
      }
      const bokehDoc = this.$refs.frame.contentWindow.Bokeh.documents[0];
      // Get access to the data..not sure if there's a better/proper way
      const models = [...bokehDoc._all_models.values()];
      const modelWithData = models.find((x) => x.data);
      const { data } = modelWithData;
      const idx = Math.floor(data.Close.length / 2);
      // Store old data so we can reset it
      this.orig = data.Close[idx];
      data.Close[Math.floor(data.Close.length / 2)] = 0;
      modelWithData.change.emit();
    },
    reset() {
      if (!this.orig) {
        return;
      }
      const bokehDoc = this.$refs.frame.contentWindow.Bokeh.documents[0];
      // Get access to the data..not sure if there's a better/proper way
      const models = [...bokehDoc._all_models.values()];
      const modelWithData = models.find((x) => x.data);
      const { data } = modelWithData;
      const idx = Math.floor(data.Close.length / 2);
      data.Close[idx] = this.orig;
      modelWithData.change.emit();
      delete this.orig;
    }

Answer №2

One of the easiest methods is to host your HTML on a server of your choice, even without using Vue.

To integrate this HTML into your app, you can utilize an <iframe> with the src attribute pointing to that HTML file. For instance, take a look at this sample utilizing codesandbox.io, where I included your content in the index.html. Below is a demonstration showing how it functions with both <iframe> and <object> elements:

Vue.config.productionTip = false;
Vue.config.devtools = false;

new Vue({
  'el': '#app'
})
body  {
  margin: 0;
}
h1, h3 {padding-left: 1rem;}
object, iframe {
  border: none;
  height: 800px;
  width: 100%;
  min-height: calc(100vh - 125px);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <h1>This content is placed in Vue</h1>
  <h3>Vue doesn't really care.</h3>
  <iframe src="https://1gk6z.csb.app/"></iframe>

  <h1><code>&lt;object></code> works, too:</h1>
  <object type="text/html" data="https://1gk6z.csb.app/"></object>
  
</div>

Note: if the domain serving the graph and the one displaying it differ, you'll need server-side configuration to allow the embed (most domains have it turned off by default).

Answer №3

Approach:

  1. Include and initialize Bokeh in the head section of public/index.html
  2. Read a file as a string using ajax/xhr and convert it into a DOM tree
  3. Identify and extract each required element from the parsed tree
  4. Recreate and add each element back to the document

No iframe is necessary. The window.Bokeh can be accessed directly.

A basic demonstration of reactivity is illustrated through the utilization of the method logBkh, which logs the global Bokeh object upon clicking the graph

<template>
  <div id="app">
    <div id="page-container" @click="logBkh"></div>
  </div>
</template>

<script>
// fetched from filesystem for testing purposes
import page from 'raw-loader!./assets/page.txt'

// parse as dom tree
const extDoc = new DOMParser().parseFromString(page, 'text/html');

export default {
  methods: {
    logBkh(){
      console.log(window.Bokeh)
    }
  },
  mounted() {
    const pageContainer = document.querySelector('#page-container')

    // generate and append root div
    const dv = document.createElement('div')
    const { attributes } = extDoc.querySelector('.bk-root')

    for(const attr in attributes) {
      dv.setAttribute(attributes[attr].name, attributes[attr].value)
    }
    pageContainer.append(dv)
    
    for(const _scrpt of extDoc.body.querySelectorAll('script')) {

      // generate and append each script
      const scrpt = document.createElement('script')

      for(const attr in _scrpt.attributes) {
        scrpt.setAttribute(
          _scrpt.attributes[attr].name,
          _scrpt.attributes[attr].value
        )
      }
      scrpt.innerHTML = _scrpt.innerHTML
      pageContainer.append(scrpt)
    }
  }
}
</script>

outcome:

https://i.sstatic.net/e7hNS.png

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

The .removeAttr('checked') method fails to function as expected

I am currently using CodeIgniter. Check out this example of my code. Please fill the textbox and check the checkbox next to it, then click on the "add" link. You will notice that it does not work as expected..removeAttr('checked') newDiv.find(&a ...

What could be causing the .hover function to malfunction and how can I make it so that the .hover function only applies within the corner radius area?

I am attempting to make circles react to my jquery .hover function. Below is the JavaScript code I am using: jQuery.fn.center = function () { this.css("position","absolute"); this.css("top", Math.max(0, (($(window).height() - this.outerHeight()) / 2) + ...

Is it possible to time a page without relying on the Form tag?

As I search for solutions, I have come across some examples that require a Form tag, but unfortunately, it doesn't seem to integrate well with my current application. My goal is to implement a timer on a webpage that starts counting when one button i ...

Remove all spaces from input fields in angular Typescript, excluding the enter key

I've encountered an issue where the code below removes all spaces, but it's also removing the enter key. Is there a way to remove only spaces and not affect the enter key? static stripDoubleSpaces(str: string): string { if (!!str) { ...

Discovering the size and count of JavaScript objects within a browser's memory

Many suggest using the Chrome Profiler Heap Snapshot to analyze memory usage, but I have found that on an empty page (no JavaScript or CSS, just HTML), it shows a heap size of 8MB and anywhere from 12 to 30 thousand objects depending on its mood. This tool ...

The value of Vue's v-model data is currently undefined, likely due to an

When working with Vue 2, I encountered a scenario where I had data coming in from an ajax call. Here is the code snippet that exemplifies this: <template> <div> <input type="input" class="form-control" v-model="siteInfo.siteId"& ...

Exploring the process of transferring a jQuery array from a Rails controller to a PostgreSQL array column

For my project, I successfully pass a JavaScript array to a Rails controller using AJAX. The JavaScript array consists of upload image names. Within my postgresql database, there is an array column called "images." In the Rails controller, I attempted the ...

Obtain the response header variable within a Shiny application

In Apache, the LDAP login is passed to a variable called X-Remote-User in the header: https://i.sstatic.net/7jyxO.jpg I am unsure how to retrieve this information in my Shiny app. Does anyone have any ideas? Maybe using JavaScript could be a solution? ...

Ways to retrieve the page name where the script originates from

I have a function that is triggered from three different pages. Each page involves adding an attribute to a specific div. For instance: <div id="posts" page="home"></div> <div id="posts" page="feed"></div> <div id="posts" page= ...

How can I select a checkbox dynamically during runtime?

I am working on a JavaScript code that needs to add the checked option to a checkbox if it has an id or value of 2 at runtime. I have tried the following code, but unfortunately, I am unable to check the checkbox. Do you have any ideas on how to solve th ...

Having difficulty with building a basic module in Node JS, it's just not cooperating

As a newcomer to Node JS, this platform, and the English language, I apologize in advance for any linguistic errors. I seem to be encountering a "return" error within my code. Specifically, when I include the hi.myFunc(); function, I receive the ...

Is utilizing a standardized logging system considered a best practice for a node and express application?

I am currently working on a node.js application that consists of several dozen modules and uses bunyan for logging with JSON output and multiple configurable streams. I have been searching for good examples on how to implement a logger instance across all ...

Is there a way to incorporate multiple functions into a single sx property, such as color, zIndex, backgroundColor, etc? Can this be achieved in any way?

I am currently developing a single search component that will be used across various sections of my application. Each component receives a prop called search: string to determine its type and apply specific styles accordingly. Although I could use classNam ...

Tips for updating the navigation bar once a user logs in

Currently, I am working on a project that involves using Laravel in conjunction with vue, and integrating JWT auth for user authentication. One of the challenges I have encountered is updating the navigation bar links based on the user's login status. ...

Is there a way to manually trigger a re-render of all React components on a page generated using array.map?

Within my parent component (Game), I am rendering child components (Card) from an array. Additionally, there is a Menu component that triggers a callback to Game in order to change its state. When switching levels (via a button click on the Menu), I want a ...

When importing modules in node.js, the presence of a function can overwrite another function even if it

Within this code snippet, I am utilizing Express.js. //index.js app.use('/',routes()); //app/routes.js module.exports = function() { express = require('express'); const loggedUserProfileController = require('../controller ...

Troubleshooting VueJS Promise.all Problem

I need help implementing promise-based logic for asynchronous data fetching in VueJS. Previously, I had the following logic: if (influencer.suggested?.length && url.length) { const [ interactions, suggested_ids ] = await Promise.all([ $axios.$ ...

Easily move a group of HTML elements at the same time with a simple

Exploring HTML5 Drag and Drop with Multiple Elements When it comes to dragging and dropping multiple elements in HTML5, there seems to be a limitation with the default draggable attribute. This attribute allows only one element to be dragged at a time, ma ...

Transferring Variables from WordPress PHP to JavaScript

I have implemented two WordPress plugins - Snippets for PHP code insertion and Scripts n Styles for JavaScript. My objective is to automatically populate a form with the email address of a logged-in user. Here is the PHP snippet used in Snippets: <?p ...

Encountering the message "Error: Unable to access undefined properties (reading 'username')" while making my POST request

My POST request isn't functioning correctly and it's failing to update. The specific error message I'm encountering is: TypeError: Cannot read properties of undefined (reading 'username') app.post('/create-user', functio ...