Utilizing Vue components to streamline the process of adding and updating data

I have a rather straightforward parent/child component. I am looking to utilize the child component in two ways - first, for adding a new entry and second, for updating an entity.

Here are the components that I've built: https://codepen.io/anon/pen/bJdjyx. In this implementation, I do not use props; instead, I sync the value from the parent to the child using custom events.

Add - Template:

<div id="app">
  <v-app>
    <v-content>
      <v-container grid-list-xl>
        <my-address
          :addresscompany.sync="addressCompany"
          :addressstreet.sync="addressStreet"
          ></my-address>
        <v-btn @click="submit">Submit</v-btn>
      </v-container>
    </v-content>
  </v-app>
</div>

<script type="text/x-template" id="address-template">
    <div>
      <v-text-field
        name="company"
        v-model="addressCompany"
        @change="updateCompany()">
        </v-text-field>
      <v-text-field
        name="street"
        v-model="addressStreet"
        @change="updateStreet()">
        </v-text-field>
    </div>
</script>

Add - Script:

let addressComponent = {
  template: '#address-template',

  data() {
    return {
      addressCompany: '',
      addressStreet: '',
    }
  },

  methods: {
    updateCompany () {
      this.$emit('update:addresscompany', this.addressCompany);
    },
        updateStreet () {
      this.$emit('update:addressstreet', this.addressStreet);
    }
  }
};


new Vue({
  el: '#app',
  components: {'my-address' : addressComponent},

  data() {
    return {
      addressCompany: '',
      addressStreet: '',
    }
  },

  methods: {
    submit () {
      console.log('Company ' + this.addressCompany);
       console.log('Street ' + this.addressStreet);
    }
  }
})

However, this template does not work for the edit case because I need props to pass the value to the child. Therefore, I have come up with this solution: https://codepen.io/anon/pen/zXGLQG

Update - Template:

<div id="app">
  <v-app>
    <v-content>
      <v-container grid-list-xl>
        <my-address
          :addresscompany.sync="addressCompany"
          :addressstreet.sync="addressStreet"
          ></my-address>
        <v-btn @click="submit">Submit</v-btn>
      </v-container>
    </v-content>
  </v-app>
</div>


<script type="text/x-template" id="address-template">
    <div>
      <v-text-field
        name="company"
         :value="addressCompany"
        @change="updateCompany()">
        </v-text-field>
      <v-text-field
        name="street"
         :value="addressStreet"
        @change="updateStreet()">
        </v-text-field>
    </div>
</script>

Update - Script:

let addressComponent = {
  template: '#address-template',
  props: ['addressCompany', 'addressStreet'],

  data() {
    return {
    }
  },

  methods: {
    updateCompany () {
      this.$emit('update:addresscompany', this.addressCompany);
    },
        updateStreet () {
      this.$emit('update:addressstreet', this.addressStreet);
    }
  }
};


new Vue({
  el: '#app',
  components: {'my-address' : addressComponent},

  data() {
    return {
      addressCompany: 'Company',
      addressStreet: 'Street',
    }
  },

  methods: {
    submit () {
      console.log('Company ' + this.addressCompany);
       console.log('Street ' + this.addressStreet);
    }
  }
})

The key difference is that for the update case, I don't use v-model on the child elements as I can't directly change the props. Instead, by using :value, the update event is not triggered.

What is the correct way to use this child component for both add and update operations? Is there a standard Vue approach before resorting to Vuex for these functionalities?

Thank you!

Answer №1

I have frequently utilized the following strategy:

Develop a component that serves as an abstraction of your entity. In this instance, I created a Person.vue with firstName and lastName props.

Vue.component('Person' , {
   template: `
    <div>
      <input type="text"
             :value="firstName"
              @input="$emit('update:firstName', $event.target.value)"/>
      <input type="text"
              :value="lastName"
              @input="$emit('update:lastName', $event.target.value)"/>
    </div>
  `,
  props: {
    firstName: String,
    lastName: String
  }
});

The purpose of this component is solely focused on connecting to a Person object, responding to its changes, and updating the parent using the sync event modifier.

Subsequently, create a wrapping component responsible for managing the state of the actual object, e.g., Person.js. Its child component Person.vue doesn't distinguish between Add or Edit "modes". It simply reacts to model changes.

This component should fetch the Person Object from an API call or data store (Update mode), or generate a new empty one like new Person() (Add Mode).

A straightforward example is provided below. Ideally, the parent component (e.g., app) would be structured in a way that minimizes the need for helper properties.

function Person (firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
Vue.component('Person' , {
  template: `
    <div>
      <input type="text"
             :value="firstName"
              @input="$emit('update:firstName', $event.target.value)"/>
      <input type="text"
              :value="lastName"
              @input="$emit('update:lastName', $event.target.value)"/>
    </div>
  `,
  props: {
    firstName: String,
    lastName: String
  }
});
new Vue({
  el: '#app',
  template: `
    <div>
      <!-- Add mode, should be its own component -->
      <section v-if="promptAdd">
        <Person v-bind.sync="newPerson"/>
        <button type="button" @click="onAdded">Add</button>
      </section>
      <!-- Update mode, should be its own component -->
      <section v-else>
        <p>Users:</p>
        <div v-for="(person, index) in arr" :key="index">
          <Person v-bind.sync="person"/>
        </div>
        <button type="button"
                @click="addNew">Add New User</button>
      </section>
    </div>
  `,
  data: () => ({
    promptAdd: false,
    newPerson: undefined,
    arr: [
      new Person('Bob', 'Smith')
    ]
  }),
  methods: {
    addNew() {
      this.newPerson = new Person();
      this.promptAdd = true;
    },
    onAdded () {
      this.arr.push({ ...this.newPerson });
      this.promptAdd = false;
      this.newPerson = undefined;
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/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

Sophisticated filter - Conceal Ancestry

Check out this snippet of my HTML: <td> <a class="button" href="#"> <input id="download">...</input> </a> <a class="button" href="#"> <input id="downloadcsv">...</input> </a> </td> I am ...

Encountering the error message 'XMLHttpRequest is not defined' while incorporating getServerSideProps() in NextJS

I'm currently exploring NextJS with SSR and encountering an error when trying to fetch data from a Spotify playlist using the spotify-web-api-js library. This issue only occurs when executing on the server side: error - ReferenceError: XMLHttpRequest ...

React JS error: Trying to use props.video as a function, but it's not

As a newcomer to React, I'm encountering some errors that I need help debugging. Error #1: Uncaught TypeError: props.videos.map is not a function at new VideoList Error #2: bundle.js:19956 Error: findComponentRoot(..., .0.0.0): Unable to find el ...

The elements within the array are being refreshed accurately, however, a separate component is being removed

I have developed a component that has the ability to contain multiple Value components. Users can add as many values as they want, with the first value being mandatory and non-removable. When adding two new Value components, I provide input fields for name ...

Saving the author of a message from one function and transferring it to another

I'm currently working on a Discord bot that manages tickets as applications. I've almost completed it, but I want the bot to log the closed ticket when the -close command is used. I've experimented with different approaches, such as using a ...

Using Jquery Functions within Codeigniter

I am facing an issue with calling a function containing jQuery script within an if-else statement. Below is the codeignitor view code: Code if($osoption == "windows") { ?> <script> windows(); </script> <? ...

Unlimited scrolling feature in Ionic using JSON endpoint

Trying to create an Ionic list using data from a JSON URL. JSON Code: [{"id":"1","firstName":"John", "lastName":"Doe"}, {"id":"2","firstName":"Anna", "lastName":"Smith"}, {"id":"3","firstName":"Peter", "lastName":"Jones"},{......................}] app.j ...

Keeping track of important dates is ineffective using the calendar

I am facing an issue with developing a calendar that marks events on the correct dates. I am receiving the dates in the following format in PHP [ { "status": "OK", "statusCode": 200, "statusMensagem": & ...

"Unlocking the Power of Colormap in JavaScript: A Comprehensive

I am in need of incorporating a colormap into my JavaScript page. My server side is powered by Flask, written in Python. To render colormaps on the client side, I have a JavaScript file that requires the colormap module (installed using "npm install colorm ...

Unlock the power of TypeScript's inheritance by utilizing static methods for type

In my TypeScript project, I have two classes: BaseModel and HotelModel. The HotelModel extends the BaseModel class, which provides static methods like findById, all, etc. export default class BaseModel { private collection:string _id:string | undefine ...

It is not possible to recycle a TinyMCE editor that is embedded in a popup

Having a frustrating issue with the TinyMCE Editor plugin embedded within a Fancybox pop-up window. I have a list of objects with Edit links that trigger an AJAX call to retrieve content from the server and place it in a <textarea>. A TinyMCE editor ...

Discovering whether an image contains a caption with the help of JavaScript

There are various websites that display captions on images in paragraphs, h1 tags, or contained within a div along with the image. I am interested in learning how to determine if an image has an associated caption using JavaScript, especially when the cap ...

Exploring Selenium: Clicking on Auto-Complete Suggestions using Python

Attempting to interact with an auto-complete search bar on the site in order to search for results. Wanting to click on the drop-down element that appears after entering a city name to perform a full city name search and obtain results. Below is the cod ...

Is there a way to trigger $q notify without initiating a $digest cycle?

Within my application, the $digest cycle typically takes around 5ms to complete. I heavily utilize $q.defer with deferred.notify throughout my codebase, but I've encountered an issue. Each time deferred.notify is triggered, it schedules a new digest c ...

Creating a dynamic image carousel using jQuery

Today, I completed the jQuery course on Code Academy and am now trying to create an image slider using it. While I have a general idea of how it should work, I feel like I'm missing a key element. My goal is to have the slider continuously running whe ...

By utilizing .focus() in iOS8, the virtual keyboard will be displayed and the page will automatically scroll upon touch

In the era before iOS8, when utilizing the Javascript .focus() method on an input element, it seemed as though nothing happened - the virtual keyboard did not make an appearance. However, with the latest iOS 8 update, executing the .focus() method initiall ...

Storing a Vue JS component as a string in a variable and displaying it using that variable

I am currently working on a project where I need to implement the following code snippet: <template v-for="(element, k) in place.data.content"> <data_elements[element.name].component></data_elements[element.name].component> </te ...

You have encountered an issue with the runtime-only build of Vue, which does not include the template compiler

Lately, I have been utilizing Vue in a project and encountered an issue where upon compiling, my browser page displays as white with an error message stating "You are using the runtime-only build of Vue where the template compiler is not available. Either ...

What is the best way to send the current ID for data deletion in React?

Here is my current code snippet: var html = []; for (var i = 0, len = this.props.tables.length; i < len; i++) { var id = this.props.tables[i]._id;//._str; html.push( <div key={id} className="col-xs-6 col-md-3"> ...

Uploading a file using AngularJs

When it comes to uploading an image or file using HTML tag in AngularJS, it seems that the tag is not supported. The solution? Create a custom directive. In my index.html page, I included the following code: <body ng-controller="myController"> ...