Prepare for a thorough cross-referencing session

In my attempt to create a tool with 3 inputs that are interdependent - "Earn %", "Earn $" and "Own Price".

Initially, the default value for "Earn percentage" is set at "10", making the initial calculation function as intended. Changing this one value automatically adjusts the other two because there are no circular references: https://jsfiddle.net/2m971kur/2/

const app = new Vue({
  el: '#app',
  data: {
        exVAT: 1500,
        retailPrice: 2900,
        earnPercentage: 10
  },
    computed: {
        incVAT() {
            return this.exVAT * 1.25;
        },
        ownPrice() {
            return this.exVAT + (this.exVAT * (this.earnPercentage / 100));
        },
        earnAmount() {
            return this.ownPrice - this.exVAT;
        }
    }
})

However...
When attempting to introduce circular references, my code encounters issues: https://jsfiddle.net/xrwykvg5/

const app = new Vue({
  el: '#app',
  data: {
        exVAT: 1500,
        retailPrice: 2900,
        earnPercentage: 10,
        ownPrice: 0,
        earnAmount: 0
  },
    watch: {
        earnPercentage() {
            this.earnAmount = this.exVAT * (this.earnPercentage / 100);
            this.ownPrice = this.exVAT + this.earnPercentage;
        },
        ownPrice() {
            this.earnAmount = this.ownPrice - this.exVAT;
            this.earnPercentage = 100 / (this.ownPrice / this.exVAT);
        },
        earnAmount() {
            this.ownPrice = this.exVAT + this.earnAmount;
            this.earnPercentage = (this.ownPrice / this.exVAT) * 100;
        }
    }
})

How can I resolve this issue?

While the example was created using Vue.js, showcasing a simple demonstration of the problem, my actual code is in Angular 2.

Answer №1

Trust Roy J's skills to create a clean and concise solution with computed properties.

Admittedly, I took the easy way out. I lacked the courage to fully comprehend your logic and opted for a simple method-based approach:

const app3 = new Vue({
  el: '#app',
  data: {
    incVAT: 0,
    exVAT: 1500,
    retailPrice: 2900,
    earnPercentage: 10,
    ownPrice: 0,
    earnAmount: 0
  },
  methods: {
    changeEarnPercentage(earnPercentage) {
      this.earnPercentage = Number(earnPercentage);
      this.earnAmount = this.exVAT * (this.earnPercentage / 100);
      this.ownPrice = this.exVAT + this.earnPercentage;
    },
    changeOwnPrice(ownPrice) {
      this.ownPrice = Number(ownPrice);
      this.earnAmount = this.ownPrice - this.exVAT;
      this.earnPercentage = 100 / (this.ownPrice / this.exVAT);
    },
    changeEarnAmount(earnAmount) {
      this.earnAmount = Number(earnAmount);
      this.ownPrice = this.exVAT + this.earnAmount;
      this.earnPercentage = (this.ownPrice / this.exVAT) * 100;
    }
  }
})
#app div {
  float: left;
  margin: 1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
  <div>
    Inc. VAT: <br>{{ incVAT }}
  </div>
  <div>
    Ex. VAT: <br>{{ exVAT }}
  </div>
  <div>
    % earned: <br>
    <input type="text" :value="earnPercentage" @input="changeEarnPercentage($event.target.value)" />
  </div>
  <div>
    $ earned: <br><input type="text" :value="earnAmount" @input="changeEarnAmount($event.target.value)" />
  </div>
  <div>
    Own price: <br><input type="text" :value="ownPrice" @input="changeOwnPrice($event.target.value)" />
  </div>
  <div>
    Retail: <br>{{ retailPrice }}
  </div>
</div>

All in all, not too shabby.

It's worth noting that I used some Number() conversions to avoid treating numbers as strings. Additionally, utilizing an <input type="number"> would be a wise choice.

Answer №2

In order to properly use v-model on your computed values, it is essential for them to have setters defined. The set function should essentially be the inverse of the get function to ensure that the input and output align correctly. This issue can be observed in Cobaltway's example where slight changes in Own Price result in significant jumps in % Earned, showcasing inconsistent results due to improper formulation.

this.ownPrice - this.exVAT;

For instance, for the earnAmount, with the get value above, the corresponding set function would be:

this.ownPrice = Number(newValue) + this.exVAT;

Note that a conversion from text to number is required here. By setting up ownPrice similarly (and utilizing v-model.lazy), you can create a structured approach as shown below:

const app3 = new Vue({
  el: '#app',
  data: {
    exVAT: 1500,
    retailPrice: 2900,
    earnPercentage: 10
  },
  computed: {
    incVAT: function() {
      return this.exVAT * 1.25;
    },
    earnAmount: {
      get: function() {
        return this.ownPrice - this.exVAT;
      },
      set: function(newValue) {
        this.ownPrice = Number(newValue) + this.exVAT;
      }
    },
    ownPrice: {
      get: function() {
        return this.exVAT + (this.exVAT * (this.earnPercentage / 100));
      },
      set(newValue) {
        this.earnPercentage = 100 * (Number(newValue) - this.exVAT) / this.exVAT;
      }
    }
  }
});
#app div {
  float: left;
  margin: 1em;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
  <div>
    Inc. VAT:
    <br>{{ incVAT }}
  </div>
  <div>
    Ex. VAT:
    <br>{{ exVAT }}
  </div>
  <div>
    % earned:
    <br>
    <input type="text" v-model.lazy="earnPercentage" />
  </div>
  <div>
    $ earned:
    <br>
    <input type="text" v-model.lazy="earnAmount" />
  </div>
  <div>
    Own price:
    <br>
    <input type="text" v-model.lazy="ownPrice" />
  </div>
  <div>
    Retail:
    <br>{{ retailPrice }}
  </div>
</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

When a Bootstrap row has a set maximum height, it continues to occupy space even when its content overflows

There is a div with colors that includes a Bootstrap row with multiple columns. A fixed max-height has been set, giving the appearance of scrollability. However, the row with columns continues to expand well beyond the footer: https://i.sstatic.net/jf3pGx ...

"Learn how to add up elements in an array based on their unique IDs and generate a new array using

There is an array called data that looks like this: const data = [ {"id": "One", "number": 100}, {"id": "One", "number": 150}, {"id": "One", "number": 200}, ...

Is there a glitch in JavaScript when comparing dates?

What's wrong with this code? function test() { var start = new Date(2012, 3, 31, 19, 0, 0); // Start: 3/31/2012 7:00 PM var end = new Date(2012, 4, 1, 1, 0, 0); // End: 4/01/2012 1:00 AM if (end < start) console.log("oops ...

What is the best way to verify the accuracy of my model when it includes an array property?

One of the challenges I am facing is dealing with a class that has an array property in MVC. public class MyClass { public int Contrato_Id { get; set; } public string Contrato { get; set; } public int Torre_Id { get; set; } public string T ...

Button component in React remains visible until interacted with

https://i.stack.imgur.com/gTKzT.png I'm dealing with a sign out component in my app that requires me to click on it specifically to unselect any part of the application. This is implemented using React Material UI. <MenuItem onClick={e => this ...

Employing global variables in JavaScript files with Vue3

In my Vue3 project, I have defined global variables like this: app.config.globalproperties.$locale = locale A composable function has been created to dynamically retrieve these global variables: import { getCurrentInstance ) from 'vue' export f ...

Top method for showcasing animated images (HTML/CSS/JS)

For my website, I want to create an engaging animation showing a coin being flipped multiple times in the air before landing on a specific side. After the animation finishes, I would like it to transform into a static image of the final result. I've ...

Error Alert: VueRouter has not been properly defined

I am completely new to fullstack development and was recommended to check out this tutorial on creating WedAPI and VueJS. Prior to this, I have only worked with Python, PERL, and C#. The tutorial can be found at: https://www.youtube.com/watch?v=qS833HGKPD8 ...

Centering the scrollIntoView feature on mobile devices is presenting challenges with NextJS applications

Description While navigating on mobile browsers, I'm facing a challenge with keeping an element centered as I scroll due to the browser window minimizing. I've experimented with different solutions such as utilizing the react-scroll library and ...

When attempting to compile at runtime without an instance, the act of creating a function constructor can lead to an

My website is designed to function as a simple quiz game. It retrieves an array of JSON questions using AJAX, and for each question in the array, it displays the question itself along with buttons containing various options that were stored in the question ...

Step by step guide on adding content to a div by clicking a button, even after the page has already loaded

I have scoured the depths of the internet and forums but have yet to find a satisfactory solution to my problem. Hopefully, someone reading this can offer some help in resolving my issue. I manage a website that showcases concerts for various music groups. ...

Effectively encoding and decoding AJAX and JSON objects with PHP scripting

I've successfully set up a basic HTML file with a fixed JSON object. My goal is to transfer this object to a PHP file named text.php, encode it, decode it, display it in the PHP file, and then showcase it back in the HTML file. <!DOCTYPE html> ...

How to retrieve the context of a .js file using jQuery $.ajax without automatically executing upon receipt

Upon fetching a *.js file using $.ajax, the scripts are executed upon receipt! Is there a way to fetch and execute it only when desired? Moreover, is there a method to remove these scripts at will? ...

Issue with CSV download box not showing up on Rails version 2.3.11

I've encountered an issue while trying to export a csv file with some data. I am using ajax call to select rows from the view table (jqGrid) and export them in a csv format. However, after a successful ajax call, the filtered data is displaying as an ...

Manually reloading the page causes issues with AngularJS routing functionality

I've been attempting to manually reload the page from my browser, but unfortunately it's not working and is displaying an error message: Cannot GET /rate/4 This is what my route setup looks like in Angular: angular.module('routing&ap ...

What could have caused the lack of output from the render function?

I've been working on generating my navigation drawer from JSON data and have everything functioning using components. Now, I'm in the process of refactoring to functions for better performance and to enhance my knowledge of React and JavaScript. ...

Can I retrieve a variable within the watch() function in Vue?

Is there a way to access variable asd in the watch() function and clear the interval when it's complete? I've tried the code below but the timer goes into negative values. Are there any possible solutions? methods:{ testing(){ this. ...

How to efficiently send multiple objects in response to a single GET request with Express and Node.js

The code snippet I am working with looks like this - sendServer.get('/download',function(request,response){ var status="SELECT * from poetserver.download where status='0'"; console.log(status) connection.query(status,function(error,ro ...

Is it possible for ng-options to function independently of a controller?

Is it possible to populate a select dropdown in an Angular component using an array of strings as option tags without the use of a controller? How can this be achieved? Within the component, I have defined the following variables: array: String[] = [&apo ...

There seems to be an issue with declaring Gulp Open

Here is the code snippet for my `open.js` task: import gulp from 'gulp'; import gulpOpen from 'gulp-open'; gulp.task('open', () => { // doesn't work with `function()` either. const options = { uri: 'local ...