Transition from jQuery to Vue.js

The author of the article, the translation of which we publish today, believes that there are many more programmers in the world who, when they need to develop a simple web application, turn to jQuery . Usually this happens when you need to equip a certain page with simple interactive features, but using some kind of JavaScript framework for this seems like an overkill. After all, these are kilobytes of unnecessary code, templates, tools for assembling projects, means for packing modules ... At the same time, connecting to the jQuery page using the CDN resource is a breeze.



This article will discuss how to translate a project created using jQuery to Vue.js. This project will be created on jQuery, and then reworked using Vue. The author of the material wants to show everyone that using Vue, even in relatively small projects, does not necessarily mean an excessive increase in the code size of such projects and a large additional burden on the programmer. This, on the contrary, with almost the same size of the auxiliary code, as when using jQuery, allows you to increase productivity and improve the quality of applications.

Project Overview


We are going to develop a simple electronic invoice based on this open source template from Sparksuite.

Often, such training examples use all sorts of to-do lists. I hope the deviation from this tradition will make the story fresher and more interesting, and, at the same time, our task will be quite difficult for her example to demonstrate the advantages of using something like Vue for developing small projects. The manual is designed in such a way that everyone, without any difficulty, could reproduce it and master the proposed methods of work.

Here is the account template that we want to work with.


Electronic account

We are going to add interactive possibilities to it, making it so that it would be possible to choose a product, its quantity and price per unit. If the values ​​change, the total cost of the goods and the total amount of the document will be recalculated. In addition, we will add here a button that will allow to insert new blank lines into the bill.

I modified the template, bringing the HTML code of the empty string to the following form:

<tr class="item">  <td><input value="" /></td>  <td>$<input type="number" value="0" /></td>  <td><input type="number" value="1" /></td>  <td>$0.00</td> </tr> 

Developing a project on jQuery


First, let's take a look at how to solve our problem using jQuery.

 $('table').on('mouseup keyup', 'input[type=number]', calculateTotals); 

We connect an event listener to the table. This listener will call the function calculateTotals if the values ​​corresponding to the unit price or quantity of the item change:

 function calculateTotals()  { const subtotals = $('.item').map((idx, val)  => calculateSubtotal(val)).get(); const total = subtotals.reduce((a, v)  => a + Number(v), 0); $('.total td:eq(1)').text(formatAsCurrency(total)); } 

The function takes the rows of the table and traverses them, passing each of the rows of the function calculateSubtotal and summing up the results. The result of the calculation falls into the constant total . As a result, the total amount of the document is inserted in the appropriate field of the account.

 function calculateSubtotal(row) { const $row = $(row); const inputs = $row.find('input'); const subtotal = inputs[1].value * inputs[2].value; $row.find('td:last').text(formatAsCurrency(subtotal)); return subtotal; } 

In this code, we take references to all the <input> fields present in the string, then multiply the values ​​stored in the second and third fields to get the value of the subtotal . Then this value is inserted into the last cell of the row. We format this value using the formatAsCurrency function. Here is its code:

 function formatAsCurrency(amount) { return `$${Number(amount).toFixed(2)}`; } 

In addition, we have a small auxiliary function that we use in order to ensure the same appearance of the fields with the amounts for the goods and for the document as a whole. Namely, we need the $ sign in front of the numbers in these fields, and that they are decimal numbers with two decimal places.

 $('.btn-add-row').on('click', () => { const $lastRow = $('.item:last'); const $newRow = $lastRow.clone(); $newRow.find('input').val(''); $newRow.find('td:last').text('$0.00'); $newRow.insertAfter($lastRow); $newRow.find('input:first').focus(); }); 

And finally, we have an event handler for clicking the Add row button, which is used to add a new line to the document. Here we take the last row in the table containing the product data and make a copy of it. In this case, we insert standard data into the fields of this new line. In addition, we can take care of the user experience and set the focus to the first field of the new line, which will allow the user, immediately after adding a new line, to begin entering data into its first field.

Here is a working example of an account, the interactive capabilities of which are implemented by jQuery tools.

Disadvantages of jQuery based solution


Let us ask ourselves what the shortcomings of the above approach are, or rather, how to improve our project.

You may have heard about the new libraries, like Vue and React, which are said to allow you to work in a declarative, not imperative style. If you look at the above jQuery code, it is clear that it is mainly read as a set of instructions describing DOM manipulations. The purpose of each section of this code, that is, the answer to the question of what it does, is often quite difficult to figure out by looking at how it does it. Of course, you can make the intentions of the code fragments used more clearly by breaking it up into functions with thoughtfully chosen names. However, if you leave this code for some time, and then return to it again, it turns out that in order to understand it, you still have to make some effort.

Another problem with this code is that the state of the application is stored in the DOM. Information about the goods that the user wants to order, exists only as part of the HTML markup in the user interface. If all we need is to output the data in one place, then this problem does not seem so serious. However, as soon as it becomes necessary to output this data in several places of the application, we face the problem of data synchronization. Without, as in our case, a single source of reliable data, it is very difficult to keep them up to date in the entire application.

Although using jQuery does not prevent us from storing the state of the application outside the DOM and avoiding the problem described above, libraries such as Vue provide features that contribute to creating applications that have a good architecture. These libraries help the programmer write cleaner and better structured code.

Translation project on Vue


Now let's talk about how to recreate the same functionality on Vue. In order to take advantage of the possibilities of Vue, simply connect this library to a regular web page, just as is the case with jQuery. That is, there is no need to use the system to pack modules or a transpiler, you do not need to split the application into components and decompose their code into .vue files.

Let's start translating the project to Vue by replacing the contents of the <script> :

 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> 

In the next step, create a new instance of Vue:

 const app = new Vue({ el: 'table' }); 

The only parameter that needs to be passed to the constructor of the new Vue instance is el . This is the selector (the same one used in jQuery), which identifies the part of the document that we want to manage with Vue.

Vue can be assigned to tasks of different scale - from managing a whole page (in the case of, for example, with a one-page application) to a small fragment enclosed in a <div> . In our example, Vue will be responsible for working with the HTML table.

Data


Add conditional data to the Vue instance for three lines of the document:

 const app = new Vue({ el: 'table', data: {   items: [     { description: 'Website design', quantity: 1, price: 300 },     { description: 'Hosting (3 months)', quantity: 1, price: 75 },     { description: 'Domain name (1 year)', quantity: 1, price: 10 },   ] } }); 

The data property is where we store the state of the application. The state includes not only the data with which our application should work, but also information about the state of the user interface (for example, which of the sections of the application consisting of several tabs is currently active, or whether some widget is minimized like "accordion", or deployed).

Vue helps to ensure that the state of the application is separated from its presentation (that is, from the DOM), and helps to centrally store the state in one place, which is the only source of reliable data.

Template modification


Now we set up the template so that it displays the data stored in the data object. Since we told the framework that we wanted to work with a table using an instance of Vue, we can use the syntax of the Vue templates in the HTML code describing this table to tell Vue how to visualize the table and how work.

Using the v-for attribute, we can output a block of HTML code for each element from the items array:

 <tr class="item" v-for="item in items"> </tr> 

Vue repeats this markup for each element of the array (or object) that is passed to v-for . This allows us to refer to each element in the loop. In this case, this element is represented by the variable item . As Vue monitors the properties of the data object, the framework will dynamically update the markup as the contents of the items array change. All we have to do is modify the state by adding or removing elements, and Vue will automatically update the user interface.

We also need to add input fields that the user could fill in by entering a description of the product, the price per unit, quantity:

 <td><input v-model="item.description" /></td> <td>$<input type="number" v-model="item.price" /></td> <td><input type="number" v-model="item.quantity" /></td> <td>${{ item.price * item.quantity }}</td> 

Here we use the v-model attribute to configure two-way data binding between input fields and properties in the data model. This means that changing the data in the input fields will change it in the model, and vice versa.

In the last cell, we use curly brackets {{}} , we use them to display text. You can use any working JavaScript expression in brackets. In this case, we multiply the two properties of the element and derive what happened. Again, Vue observes the data model, changing any property will result in automatic recalculation of the expression.

Events and methods


Now we have a template that is ready for the collection of the items and we are faced with the question of how to add new rows to the table. Since Vue will display everything on the page that is in the items array, in order to display an empty string on the page, you only need to pass a Vue object with any values ​​that must be present in the items array.

To create a function that can be accessed from a template, you need to pass this function to the Vue instance as a property of the methods object:

 const app = new Vue({ // ... methods: {   myMethod() {} }, // ... }) 

addRow declare the addRow method, which can be called to add a new element to the items array:

 methods: { addRow() {   this.items.push({ description: '', quantity: 1, price: 0 }); }, }, 

Please note that any methods we create will be automatically tied to a Vue instance, which will give us the opportunity to refer from these methods to the properties of the data object and to other methods like the properties of this .

So now we have a method. How to call it by pressing the Add row button? To add event listeners to controls in a template, Vue uses the v-on:event-name syntax.

 <button class="btn-add-row" @click="addRow">Add row</button> 

Vue also provides an abbreviation for the v-on : construct, which looks like @ . It is used in the above code snippet. You can use any method from a Vue instance as an event handler.

Calculated properties


Now we just have to withdraw the total amount of the document at the bottom of the account. This can be done in the template itself. As already mentioned, Vue allows you to place any working JS-expressions in constructions of braces. However, it is much better to follow the approach where only very simple logic is stored in the template and nothing else. If the logic is separated from the pattern, the code will be cleaner, it will be easier to test.

For this purpose, we can use the usual method, but I believe that in this case, the so-called computed property would be best for us. Working with such properties resembles the above described work with methods. Namely, to create such properties, a computed object is passed to the Vue computed . It contains functions whose execution results we want to use in the template:

 const app = new Vue({ // ... computed: {   total() {     return this.items.reduce((acc, item) => acc + (item.price * item.quantity), 0);   } } }); 

Now you can refer to the calculated property from the template:

 <tr class="total"> <td colspan="3"></td> <td>Total: ${{ total }}</td> 

As you may have noticed, the calculated properties in the template work as if they are data. There is no need to "call" anything in the template. Using computed properties has another advantage. Vue - the system is quite intelligent, it caches the returned values ​​and recalculates them only if the properties change, on which the values ​​of the calculated properties depend.

If we used the method to calculate the total amount of the document, the calculations would be performed each time the template is output. But, since we use a calculated property, the total amount is recalculated only when the quantity or price changes in the rows of the table.

Filters


You may have noticed that in the implementation of our project, created using Vue, there is one small error. It consists in the fact that when the prices of individual goods are expressed in integers, the calculated values ​​of the totals for the lines and the total amount for the document are displayed as whole dollar values, without cents. We would like, as in the example made on jQuery, in these numbers there are always two numbers after the comma.

Instead of modifying the code responsible for calculating the total value of the string, and the code calculating the total amount of the document, we can use the convenient data formatting features of Vue. We are talking about filters.

As you probably already understood, in order to create a filter, it is enough to pass an object ( filters in this case) to the Vue instance with the corresponding key:

 const app = new Vue({ // ... filters: {   currency(value) {     return value.toFixed(2);   } } }); 

Here we have created a very simple filter called currency . It calls the toFixed(2) function for the numeric value passed to it and returns the result obtained. You can use this filter in the template as follows:

 <td>Total: ${{ total | currency }}</td> 

Here is the finished implementation of our project on Vue.

Results


If you compare two versions of the project, one - created using jQuery, the second - written in Vue, you will notice the following strengths of the application based on Vue:


The Vue and jQuery libraries are almost the same size (in kilobytes). Of course, the size of jQuery can be reduced by using the library’s own build, but, even in relatively simple projects, such as our example of an electronic account, I think the simplicity of development and the readability of the code justify some increase in the size of the application.

In addition, the possibilities of Vue are much broader than those we have described here. The strength of this framework is that it allows you to create modular, reusable user interface components from which you can build complex client applications.

If the topic of web application development on Vue is interesting for you, take a look at this material . In addition, if you are considering transferring projects from pure JS to Vue, this is our recent publication on this topic.

Dear readers! Do you use jQuery? And, if you use, do you plan to change this library to something else, for example - to Vue?

Source: https://habr.com/ru/post/414073/


All Articles