Home > Software engineering >  Use Vue component data in JavaScript
Use Vue component data in JavaScript

Time:10-22

How are we supposed to access a Vue component its data from outside the app? For example how can we get the data within a regular JavaScript onClick event triggered from a button that is in the DOM outside the Vue app.

In the following setup I have a hidden field which I keep updated with every action in the Vue app, this way I have the necessary data ready for the JS click event .. but I am sure there is a better way.

Currently my setup is the following:

VehicleCertificates.js

import { createApp, Vue } from 'vue'
import VehicleCertificates from './VehicleCertificates.vue';

const mountEl = document.querySelector("#certificates");
const app = createApp(VehicleCertificates, { ...mountEl.dataset })
const vm = app.mount("#certificates");

VehicleCertificates.vue

<template>
    <div style="background-color: red;">
        <h3>Certificates</h3>
        <div>
            <table class="table table-striped table-hover table-condensed2" style="clear: both;">
                <thead>
                    <tr>
                        <th><b>Type</b></th>
                        <th><b>Valid From</b></th>
                        <th><b>Valid Till</b></th>
                        <th style="text-align: right;">
                            <a href="#" @click='addCertificate'>
                                <i class="fa fa-plus-square"></i> Add
                            </a>
                        </th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="(certificate, index) in certificates" :key="index">
                        <td>{{ certificate.CertificateTypeDescription }}</td>
                        <td>
                            {{ certificate.ValidFrom }}
                        </td>
                        <td>
                            {{ certificate.ValidTo }}
                        </td>
                        <td>
                            <a href='#' @click="removeCertificate(index)" title="Delete" style="float: right;" class="btn btn-default">
                                <i class="fa fa-trash"></i>
                            </a>
                        </td>
                    </tr>
                    <tr v-show="certificates.length == 0">
                        <td colspan="4">
                            No certificates added
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</template>

<script>
    import axios from 'axios';
    import { onMounted, ref } from "vue";

    export default {
        props: {
            vehicleId: String
        },
        data() {
            return {
                count: 0,
                certificates: ref([]),
                types: []
            }
        },
        created() {
            onMounted(async () => {
                let result = await axios.get("/api/v1.0/vehicle/GetCertificates", { params: { vehicleId: this.vehicleId } });
                this.certificates.splice(0, 0, ...result.data);
                this.certificatesUpdated();
            });
        },
        methods: {
            removeCertificate(index) {
                this.certificates.splice(index, 1);
                this.certificatesUpdated();
            },
            addCertificate() {
                this.certificates.push({ CertificateTypeDescription: 'ADR', ValidFrom: 1, ValidTo: 2 });
                this.certificatesUpdated();
            },
            certificatesUpdated() {
                $("#VehicleCertificatesJson").val(JSON.stringify(this.certificates));
            }
        }
    }
</script>

In the end I want to be able to send the data from the Vue app together with other non-Vue data on submit of an ASP.Net core razor page its form. The Vue app is just a specific part of the razor view and thus not an SPA.

Thanks in advance!

CodePudding user response:

Here's a pretty complex solution - but at least it's quite flexible.

  1. Create a Vue.observable store: this is nothing else, but a reactive object
  2. Create the methods you want to use to update the observable in your Vue instance
  3. Add a watcher to the store: this is a standard Vue object & a $watch set on it
  4. Set up the callback if the store changes (watcher instance): this callback is where you can connect with the "outside world"

Snippet:

const countervalueSpan = document.getElementById('countervalue')

// creating a Vue.observable - 
// reactive object
const store = Vue.observable({
  counter: 0
})

// setting up a watcher function
// using the Vue object
function watch(obj, expOrFn, callback, options) {
  let instance = null

  if ('__watcherInstance__' in obj) {
    instance = obj.__watcherInstance__
  } else {
    instance = obj.__watcherInstance__ = new Vue({
      data: obj
    })
  }

  return instance.$watch(expOrFn, callback, options)
}

// creating a watcher that reacts
// if the given store item changes
const subscriber = watch(
  store,
  'counter',
  (counter) => {
    let html = `<strong>${counter}</strong>`
    countervalueSpan.innerHTML = html
  }
)

new Vue({
  el: "#app",
  methods: {
    increment() {
      store.counter  
    }
  },
  template: `
    <div>
      <button
        @click="increment"
      >
        INCREMENT
      </button>
    </div>
  `
})
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id="outside">
  Counter outside: <span id="countervalue"></span>
</div>
<div id="app"></div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

You can always access the store object from the outside easily (e.g. store.counter) & you always get the current state of the object. The watcher is needed to react to changes automatically.

CodePudding user response:

I would advise against doing it and wrapping everything in Vue or directly use JQuery, depending on how your website is build. Having multiple frontend frameworks is usually a bad idea and introduces unnecessary complexity.

However, if you really need to access Vue's data with plain javascript you can use the following:

const element = document.getElementById('#element-id');
element._instance.data // or element._instance.props, etc...

For the properties available you can look at the inspector (see attached screenshot). Inspector screenshot

  • Related