Elm vs. Vue

lucamug

lucamug

Posted on March 20, 2020

Elm vs. Vue

Alt Text

Disclaimer: This is a comparison that is far from being exhaustive or unbiased. Similarly to Elm vs. Svelte, it is based on a mini-example taken from a video tutorial.

Is more a comparison of feeling between writing a few lines of codes in these two different frameworks. I didn't know Vue before writing this post, I usually use Elm in my projects.

Let's start with Vue

It is a simple application that send an HTTP request, retrieve a list of products, list them with the stock quantity and let the user either edit the quantity or add 1 to the quantity with a button showing the total of the inventory:

…but

#1 “Total Inventory” doesn’t work

While the “Add 1” buttons work fine, typing number in the input field give wrong result (Demo #1).

Alt Text

For example, let’s type 123... what? Where 01123052 is coming from? I guess someone is trying to concatenate strings instead of adding numbers. Note also that the list of quantities is now [“1123”,0,5,2].

After some research in the Vue documentation I found this:

"If you want user input to be automatically typecast as a number, you can add the number modifier to your v-model managed inputs"

It sounds exactly what we needed. Let’s add it in our script at line 3:

From

<input type="number" v-model="product.quantity">
Enter fullscreen mode Exit fullscreen mode

to

<input type="number" v-model.number="product.quantity">
Enter fullscreen mode Exit fullscreen mode

Fixed!

#2 “Total Inventory” is broken again!

Now typing digits works, but if we delete all digits we are getting again the same problem discussed above (Demo #2).

Alt Text

Ok, maybe we should refactor the function that calculates the total Inventory.

Let’s add a condition at line 20 that, if the quantity is not a number, the value is skipped.

From

totalProducts() {
    return this.products.reduce((sum, product) => {
        return sum + product.quantity
    }, 0)
}
Enter fullscreen mode Exit fullscreen mode

to

totalProducts() {
    return this.products.reduce((sum, product) => {
        if (typeof product.quantity === "number") {
            return sum + product.quantity;
        } else {
            return sum;
        }
    }, 0)
}
Enter fullscreen mode Exit fullscreen mode

Fixed!

#3 The “Add 1” button sometime doesn’t work

The button doesn’t work if pressed after we remove all digits or we type non-numeric characters (Demo #3).

Alt Text

Ok, time to fix this one too. Let’s change the command that add 1 (line 4) from

<button @click="product.quantity += 1">
Enter fullscreen mode Exit fullscreen mode

to

<button @click="product.quantity = Number(product.quantity) + 1">
Enter fullscreen mode Exit fullscreen mode

Fixed!

Vue/Javascript experience

Maybe these issues came from my lack of knowledge of Vue but I was expecting a smoother experience.

Maybe using the magic two-way data binder v-model is not a good practice and we should rather use one-way binding?

Even if the application now works, the array of quantities still contain a mix of strings and numbers, that is a prelude for other issues in the future. For example when this data will be saved permanently somewhere (Demo #4).

Alt Text

Also all the above issues were detected just using the application and not automatically by the development environment. They could have been easily passed undetected and go to production.

Elm experience

This is the code. It consists of two parts, a small html section to load and initialise the page, and the Elm code that will compile to Javascript.

Elm by design forces us to consider all possible cases that the state of code can be in, including the cases that are responsible for the issues above.

The quantities are of type Integer so is not possible to store any string in them.

We need to convert the string that we get from the input field into an integer before saving it. Then we need to convert it back to a string before displaying it in the page.

During the conversion from string to integer things can go wrong, for example when the string doesn’t contain a valid number.

In this case Elm wants to know how we want to deal with such a case.

One of the important part is, as mentioned above, when we convert from string to integer:

{ quantity = Maybe.withDefault product.quantity (String.toInt newQuantity) }
Enter fullscreen mode Exit fullscreen mode

This piece of code says: “If the user typed a valid number, replace the old number with the new one (newQuantity), otherwise keep the old number (product.quantity)”

There is one caveat here: because Elm doesn’t allow any impossible state, it also doesn't allow to delete all the digits from the input field because that would not be a valid number anymore.

This makes it difficult to change the single digit. This could be fixed checking if the string is empty and converting to a 0, for example. But here we would enter into the realm of issues related to the input field of type number.

In any case, the Total Inventory is always correct and the list of quantities always contains numbers, out of the box.

Usually is a good practice to store input field as String in the model and not as Int because String is the "natural" type of values from forms, also when the input text if of type "number".

There are other things that Elm forces us to consider, for example the case when the HTTP request fails, but these go out of the scope of this post so in the code we simple tell Elm to ignore errors:

GotProducts (Err _) ->
    ( products, Cmd.none )
Enter fullscreen mode Exit fullscreen mode

Closing HTML Elements

One thing that I was not doing for a long time was writing HTML and specifically, remembering to close HTML elements. I the last several years I have been using HAML, Jade, Pug and Elm and all of these systems do not require to close elements.

Working on this example reminded me of the good old days when I was opening a <div> and closing a </p> by mistake.

Typos

While several types of typos will generate a runtime error in Vue, there are others that go undetected. For example changing

<input v-model.number="product.quantity">
Enter fullscreen mode Exit fullscreen mode

into

<input v-model.number="product.quanity">
                               ^^^^^^^
Enter fullscreen mode Exit fullscreen mode

does not show any error but the application is broken: all input fields are empty and typing in them nothing happens. The buttons are still working.

Other errors will only show when interacting on the interface but not on loading.

In Elm basically all typos get detected at compile time, unless they happen inside strings. The above typo generate this error at compile time:

Alt Text

Note the Hint section at the bottom.

Conclusion

Most of these issues come from Javascript but I thought Vue would take a more active role in shielding these from developers. Would TypeScript or Flow fix these issues? Leave your comments below.

This article, in a slightly different form, was initially published in Medium.

Thank you for reading!

💖 💪 🙅 🚩
lucamug
lucamug

Posted on March 20, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Elm vs. Vue
vue Elm vs. Vue

March 20, 2020