Part 8: Fix bugs with number-to-string coercion [Beginner Tutorial: Learn Vue, CSS Grid and Flexbox]

Robert Mion
5 min readApr 15, 2018

Before you continue, make sure you see this in Codepen

HTML you should see after completing Part 7
JS you should see before proceeding with this tutorial

What we will accomplish

By the end of Part 8, our calculator should be fully functional.

The problem I’ve been avoiding until now

In any digital calculator, the expected behavior is as follows:

  • Initially, the number you see is 0
  • After entering a single number, the 0 is replaced by that number
  • Entering subsequent numbers builds up a long string of number characters that you entered before applying any of the operators (+, -, *, /)
  • Applying an operator should capture a number, not a string, to calculate the solution to a numerical equation

All this is to say, we have to manually convert the value stored in our data model's current property between a number and a string depending on a few different circumstances.

Let’s start by updating a Vue directive in our template

Step 1: Use a ternary operator to conditionally set a value in our data model

  • Find the button element whose text content is the interpolation of the aliased property digit.

The expression used in its v-on directive currently assigns digit to current. This is no longer smart enough. We instead want to update current to one of two possible values, based on a specific condition. Usually, we would use normal control flow via if() ... else to accomplish this. However, in a Vue directive, that syntax is not valid. Instead, we can and must use JavaScript’s ternary operator. The syntax is as follows:

condition ? true : false

Here’s an example:

1 + 1 == 2 ? 'Yes' : 'No'

If 1 + 1 is equal to 2 then Yes is returned. Otherwise, No is returned.

The answer is a bit difficult to describe in words, so here it is:

current === 0 ? current = String(digit) : current += digit

What is this doing?

Each time any of the buttons labeled 0–9 is clicked:

  • It asks: ‘is the value stored in current right now exactly equal to 0 ?
  • If it is: re-assign the value stored in current to the string-representation of the value referenced by digit (which is the same as the label of the button). At this point, current is of type string, not number .
  • If it is not exactly equal to 0: re-assign current to the result of concatenating the value referenced by digit with the value currently stored in current .

For example:

  • When current is 0 , pressing 7 will change current to '7'
  • When current is '7' , pressing 4 will change current to '74'
  • When current is '74' , pressing 8 will change current to '748'

New problem

As you see in the example, as soon as you press a button to begin an equation, current is no longer a number but rather a string . Well, back in our data model, the computed function named answer is currently configured to expect numbers to be stored in this.total and this.current . If any of them is a string then answer will return NaN. That’s no good.

Step 2: Coerce a value from string to number before operating on it

  • In the JS pane, find the line inside answer — the computed function — that returns the result of calling eval()
  • We need to make one small but significant edit: amend the third part of eval ‘s argument so that instead of concatenating this.current in its current form (a string), we concatenate a number. The way to do this is by passing this.current as the sole argument to the native JavaScript function, Number() .

Your code should now look like this

answer: function() {  return eval(this.total + this.selected + Number(this.current));}

Step 3: Update the ‘%’ button’s Vue directive to correctly operate on a value of type number

  • Back in your template in the HTML pane, in the section with a class of modifiers , draw your attention to the third button — the one whose label is a percent sign %

The way it is now, we are performing a math operation (multiplying by 0.01) on what could be a value of type string . Let’s fix this to guarantee we only ever operate on a number .

  • Instead of multiplying current by itself, multiply it by the Number() coerced version of itself — by passing current as an argument to Number()

The result should look familiar now:

current = Number(current) * 0.01

Step 4: Use another ternary operator to correctly identify when to apply a decimal point

The problem

We should be able to insert a single decimal point into the current working number. But only one. And whenever we want.

  • Find the last button in your template: it is in the last section , is the second of two buttons, and its text content is a single decimal point

We’re about to write another seemingly long ternary operation . Similar to earlier, it’s probably smarter if I write it below and walk you through what it does rather than try to describe it and hope you type it correctly. Here it is:

current.toString().indexOf('.') == -1 ? current += '.' : null

To explain

The part before the ?

  • In a very complicated way, it asks: ‘Does current have a . in it?’

The way it asks this is as follows:

  • Look-up the value stored in current
  • Coerce that value to a string using JavaScript’s toString() function
  • Search the string for the existence of a decimal point . and return the index any such character is found at using JavaScript’s indexOf() function
  • indexOf() will return one of two values: a positive number, or -1 . If it returns a positive number, it means that the string passed in as an argument was found — specifically at the index of that positive number. If indexOf() was unable to find the passed-in value in the searched string, it will return -1 .
  • We set our condition to specifically check whether indexOf() returns -1 .

The part between the ? and the :

  • If indexOf() does return -1 : current does not yet contain a decimal point, so we are safe to add one by executing the code: current += '.'

The part after the :

  • If indexOf() returns anything other than -1 : current already contains a decimal point. We should not add another one. In fact, we should do nothing. That’s why I just return null .
current.toString().indexOf('.') == -1 ? current += '.' : null

I re-pasted the code from above in case you are reading the last few bullets and don’t want to keep scrolling back up.

Try it out…it all works!

Play around, please. I hope all your hard work feels great.

If you think your application is broken on account of things not working as described above, click through to Part 9 to see a screenshot of my Codepen up to this point.

This marks the end of Part 8

In Part 9, we will make a few modifications to the template before proceeding to style our application.

Continue to Part 9: Clean-up HTML and improve UX

--

--