What's the best way to show floating point numbers in a concise format while also maintaining the ability to perform calculations within this Vue app?

I'm currently developing a compact calculator application using Vue 3 and implementing some custom CSS.

For the most part, everything seems to be functioning correctly, except for when the results are long numbers that extend beyond the display limit.

const calculatorApp = {
  data() {
    return {
      operationsTrack: [],
      operations: [],
      calculation: null,
      currentVal: "",
      isOperatorClicked: false,
      isEquaSignClicked: false
    };
  },
  methods: {
    appendDot() {
      if (this.currentVal.indexOf(".") === -1) {
        this.appendNumber(".");
      }
    },
    appendNumber(clickedVal) {
      if (this.isOperatorClicked) {
        this.currentVal = "";
        this.isOperatorClicked = false;
      }

      if (!this.isEquaSignClicked) {
        this.currentVal = `${this.currentVal}${clickedVal}`;
      } else {
        this.currentVal = clickedVal;
        this.operationsTrack = [];
      }

      this.operationsTrack.push(clickedVal);
    },
    doOperation(operator, operatorLabel) {
      this.isEquaSignClicked = false;
      this.isOperatorClicked = true;
      this.operations.push(this.currentVal);
      this.operations.push(operator);
      this.operationsTrack.push(operatorLabel);
    },
    doCalculation() {
      // push the last value to the operations array
      this.operations.push(this.currentVal);

      let opt = this.operations.map((i) => {
        if (!isNaN(i)) {
          return Number(i);
        }
        return i;
      });

      // Make the calulations string
      this.calculation = opt.join(" ");

      // Compute the calculations
      this.currentVal = eval(this.calculation);

      // Empty the operations array
      this.operations = [];

      // Update equal sign flag
      this.isEquaSignClicked = true;
    },
    clearDisplay() {
      this.currentVal = "";
      this.operations = [];
      this.operationsTrack = [];
      this.calculation = null;
      this.isOperatorClicked = false;
      this.isEquaSignClicked = false;
    }
  },
  computed: {
    operationsTrackDisplay() {
      return this.operationsTrack.join(" ");
    }
  }
};
Vue.createApp(calculatorApp).mount("#myCalculator");
body {
  padding: 0;
  margin: 0;
}

body * {
  box-sizing: border-box;
}

.container {
  min-height: 100vh;
}

.calculator {
  font-family: Montserrat, sans-serif;
  width: 100%;
  max-width: 360px;
  min-width: 300px;
  margin: 10px auto;
  padding: 36px;
  background: #efefef;
  border-radius: 10px;
}

.display {
  position: relative;
  font-weight: bold;
  display: flex;
  flex-direction: column;
  background: #c3c3c3;
  border-radius: 10px;
  margin-bottom: 34px;
  padding: 10px 20px;
}

.result,
.track {
  display: flex;
  justify-content: flex-end;
}

.result {
  margin-top: 3px;
  font-size: 48px;
}

.track {
  position: absolute;
  top: 5px;
  left: 20px;
  right: 20px;
  font-size: 12px;
}

.buttons-container ol {
  list-style-type: none;
  padding: 0;
  margin: 0;
  color: #242424;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  column-gap: 10px;
  row-gap: 10px;
}

.buttons-container li,
.buttons-container .clear {
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 36px;
  height: 60px;
  font-weight: 600;
  background: #e7e7e7;
  border-radius: 7px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  cursor: pointer;
}

.buttons-container li {
  width: 60px;
}

.buttons-container .clear {
  margin-top: 10px;
  text-transform: uppercase;
  font-weight: 600;
}

.buttons-container .orange {
  background: #ff9800;
  color: #fff;
}
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500,600&display=swap" rel="stylesheet">
<script src="https://unpkg.com/vue@next"></script>

<div class="container">
  <div class="calculator" id="myCalculator">
    <div class="display">
      <div class="track">
        <span>{{operationsTrack.length ? operationsTrackDisplay + ' =' : ''}}</span>
      </div>
      <div class="result">
        {{currentVal || 0}}
      </div>
    </div>
    <div class="buttons-container">
      <ol>
        <li @click="appendNumber('7')">7</li>
        <li @click="appendNumber('8')">8</li>
        <li @click="appendNumber('9')">9</li>
        <li @click="doOperation('/', '&divide;')">&divide;</li>
        <li @click="appendNumber('4')">4</li>
        <li @click="appendNumber('5')">5</li>
        <li @click="appendNumber('6')">6</li>
        <li @click="doOperation('*', '&times;')">&times;</li>
        <li @click="appendNumber('1')">1</li>
        <li @click="appendNumber('2')">2</li>
        <li @click="appendNumber('3')">3</li>
        <li @click="doOperation('-', '-')">-</li>
        <li @click="appendNumber('0')">0</li>
        <li @click="appendDot('.')">&middot;</li>
        <li @click="doOperation('+', '+')">+</li>
        <li @click="doCalculation" class="orange">=</li>
      </ol>
      <div @click="clearDisplay" class="clear orange">Clear</div>
    </div>
  </div>
</div>

The Issue at Hand

Encountering problems with displaying lengthy floating point numbers in arithmetic operations, such as 1 5 ÷ 9 resulting in 1.6666666666666667, causing overflow outside of the display area.

Utilizing toFixed() as a fix is not ideal since it converts values into strings:

 <div class="result">
    {{currentVal.toFixed(6) || 0}}
  </div> 

Proposed Solutions for Handling Long Floats Display

Answer №1

To achieve this functionality, you can create a new computed method called result. This method will display the number in one of three formats without modifying the value stored in currentVal:

  1. If the number is 1M or greater, it will be displayed in scientific notation using Number.toExponential()

  2. If the number is an integer, it will be shown with no decimal places when Number.isInteger() returns true

  3. If the number has a finite number of digits after the decimal point, it will be presented as a floating-point number. This occurs when the length of the string representation is less than the length obtained by calling .toFixed()

  4. If none of the above conditions are met, the number will be displayed as a floating-point number with fixed precision obtained from .toFixed()


computed:
  result() {
    const number = +this.currentVal;
    if (number > 999999) { 
      return number.toExponential(4);
    }
    if (Number.isInteger(number)) {
      return this.currentVal;
    }
    const fixed = number.toFixed(6);
    if (fixed.length > this.currentVal.length) {
      return this.currentVal;
    }
    return fixed; 
  }

This approach allows for displaying the number in the desired format.

Answer №2

By implementing this modification in your calculation function, the number will remain unchanged

this.currentVal = parseFloat(eval(this.calculation).toFixed(6));

const calculatorApp = {
  data() {
    return {
      operationsTrack: [],
      operations: [],
      calculation: null,
      currentVal: "",
      isOperatorClicked: false,
      isEquaSignClicked: false
    };
  },
  methods: {
    appendDot() {
      if (this.currentVal.indexOf(".") === -1) {
        this.appendNumber(".");
      }
    },
    appendNumber(clickedVal) {
      if (this.isOperatorClicked) {
        this.currentVal = "";
        this.isOperatorClicked = false;
      }

      if (!this.isEquaSignClicked) {
        this.currentVal = `${this.currentVal}${clickedVal}`;
      } else {
        this.currentVal = clickedVal;
        this.operationsTrack = [];
      }

      this.operationsTrack.push(clickedVal);
    },
    doOperation(operator, operatorLabel) {
      this.isEquaSignClicked = false;
      this.isOperatorClicked = true;
      this.operations.push(this.currentVal);
      this.operations.push(operator);
      this.operationsTrack.push(operatorLabel);
    },
    doCalculation() {
      // push the last value to the operations array
      this.operations.push(this.currentVal);

      let opt = this.operations.map((i) => {
        if (!isNaN(i)) {
          return Number(i);
        }
        return i;
      });

      // Make the calulations string
      this.calculation = opt.join(" ");
      // Compute the calculations
      this.currentVal = parseFloat(eval(this.calculation).toFixed(6));
      // Empty the operations array
      this.operations = [];
      // Update equal sign flag
      this.isEquaSignClicked = true;
    },
    clearDisplay() {
      this.currentVal = "";
      this.operations = [];
      this.operationsTrack = [];
      this.calculation = null;
      this.isOperatorClicked = false;
      this.isEquaSignClicked = false;
    }
  },
  computed: {
    operationsTrackDisplay() {
      return this.operationsTrack.join(" ");
    }
  }
};
Vue.createApp(calculatorApp).mount("#myCalculator");
body {
  padding: 0;
  margin: 0;
}

body * {
  box-sizing: border-box;
}

.container {
  min-height: 100vh;
}

.calculator {
  font-family: Montserrat, sans-serif;
  width: 100%;
  max-width: 360px;
  min-width: 300px;
  margin: 10px auto;
  padding: 36px;
  background: #efefef;
  border-radius: 10px;
}

.display {
  position: relative;
  font-weight: bold;
  display: flex;
  flex-direction: column;
  background: #c3c3c3;
  border-radius: 10px;
  margin-bottom: 34px;
  padding: 10px 20px;
}

.result,
.track {
  display: flex;
  justify-content: flex-end;
}

.result {
  margin-top: 3px;
  font-size: 48px;
}

.track {
  position: absolute;
  top: 5px;
  left: 20px;
  right: 20px;
  font-size: 12px;
}

.buttons-container ol {
  list-style-type: none;
  padding: 0;
  margin: 0;
  color: #242424;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  column-gap: 10px;
  row-gap: 10px;
}

.buttons-container li,
.buttons-container .clear {
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 36px;
  height: 60px;
  font-weight: 600;
  background: #e7e7e7;
  border-radius: 7px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  cursor: pointer;
}

.buttons-container li {
  width: 60px;
}

.buttons-container .clear {
  margin-top: 10px;
  text-transform: uppercase;
  font-weight: 600;
}

.buttons-container .orange {
  background: #ff9800;
  color: #fff;
}
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500,600&display=swap" rel="stylesheet">
<script src="https://unpkg.com/vue@next"></script>

<div class="container">
  <div class="calculator" id="myCalculator">
    <div class="display">
      <div class="track">
        <span>{{operationsTrack.length ? operationsTrackDisplay + ' =' : ''}}</span>
      </div>
      <div class="result">
        {{currentVal || 0}}
      </div>
    </div>
    <div class="buttons-container">
      <ol>
        <li @click="appendNumber('7')">7</li>
        <li @click="appendNumber('8')">8</li>
        <li @click="appendNumber('9')">9</li>
        <li @click="doOperation('/', '&divide;')">&divide;</li>
        <li @click="appendNumber('4')">4</li>
        <li @click="appendNumber('5')">5</li>
        <li @click="appendNumber('6')">6</li>
        <li @click="doOperation('*', '&times;')">&times;</li>
        <li @click="appendNumber('1')">1</li>
        <li @click="appendNumber('2')">2</li>
        <li @click="appendNumber('3')">3</li>
        <li @click="doOperation('-', '-')">-</li>
        <li @click="appendNumber('0')">0</li>
        <li @click="appendDot('.')">&middot;</li>
        <li @click="doOperation('+', '+')">+</li>
        <li @click="doCalculation" class="orange">=</li>
      </ol>
      <div @click="clearDisplay" class="clear orange">Clear</div>
    </div>
  </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

Encounter an issue while using CreateAsyncThunk due to a 400 Bad Request

I have encountered an issue with my asynchronous thunk. Even when the status code is 400, the request result remains fulfilled. How can I handle a 400 error using createAsyncThunk and the fetch API? This is the action code: import { createSlice, creat ...

Using PHP to enhance a select statement to return data when paired with JavaScript

How can I post JavaScript with PHP to enable select statement return? I have a script that is currently functioning as follows: $.post('2.php', { id: 12345 }, function(data) { // Increment vote count, etc }); This is the content of my 2.ph ...

Sending a value to a text box using json data transmission

I am having trouble accessing data from a js file and fetching the value to an html text box. Despite trying, I'm unable to get the desired result. Below are the contents of samle.js file and jsonhtml.html file: { "var1": "1", "var2": "2" } <scri ...

What steps should be taken to avoid an event from occurring when an error message is encountered?

I have a dropdown list of cars where an error message is displayed if any of them becomes inactive. When the error message is shown, clicking on the Route Car button should prevent any event from occurring, i.e., no modal popup should be displayed. How ca ...

How do I ensure my object is fully constructed before sending it in the response using NodeJS Express?

Currently, I am in the process of constructing a result_arr made up of location objects to be sent as a response. However, my dilemma lies in figuring out how to send the response only after the entire array has been fully constructed. As it stands, the re ...

How can one execute a function within an HTML attribute value using Angular?

I am currently attempting to use the weatherToFontAwesomeIcon(weatherDescription: string) function directly within the HTML file for this component. My goal is to showcase a specific FontAwesome icon based on the weather response from the API. import { Cur ...

What's causing the "Uncaught SyntaxError: Unexpected token u" error to consistently pop up in Chrome?

I've been trying to figure this out all day by searching online, but I'm still stuck. My code is quite long and runs fine in Firefox, but Chrome throws an error: "Uncaught SyntaxError: Unexpected token u". Could someone please point out where I ...

What is the best method for retrieving key-value pairs from an object based on a specific string filter?

const obj = { "pi_diagram": null, "painting": null, "heat_treatment": null, "welding_procedure": null, "inspection_test": null, "pipecl_hadoop": null, "pipecl": null, "ludo_min_hado ...

Utilize a global variable in Vue.js

How can I efficiently pass a javascript variable globally to Vue components upon instantiation so that each registered component has it as a property or it can be accessed globally? Please note: I need to ensure that this global variable for vuejs is set ...

What is the best way to send an array and file in the same AJAX request?

When attempting to send both an image file and an array through my AJAX request to a PHP script, I encountered an issue where either the array or the image file doesn't appear. The problem seems to stem from the specific lines that need to be added to ...

Struggling to incorporate pagination with axios in my code

As a newcomer to the world of REACT, I am currently delving into the realm of implementing pagination in my React project using axios. The API that I am utilizing (swapi.dev) boasts a total of 87 characters. Upon submitting a GET request with , only 10 cha ...

Design a styled rectangular tab using CSS

Does anyone know of any cool CSS techniques for achieving the tabbed rectangle appearance depicted in the image below? While it's clear that accomplishing this with just one div is not possible, is there a more efficient method than layering one div ...

Can general principles be applied to determine which script is the most efficient and will load the quickest?

Is there a way to determine which script is optimal and will load faster when there are multiple ways to write scripts that achieve the same outcome? ...

Overlap one element entirely with another

Currently, I am working on a way for an element called overlayElement to completely cover another element known as originalElement. The overlayElement is an iFrame, but that detail may not be significant in this scenario. My goal is to ensure that the over ...

Tips for ending a page prematurely

I am currently facing an issue with hosting images on my webpage where the bottom of the images are uneven, creating a messy look at the bottom of the page. https://i.sstatic.net/N3XvJ.png Normally I would attempt to align them in a perfect square layout ...

Tips for creating basic Jasmine and Karma tests for an Angular application to add an object to an array of objects

I have developed a basic Angular project for managing employee data and I'm looking to test the addProduct function. Can someone guide me on how to write a test case for this scenario? I am not using a service, just a simple push operation. Any assist ...

Node JS confirmation dialog box

I need to incorporate a confirmation message in my application that will execute the action if the user clicks submit, but cancel the event if they don't. I have set up a route in Express for this purpose, however I want to prevent the backend code f ...

Strategies for streamlining repetitive code within a closure in Angularjs

We are currently utilizing Angularjs 1x and I am in the process of refactoring some repetitive code within an Angularjs filter. However, I am facing challenges in getting it to function correctly. It should be a straightforward task. Our standard approach ...

Combining multiple rows into one using either mysql or lodash was achieved by Node

Currently in my Javascript framework, I am utilizing the Node MySQL library for executing MySQL queries. I encountered an issue with a left join that resulted in multiple rows being returned. This is because the left join generates duplicate rows with diff ...

The slider causing conflicts with widgets and content positioned below it in an HTML layout

When implementing a slider with a fade effect, there seems to be an issue with the placement of widgets on the homepage. The problem arises from the margin-top setting as when images change in the slider, the widgets clash with the slider. During the trans ...