A step-by-step guide to creating a connected form input using props in Vue

My goal is to create a reusable form component similar to

<buy-form :price="price" />
(which would set the price as pre-defined and read-only. I aim to have the flexibility to pass any of the three fields' values amount, price, and total

Within the form, there are 3 Vuetify v-text-fields.

BuyForm.vue

<template>
<v-card>
    <v-card-text>
      <crypto-input :value.sync="form.amount" :disabled="!!amount" label="Amount"/>
      <crypto-input :value.sync="form.price" :disabled="!!price" label="Price"/>
      <crypto-input :value.sync="form.total" :disabled="!!total" label="Total"/>
    </v-card-text>
    <v-card-actions>
      <v-spacer />
      <v-btn depressed dark :color="style">BUY</v-btn>
    </v-card-actions>
</v-card>
</template>

<script>
  import styles from '@/styles/_export.scss'
  import CryptoInput from './CryptoInput'

  export default {
    name: 'BuyForm',
    components: { CryptoInput },
    props: {
      label: {
        type: String,
        default: null,
      },
      price: {
        default: null,
      },
      amount: {
        default: null,
      },
      total: {
        default: null,
      }
    },
    data: () => ({
      form: {
        amount: null,
        price: null,
        total: null,
      },
      styles,
    }),
    mounted() {
      Object.keys(this.form).forEach((e) => {
        this.form[e] = this.$props[e]
      })
    },
// ...

Progress has been made. Changes in the inputs reflect in the data.form object.

Now, I want to calculate the remaining field (i.e., if I pass the price, users can input either the amount or the total, and the other one will be calculated automatically). I've set up watchers for each field, but encountered a problem: the watchers fire constantly because when users input the amount, the total changes automatically, causing the total watcher to trigger, which then recalculates and so on. In most cases, this is acceptable, as the value remains the same. However, I've come across some edge cases where it breaks (when clearing one field, for example). I attempted to resolve all these issues with a convoluted mess of watcher logic

watch: {
      'form.amount': function(newVal) {
        if (!this.$props.amount) { // not disabled
          let { amount, price, total } = this.form
          if (this.$props.price) this.form.total = amount * price
          if (this.$props.amount) this.form.price = total / amount
        }
      },
      'form.total': function(newVal) {
        if (!this.$props.total) { // not disabled
          let { amount, price, total } = this.form
          if (this.$props.price) this.form.amount = total / price
          if (this.$props.amount) this.form.price = total / amount
        }

This is just a basic concept and far from being "functional" or even "good".

Looking ahead, dealing with scenarios where none of the fields are disabled gives me headaches because all three fields will depend on each other, triggering their respective watchers upon any changes, and I need to anticipate all of that....

It feels fundamentally flawed. So, my question is: Is there a preferred method to tackle this? Surely, there must be a better way? Am I overlooking something obvious? Maybe there's even a library that could assist in this task? I haven't found one, and I don't want to spend countless hours on this cumbersome process only to end up with a fragile and bloated result.

Thank you for any guidance :)

Answer №1

Since I'm not familiar with how your version of value.sync operates, let's assume you are using a standard v-model (with value prop and input event for changes).

To ensure recalculation only occurs on user input, consider triggering them in on-change listeners instead of using watchers. Here is an example:

<input
  :value="form.amount"
  @input="onAmountChange"
/>
methods: {
  onAmountChange (value) {
    this.form.amount = value;
    ... // recalculation logic
  }
}

You can also take it one step further to tidy up your template by utilizing computed properties:

<input v-model="formAmount"/>
computed: {
  formAmount: {
    get () {
      return this.form.amount
    },
    set (value) {
      this.form.amount = value;
      ... // recalculation logic
    }
  }
}

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 behavior of Daterangepicker varies when using callback functions versus applying the daterangepicker

Encountered an issue with the daterangepicker recently. There is a form that needs to be submitted whenever a user selects a new date range. The problem arises when using the daterangepicker callback - even though the correct values for the newly selected ...

Transforming a menu tab into an active anchor when navigating to a particular section

Hello, I have successfully created a JavaScript code that can dynamically add menu tabs into the menu list window.addEventListener("load", () => { const navList = document.getElementById("nav_list") const fragment ...

A sleek Javascript gallery similar to fancybox

I'm currently working on creating my own custom image gallery, inspired by fancybox. To view the progress so far, please visit: I've encountered some issues with the fade effects of #gallery. Sometimes the background (#gallery) fades out before ...

Attempting to scroll through a webpage and extract data using Python and Selenium in a continuous manner

Recently, I posed a question (you can find it here: Python Web Scraping (Beautiful Soup, Selenium and PhantomJS): Only scraping part of full page) that shed light on an issue I encountered while attempting to scrape all the data from a webpage that updates ...

Deleting the initial line in a JSON reply

My current code is : $.getJSON("https://www.domain.com/someapi/callback=?", function(data){ $.each(data, function(i,item){ alert(item.x); }); }); I am currently facing an issue with my JSON response because there is ...

The module 'AppModule' has unexpectedly declared a value of 'Component' that was not anticipated

Recently, I've been working on transitioning my application to the new angular2 webpack rc5 setup. I have successfully generated the app module using ng cli migration. However, I am facing an issue while trying to integrate a component from my own li ...

The Jquery Object #<Object> does not have the 'getElement' method available

I've been attempting to set up this table, following the instructions here: Despite verifying that my browser is correctly pulling the CSS and .js files, I keep encountering an error related to my sortabletable.js file. (screenshot of the error) htt ...

Ext 4.1 - Accessing a class instance using Ext.define()

In my code, I have a class with a method defined like this: Ext.define('MY.class.Manager', { .... .... .... manageStuff: function(){ } } Initially, the manageStuff function was only needed in one place and everything worke ...

What is the best method for arranging checkboxes in a vertical line alongside a list of items for uniform alignment?

Trying to come up with a solution to include checkboxes for each item in the list, maintaining even vertical alignment. The goal is to have the checkboxes in a straight vertical line rather than a zigzag pattern. Coffee Nestle ...

Shader customization and dynamic window resizing with Three.js

Having trouble with resizing the Three.js shader scene using ThreeX to handle window resizing and changing orientation on mobile devices. When the window width is decreased, everything works perfectly. However, expanding the window either in width or heigh ...

"Utilizing Javascript filtering to narrow down results based on date ranges within a Vue

I'm currently using Vue within a Laravel application. Most of the functionality is working as expected, except for the last part. I've been struggling to come up with the right search terms for this scenario. Apologies if this question has alread ...

The nested array structure within the database is preventing the insertion of an array into the second layer

My database setup for testdata is as follows: test1 [ [0] { test: Array, comments: Array }, [1] { test: Array, comments: Array } ] The userSchema definition includes the following fields: var UserSchema = new Schema({ test1: { type: Array, ...

Reasons Child Components in React Native & Redux Are Not Re-rendered with Every State Update

Within my React Native and Redux setup, I have a <NavigationCardStack/> component serving as the root. Whenever the state is updated, the redux-logger correctly displays the new state. However, after a state change, the child component fails to log ...

What steps do I need to take to integrate FontAwesome with the vue-cli webpack-simple template?

First, I create a new project using vue-cli: vue init webpack-simple grid-prototype Next, I add FontAwesome to my project via npm: npm install --save fontawesome Once installed, I include it in my main.js file with the following code: import 'fon ...

Execute JavaScript using "matches" without the need for the page to refresh

I have been developing a school project which involves creating a Chrome extension to streamline the Checkout process on a website. In my manifest.json file, I specified that a content.js file should run when it matches a specific URL. "content_script ...

Adding a fresh object (key-value pair) to an array in javascript: what you need to know!

My current array is set as : items=[{'id':1},{'id':2},{'id':3},{'id':4}]; Can you advise me on how to include a new pair {'id':5} in the existing array? ...

Navigating an Array Like a Pro

I am currently facing an issue with my array comparison logic as it is not filtering correctly. The goal is to add new users by comparing their ID and StaffID, and if the StaffID does not exist in the current user array, the state of the new user should be ...

Searching for substrings in NodeJS using Express

In this code snippet, I am using Express JS and AES256 encryption to encode and decode simple text strings with a predefined key. The Express JS web server is set up to display specific elements based on the URL content. For instance, if the URL was abc. ...

Fastify route handler failing to start after onRequest hook is executed

I am currently working on a fastify application that needs to capture the raw body of post requests for authentication purposes. After extensive research, I discovered that fastify does not have native support for this feature. The solutions I found online ...

PHP array checkbox problem, ensure the correct boxes remain checked upon submission

I am currently working on a checkbox system that is based on each school in an array. Everything is functioning correctly, except that after submission, only the checkboxes are checked. For example, if I have 10 checkboxes and select box number 4, 5, and ...