Creating interactive Vue components using sync and event handling

In my Vue.js 2.3 project, I am dynamically rendering a list of components using <component v-for="..."> tags.

The structure of my template is as follows:

<some-component v-for="{name, props}, index in modules" :key="index">
    <component :is="name" v-bind="props"></component>
</some-component>

Within my component's data(), I have an array named modules:

modules: [
    {
        name: 'some-thing',
        props: {
            color: '#0f0',
            text: 'some text',
        },
    },
    {
        name: 'some-thing',
        props: {
            color: '#f3f',
            text: 'some other text',
        },
    },
],

I'm successfully using the v-bind={...} syntax to dynamically bind props. However, I am now exploring how to bind event listeners and synchronize props using v-on and .sync. I am unsure if this can be accomplished without creating custom directives.

My attempt to include event listeners and synced props directly within the props object did not yield the expected results:

props: {
    color: '#f3f',
    text: 'some other text',
    'v-on:loaded': 'handleLoaded', // unsuccessful
    'volume.sync': 'someValue', // unsuccessful
},

I have a specific goal to allow users to reorder widgets in a sidebar using vuedraggable and store their layout preferences in a database. Some of these widgets involve events and synchronized properties. How can I achieve this? Any suggestions are welcome!

Answer №1

It may not be possible to achieve this using a dynamic component, but you can use a render function instead.

Let's consider a modified data structure based on your original one.

modules: [
  {
    name: 'some-thing',
    props: {
      color: '#0f0',
      text: 'some text',
    },
    sync:{
      "volume": "volume"
    },
    on:{
      loaded: "handleLoaded"
    }
  },
  {
    name: 'other-thing',
    on:{
      clicked: "onClicked"
    }
  },
],

In this updated structure, I've included two additional properties: sync and on. The sync property contains the properties you want to synchronize. For instance, the sync property for one component includes volume: "volume", which would typically translate to :volume.sync="volume". While dynamically adding this to a dynamic component might not be straightforward, in a render function, you could deconstruct it into its components and add a property with an updated:volume handler.

Similarly, the on property in a render function allows you to attach event handlers identified by keys that call corresponding methods specified in the values. Below is a potential implementation of such a render function.

render(h){
  let components = []
  let modules = Object.assign({}, this.modules)
  for (let template of this.modules) {
    let def = {on:{}, props:{}}
    if (template.props){
      def.props = template.props
    } 
    if (template.sync){
      for (let sync of Object.keys(template.sync)){
        def.on[`update:${sync}`] = val => this[sync] = val
        def.props[sync] = this[template.sync[sync]]
      }
    }
    if (template.on){
      for (let handler of Object.keys(template.on)){
        def.on[handler] = this[template.on[handler]]
      }
    }
    components.push(h(template.name, def))
  }
  return h('div', components)
}

The render method essentially goes through the properties in the template within your modules array to determine how to render the component. It passes along ordinary properties, breaks down sync properties into their constituent parts, and adds appropriate event handlers for on properties.

Here's a working example of this concept.

console.clear()

Vue.component("some-thing", {
  props: ["volume","text","color"],
  template: `

// Template code here...

  `
})

Vue.component("other-thing", {
  template: `

// Template code here...

  `
})

new Vue({
  el: "#app",
  data: {
    modules: [{
        name: 'some-thing',
        props: {
          color: '#0f0',
          text: 'some text',
        },
        sync: {
          "volume": "volume"
        },
        on: {
          loaded: "handleLoaded"
        }
      },
      {
        name: 'other-thing',
        on: {
          clicked: "onClicked"
        }
      },
    ],
    volume: "stuff"
  },
  methods: {

// Methods defined here...

  },
  render(h) {
    let components = []
    let modules = Object.assign({}, this.modules)
    for (let template of this.modules) {
      let def = {
        on: {},
        props: {}
      }
      if (template.props) {
        def.props = template.props
      }
      if (template.sync) {
        for (let sync of Object.keys(template.sync)) {
        // Method implementation...
      }
      if (template.on) {
        for (let handler of Object.keys(template.on)) {
            // Event handling logic...
        }
      }
      components.push(h(template.name, def))
    }
    return h('div', components)
  },
})
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="7a0c0f1f3a485448544c">[email protected]</a>/dist/vue.js"></script>
<div id="app"></div>

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

Facing difficulty in passing data to a child component through VueX

<template> ... <note-info></note-info> <note-list></note-list> ... </template> I am utilizing VueX and store.js export default { state: { noteDataList: [], }, mutations: { setNoteDataList: fu ...

Utilize AxiosAbstraction to transmit a Patch request from a Vue.js application to a PHP backend

I need help with sending a PATCH request to update the birthdate of a user (promotor) from Vue.js frontend to PHP backend. The issue I'm facing is that the new date of birth is not getting saved in the database, and the existing date of birth in the d ...

What is the best way to trigger a function when a chart changes in Nuxt using Vue JS?

How can I trigger a function whenever a chart is loaded or changed in Vue Nuxt JS? I've tried using created, mounted, updated, and beforeMount but none of them seem to work. Can anyone offer any guidance? Edit 1: Here's the code snippet: upd ...

Is it possible to utilize Vue's splice method within a forEach loop in order to delete multiple elements at

I am currently working on an image grid in array form. I am trying to implement a multi-deletion functionality. When a checkbox for each image in the grid is checked, I store the selected image's index in an array called selectedImages. Upon clickin ...

What sets apart v-model from :model-value?

I'm curious because I have trouble grasping the distinction between v-model and :model-value. According to the information in the documentation: v-model is used on a native element: <input v-model="searchText"/> However, when used on ...

Creating a Vue JS project that utilizes both HTTP and HTTPS URLs for improved security

I'm currently utilizing Vue JS with the webpack template and dev mode. I have a question regarding how to configure my server to allow for both HTTPS and HTTP protocols simultaneously. I understand that enabling HTTPS can be done by simply adding "ht ...

Guide on sending files through an API request with formData() using Vuejs and Axios

My goal is to utilize an API to upload a file to the server. The documentation on documenter.getpostman outlines how to use the API: --form 'type="1"' \ --form 'user_id="1"' \ --form 'file=@"/C:/U ...

What is the total amount within a specified date range when retrieved as JSON?

Consider the following JSON structure: { "timesheets": [ { "user": { "username": "erik", "first_name": "Erik", }, &q ...

Error: Unable to locate attribute 'indexOf' within null object in vuejs when using consecutive v-for directives

I've been struggling with this issue for hours. I'm using vuejs' v-for to render items in <select> element's <options>, but I keep getting a type error. I've tried changing the :key values, but it still won't rende ...

Using Vanilla JavaScript and VueJS to smoothly scroll back to the top of a div when a button is clicked

I have a VueJS-based app that features a multistep form with a next button. You can check out the functioning of the app here: My current challenge is ensuring that once the user clicks the next button, the top of the following question becomes visible. I ...

Using Vue.js to Duplicate a Component Across a Page Multiple Times

My objective: I am working on implementing filters that will be displayed on a page to filter the products shown. For mobile devices, I want to hide these filters behind a button which, upon being clicked, will reveal the filters in a side-slide menu. Alt ...

What is the process for adding parameters to a Fetch GET request?

I have developed a basic Flask jsonify function that returns a JSON Object, although I am not certain if it qualifies as an API. @app.route('/searchData/<int:id>',methods=["GET"]) def searchData(id): return jsonify(searchData(id)) Curr ...

Storybook is pulling fonts from an incorrect location on the server

Regrettably, I am facing an issue where the fonts do not load when accessing a page with Quasar integration. In the Developer Tools, it shows a path error like this: http://localhost:6006/undefined/node_modules/@quasar/extras/roboto-font/web-font/KFOmCnqEu ...

What are the repercussions of labeling a function, TypeScript interface, or TypeScript type with export but never actually importing it? Is this considered poor practice or is there a potential consequence?

I find myself grappling with a seemingly straightforward question that surprisingly has not been asked before by others. I am currently immersed in a TypeScript project involving Vue, and one of the developers has taken to labeling numerous interfaces and ...

Creating an array of objects data is a breeze with Vue.js

I have an array of data containing selected items, and I need to extract the IDs from this array into a new array so that I can send only the IDs to the back-end. Sample Code method toggleSelection(rows) { console.log('this.multipleSelection : &a ...

Importing Global Sass Styles in Nuxt 3 for Static Assets

I'm currently attempting to import a global Sass stylesheet from the /assets directory in order to utilize variables and mixins defined there throughout the components. My current configuration in nuxt.config.ts is as follows: import { defineNuxtConfi ...

The use of fontawesome in vue is currently not functioning as expected

Has anyone successfully installed fontawesome in a Vue application? I've tried several tutorials, but none of them seem to work. The icons either don't show up or the plugin doesn't render at all. I prefer not to import the font using a scri ...

What are the benefits of opting for <router-link> compared to using <a href="">?

When utilizing vue-router, the component known as <router-link> is available for linking to various routes. For instance: <router-link to="/foo">Link</router-link> Nevertheless, it appears that the subsequent code accomplishes a similar ...

Upgrading from Vue.js 1.x to 2.x: Understanding the Compilation Process

Code snippet 1: It seems to be functioning properly. this.$compile(this.$els.ajaxcontent); Migration notes: this.$compile(this.$refs.ajaxcontent); // Issue encountered: $compile function not found. Vue.compile(this.$refs.ajaxcontent); // Problem: temp ...

Using static methods within a static class to achieve method overloading in Typescript

I have a TypeScript static class that converts key-value pairs to strings. The values can be boolean, number, or string, but I want them all to end up as strings with specific implementations. [{ key: "key1", value: false }, { key: "key2&qu ...