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

The carousel comes to a halt once it reaches the final slide and does not continue cycling

Currently working on a website project for a client and utilizing Bootstrap to create a carousel feature. I am specifically using Bootstrap 3.0. After searching for a similar issue here, I found two cases that resemble mine but unfortunately have no soluti ...

Why does my anchor disappear after a second when clicked to show the image?

Hi everyone, I'm having an issue with a dropdown menu that I created using ul and anchor tags. When I click on one of the options, an image is supposed to appear. However, the problem is that the image shows up for just a second and then disappears. I ...

The function angular.factory does not exist

Hey there! I am encountering an error in the title related to my factory. Any suggestions on how I can resolve this issue? (function (angular,namespace) { var marketplace = namespace.require('private.marketplace'); angular.factory(&apo ...

Start the vue-cli-service serve command and run cucumber tests one after the other

I am trying to figure out how to run vue-cli-serve, start the development server, and then run my cucumber tests without having to run separate scripts in different terminals. I'm wondering if there is a way to achieve this with just one command. ...

Find a way to avoid Google's site-blocking measures

Currently developing a website. I am looking into restricting access to only logged-in users on the site. How can I parse the pages in a way that Google does not block the site? ...

Having trouble changing the chosen selection in the material UI dropdown menu

I've encountered an issue with my material ui code for a select dropdown. It seems that the selected option is not updating properly within the dropdown. <FormControl variant="outlined" className={classes.formControl}> <InputLabel ref={ ...

Utilizing HTML to call a function and fetching data from AngularJS

I've been struggling to retrieve the value after calling a function in my HTML file. Despite trying various methods after conducting research, I have not achieved success yet. Take a look at the code below: HTML: <div class="form-group"> & ...

Pass data to PHP using AJAX

Is it possible to pass the variable rowNumber to the PHP file dataSource in this code? function getData(dataSource, divID,rowNumber) { if(XMLHttpRequestObject) { var obj = document.getElementById(divID); XMLHttpRequestObject.open("GET", dataSo ...

The problem lies within the `node-fibers` module

After spending a considerable amount of time today, I attempted to deploy my Nuxt project on Vercel but encountered this persistent error: ## Encountering an Issue with `node-fibers` ## `/vercel/path0/node_modules_dev/fibers/bin/linux-x64-93-glibc/fibers.n ...

Displaying a variable in a live HTML user interface

I have successfully created a Python program that captures data from an Arduino Potentiometer and shows it on the Python console. Now, I am working on enhancing the output by displaying it in a local HTML file. I am seeking guidance on how to incorporate t ...

Unable to define the color of icons path using CSS in Vue 3

When using the iconify library for VueJS, the icons' paths automatically have "currentColor" as their fill color. The issue arises when trying to set a path's color via CSS, as it seems to be ineffective. Even with "!important", the color won&apo ...

Adding quotes is optional when using the append function

We are retrieving product options from the displayed html using variables PRODUCT_CUSTOM_1_ and PRODUCT_NAME_. Some products have quotations like " and '. However, when we post these strings elsewhere, they get cut off at the ". We need to either remo ...

Identify if a process operates synchronously or asynchronously

Can you identify in node.js, with the help of a function, if a method is synchronous or asynchronous? I am interested in creating a function that achieves the following: function isSynchronous(methodName) { //check if the method is synchronous, then ...

Create an interactive popup window within a data table using Vuetify to enhance user experience

I currently have a datatable that contains numerous columns. My goal is to only display a select few columns in the datatable, while providing the remaining data in a modal/dialog when the user clicks on the "More" link within the row. Below you will find ...

Issue with verifying file existence utilizing $.ajax()

I'm currently facing a challenge checking for the existence of a file using $.ajax(). I am cycling through a JSON file with $.each and trying to determine if a specific staff member has an image. If not, I want to default to using the no_photo.jpg ima ...

Enhancing the capabilities of a browser detection function in Javascript

Looking to enhance my Javascript browser detection function with input from others. Concerns: The assumption that "Chrome 18" is equivalent to "Maxthon 3" may not be accurate! How can we distinguish between Chrome 18 and Maxthon 3? Similarly, how can we ...

Implementing VueJs Vuetify Autocomplete feature with clickable hyperlinks

I am working with Vuetify and I want to create an autocomplete feature similar to the one on their official site. However, I am encountering some issues: The items value is not appearing in the return list and I am unsure how to display the links. Here i ...

Does the frame take precedence over the button?

const div = document.createElement('div'); // creating a dynamic div element div.setAttribute('id', "layer1"); // setting an id for the div div.className = "top"; // applying a customized CSS class div.style.position = "absolute"; // sp ...

How to access v-model from a separate component in Vue.js

How can I access a v-model that is located in a different component? The search field is within the App.vue file and I am trying to activate the :search='search' property in the Datatable.vue component. Code in App.vue: <v-text-field ...

Cease the audio from playing unless you are actively hovering over the image

Is there a way to make the sound stop playing when you are not actively hovering over the picture? Any suggestions on how to achieve this would be greatly appreciated! imgarr[i].onmouseout=function(){ this.setAttribute('src',this.getAttribu ...