The daily routine of a typical programmer consists of monotonous keystrokes and coffee breaks. A good programmer can reach the desired result with fewer keystrokes. And I don't mean the "Ctrl+C / Ctrl+V" combination, as you might think :) In this article, I want to tell you how to save precious time and nerves if you need to create a beautiful and functional form.
There are many libraries that provide a large number of features for working with forms. But today we will consider only one of the most convenient and multifunctional tools for building app interfaces of any complexity. It is about the Webix library and its capabilities.
To prove that these are not empty words, we will create a small app for bookselling and see how to implement an order form using Webix components.
You can find the full source code and live demo here.
Webix and Its Capabilities
Webix UI is a JavaScript library that allows you to create a responsive design without worrying about the app's performance. The range of possibilities is represented by UI components of varying complexity. It can be a simple button and a complex solution. Each widget includes a whole set of methods and properties for flexible configuring and controlling. Besides, the library has extra tools. For example, event handling, methods of working with data, interaction with the server, styling themes and much more. You can find out more information about capabilities in the documentation. Now it is time to move on to the practice.
Main Preparations
In order to use the Webix library, we need to include it in the main index.html file. It is worth mentioning here that the library is distributed in 2 versions: the basic GPL and extended Pro version. The GPL version is free and provides a wide set of features that cover 97.26% of our day-to-day needs. But for working with forms, which are the main topic of this article, we will need some special features of the advanced version. I will mention them in the process. Based on this, we will use the extended Pro trial version to get the most impact.
To get the trial version of the library, we need to visit the download page, enter the necessary data and get a link for downloading the coveted archive.
The archive includes the webix/codebase/ folder with two sacred files, namely: webix.js and webix.css. In order for the Webix magic to start working, we need to move this folder into the root of our project and include the library source files in the index.html file:
It's worth noting that these files are also available via the CDN at the following links:
We'll use local files, as they work faster and do not need Internet access (sometimes it happens). The index.html file looks like this:
We have created a basic index.html file and included all the necessary tools. Let's start with making an overview of our app.
Application Overview
We have a small bookselling app. It consists of a toolbar and two replaceable modules, namely:
- module with a catalog of books
- module with a book description and order form.
The module with the catalog consists of small blocks, 2 items in one row. Each block contains a short description of the product and button to order. It is worth clarifying here that we use the dataview widget for creating this module. In the browser, we will see the following result:
When clicking on the "Buy now" button, the user will see the module with a detailed description of the product. For its creation we use the template widget. Next to the product description we will place the module with our form, which this article is devoted to. In the browser, we will see the following result:
And now our task is to add a module with a form, where the user can enter all the necessary data for ordering. As you may know, the purpose of any form is collecting information from users. In our case, we are going to collect the following data:
- information about the order and its delivery
- information about the user
- information about the payment.
Each form should contain input fields and various controls, namely: checkboxes, radiobuttons, selectors, buttons and much more. Considering the data we need to collect, our form will be large enough.
If we don't want to scare off the user with a large number of fields and controls, let's divide the form into semantic groups mentioned above. Each group will deal with each particular order step. Group will be placed in a separate tab and the user will be able to navigate back and forth through them. There will be 3 tabs:
- the first one includes an order form with the possibility to choose between the pick-up and delivery options. If the user chooses delivery, the app will display some extra fields for entering the desired delivery address
- the second one includes a form for user's personal data
- the last tab includes a form to collect information about the user's credit card.
The interface of each form will be created in a separate file and saved to a variable. These variables will be used for building the layout in the index.html file.
Layout for Module with Forms
In this article, we will not dive into the details of building the entire app layout. If you would like to study this topic more deeply, read the Creating a Booking App with Webix UI article. You can also refer to the layout documentation. There you will find an exhaustive description of all properties and methods with corresponding samples.
Here we are interested in only the part of the layout containing the tabs with forms. These tabs will be switched by the user dynamically. For such cases, Webix provides a special multiview component. The needed modules (in our case it is the forms) have to be placed in the array of the cells property. When initial loading, the first element of this array will be displayed. To navigate between modules, we need to set them a unique ID. The code of layout looks like this:
We have created the layout with forms. Now let's get down to the forms directly. And we start with the order form.
Order Form
The order form will consist of two logical parts. The first part will contain the fields for information about the order itself, and the second one - about its delivery.
Initially, the form will be displayed only with the "Order Information" section:
The second one "Delivery Information" section will be hidden by default. We will show it only if the user selects the corresponding option of the radio control. The order form with 2 sections will look like this:
To navigate through the tabs with forms, we will use the "Back" and "Next" buttons. The former button returns us to the catalog of goods, and the latter displays the next form.
Now let's take a look at how to implement all of this in code.
To collect data from users, Webix provides a special form widget and a number of related controls. The form organisation is similar to the layout component. We can divide form into rows and columns, putting the necessary controls into the corresponding containers.
We create a form in the order_form.js file and save it to the order_form variable:
Here we also need to define the form layout mentioned above. For doing this, the widget has dedicated cols, rows and elements properties. The first two properties allow us to divide the component interface into columns and rows. The elements property we are going to use, allows us to place the form elements as rows. All we need to do is to place two sections with the desired fields into the array of this property. Now let's tackle these sections.
The "Order Information" Section
To group the form fields into sections, we use the fieldset component. The first section is "Order Information". It combines several inputs for collecting the order data. The name of this section is defined via the label property. The desired controls will be described in the object of the body property.
All the fields within this section will be arranged in rows. For this, we need to put them inside the array of the rows property. Now let's create these fields.
Special Field for Order Name
Let's start with the field that contains the order name. It doesn't need any data entry, since its value is set dynamically. To create this field, we can use 2 label components and arrange them as columns. The first component will contain the field name, and the second one will contain the name of the selected item (in our case it is the book title).
Now the section looks like this:
For the second label we need to set the name property to the order_name value. The unique name is required to access corresponding form elements. The reason is that the value of this control will be set and read dynamically via the form API. We also need to specify the unique names for other fields, the values of which we are going to set or read.
Control for Goods Amount Setting
In this section we need to give the user an ability to indicate the desired number of goods. To do this, let's use the special counter tool and specify its label and unique name. We can also define the min and max values via the dedicated min and max properties.
Now the section looks like this:
Input Field for Gift Card
Now let's create a field where the user can enter a gift card number. It is best to use the text control here. Besides the name and label, we can set a placeholder via the placeholder property and a special icon on the right part of the field via the clear property. The icon will appear when the user enters data in the field. When he clicks on this icon, the entered data will be deleted and the icon will disappear.
Now the section looks like this:
Special Field for Order Price
Next, we have a field for displaying the order price. The price, as well as the order name, is set dynamically via the form's API and changes depending on the selected parameters. Here we do the same as with the order name field: use 2 label components, place them as columns and set the name to the second one through its name property.
Now the section looks like this:
Control for Order Receiving Method
In this section, we have to implement the field where the user can select the receiving order method: pick-up or delivery. We can do this via the radio control with two options. The label of this control will be empty, because the radio button names are informative enough. The options data for the radio buttons are set via the options property.
Now let's make it so that if the user switches between the "Delivery" and "Pick-up" options, the "Delivery Information" section will be displayed or hidden correspondingly. Let me remind you that the "Pick-up" option is selected by default, and the section itself is hidden.
To handle the desired event, we need to specify the special on property into the constructor of the radio control. In the object of this property, we also need to specify the required event (in our case, it is onChange) and set to it the desired handler.
First, we get the current value of the radio control via its getValue()
method. If the value is 1 (the "pick-up" option is selected), we will hide the "Delivery Information" section via its hide()
method.
If the value is 2 (the "delivery" option is selected), we will display the "Delivery Information" section via its show()
method.
The whole section looks like this:
The "Delivery Information" Section
Now let's move on to the "Delivery Information" section. It combines the fields for entering the delivery address. We create it in the same way as the previous "Order Information" section. As mentioned above, this section is hidden by default. To do this, we need to set its hidden property to true.
To ship the order, we need to get a delivery address. For this, we will create special fields in which the user can enter the following parameters:
- country
- address within the country:
- region
- city
- street and house
- zip code
- desired shipping date
- additional instructions.
Besides, we will provide the user with the ability to insure the delivery. And as a nice bonus, the user will be able to choose the desired packaging color, which I personally attach great importance to :) Now let's start implementing our plan.
Control for Country Selection
Let's make it so that the user doesn’t enter the country name but selects it from the drop-down list. For creating this field we can use the combo tool of the Webix library. In addition to the standard set of properties ( label, name, clear and placeholder ), we need to parse data for the drop-down list options. This is done via the options property. The data can be set either as an array with data (if they are on the client) or as the path to the data on the server. The data schema should be as follows:
[
{ "id":1, "value":"Canada" },
{ "id":2, "value":"United Kingdom" },
...
]
The library allows us to make the fields required for filling. To do this, we need to set the required property to true. As a result, we will see a special red marker at the top right corner of the label. Looking ahead, if the user leaves such a field empty and runs validation, it will turn in red and the data will not be sent.
Now the section looks like this:
Input Fields for Delivery Address
All the fields for addresses within the country will contain the same settings. To create them, we use the well-known text control and a set of basic properties: label, name, clear, placeholder and required.
Now the section looks like this:
Control for Shipping Date Selection
We can ask the user about the desired shipping date of the order. And here, instead of the usual input field, Webix provides us with a special datepicker widget. When the user clicks on it, the component will display a compact calendar, in which he can select the required shipping date. In addition to the basic properties mentioned above, we can specify the format for displaying the date via the format property. To read more about all capabilities of this widget, visit its documentation page.
Now the section looks like this:
Field for Additional Instructions
It is difficult to foresee the fields for each specific situation. Sometimes it happens that the user wants to specify the additional delivery instructions. For example, he wants the order to be left on the porch of the house or packed in additional packaging. Let's give the user such ability in our form.
The textarea component is the best decision for such an issue. If you have worked with plain HTML forms, you should be familiar with this element. In addition to the standard settings of this widget, we will set a fixed height via its height property.
Now the section looks like this:
Control for Applying Insurance
Let's provide our users with the ability to insure order delivery.
To apply for insurance, we will use the switch control. It is a toggle button that allows the user to switch between two opposite values (0 and 1). Besides the label and name, we can specify 2 inside labels for active and passive states. This can be done via its onLabel and offLabel properties.
Control for Сolor Selection
As mentioned above, we will give users the ability to choose the packaging color. For this, the Webix library provides several widgets, namely: colorpicker, colorselect and colorboard.
Since we only have 4 types of color packages, the best option would be the colorboard widget with a limited palette.
We can specify the palette colors via the palette property and the default color via the value property. Let's also set fixed dimensions for each palette box via the corresponding width and height properties, and remove the border around the component using its borderless property.
The whole section looks like this:
Buttons for Navigation
For the order form we have to implement one more important part. It is about buttons for switching to the next form and back to the trade list. For creating these buttons we will use the button component.
To apply built-in styling, we need to set the css property to the corresponding webix class. In our case, it is about the webix_secondary and webix_primary classes.
The button name is set via its label property. For completeness, it is worth noting that along with the label of each button, we will place a small arrow to indicate the button's purpose. The left arrow for going back (in this case, to the goods list), and the right one for going forward to the next form. To define built-in webix icons, we need to use dedicated css classes.
Both these buttons will be displayed as columns. For this, we need to put them into the array of the cols property.
The buttons look like this:
The order form interface is ready. Now we need to include the order_form.js file into index.html and put the order_form variable into the layout code. In the browser, we will see the following result:
User Form
After the user has entered the necessary information about the order, we need to get his personal data. It is required for identifying the user when he comes to pick up his order or receives it through the delivery service.
Let's create a separate form with fields for entering the following personal data:
- first and last name
- age
- gender
- contact information:
- phone number.
The user form will look like this:
Let's see how to implement this in the code.
We create a form in the user_form.js file and save it to the user_form variable. After this we define the "User Information" section with the fields mentioned above.
Input Fields for First and Last Name
Now let's move on to the fields in which the user can enter its first and last name. To create them, we can use the well-known text control. As in the previous form, for these controls we also specify a standard set of properties, namely: label, name, placeholder, clear and required.
Now the section looks like this:
Controls for Age and Gender
Next, we have 2 optional fields. It is about a counter with which the user can indicate his age and radio buttons for choosing a gender.
Counter
To get the user age, we can use the counter control. We have already used it to define the number of goods.
The age of our customer should be in the range from 18 to 100. These parameters can be set via the min and max properties.
Radio
To get the user gender, we can use the radio control. We have also used it to select the method of the order receiving. The widget will consist of 2 options, namely: Male and Female.
Now the section looks like this:
Input Fields for Contact Information
To contact the user for clarifying any additional information and notifying about the order readiness or its dispatch, we need to get its contact information.
Let's create 2 fields in which the user can enter his email and phone number. For this, we use the text control and specify the set of standard properties (label, name, placeholder, clear and required).
A special emphasis should be placed on the phone number field. Here we can apply a special mask for entering a phone number. To do this we need to set the pattern property to the built-in webix.patterns.phone
value. It is worth mentioning here that this is one of those features that are available only in the PRO version. In this article, you can find more information about the input field formatting.
The mask allows users to enter only 11 characters of the phone number, adds a "+" sign at the beginning of the number and encloses the telephone operator code in parentheses. Other characters will be ignored.
Let's give a little hint for filling this field and add a short message at the bottom of the input. For this, we can use the bottomLabel property.
The whole section looks like this:
For this form, we also need to add navigation buttons. Here we don't have to bother too much and just copy the similar code from the previous form. The differences will only be visible when we make these buttons workable.
The user form interface is ready. Now we just need to include the user_form.js file into index.html and put the user_form variable into layout code. In the browser, we will see the following result:
Payment form
After the user has entered personal data, we need to get his credit card information for paying the order through a special service.
Let's create a separate form for payment information and collect the following data:
- card number
- cardholder's name
- expiration date
- CVV code.
The payment form will look like this:
We create a form in the payment_form.js file and save it to the payment_form variable. After this, we define the "Payment Information" section with the fields mentioned above.
The first field is intended for the card number. Besides the standard properties of the text control (label, name, placeholder, clear and required), we also specify a built-in mask for entering the card number: pattern: webix.patterns.card
. It means that the user can enter only 16 digits indicated on the front of his card. Other characters will be ignored. I would like to remind you that masks are only available in the Pro version.
Next, we need to create a field for entering the name of the cardholder. The text control of this field will also include the standard set of properties mentioned above.
The controls for selecting card expiration date deserve more attention. Here we can use 2 richselect components. When the user clicks on it, the widget will display a drop-down list with options. The data for these options are set via the options property.
Now we move on to the field for entering the CVV code. Here the user needs to enter 3 secret digits indicated on the back of the card. The text control of this field will also include a standard set of properties. Besides, we can set the type property to the password
value, to define the type of the field. It will display asterisks instead of the entered data. We will also specify a custom mask via the pattern property. So now the user can enter only 3 digits of the CVV code.
Let's add some control for saving the entered data in the browser settings. If the user enters data and checks this control, he can reuse payment data the next time. For this, we use the checkbox control with the name and labelRight properties. The second property will display the label on the right part of the control
For this form, we also need to add corresponding buttons for going back and making the order. Here we just copy the similar code from the previous form and rename the second button to "Make order".
The payment form interface is ready. Now we just need to include the payment_form.js file into index.html and put the payment_form variable into layout code. In the browser, we will see the following result:
How to Get the App Working
Above we have described the layout and interface of our forms. But this is only half the way. Now let's get them to work. And for this, Webix has all the necessary tools.
The List of Goods
And we will start from the main page, where the list of goods is displayed. Each block on this page consists of a short description and the "Buy now" button.
When the user clicks on this button, we need to:
- show the module with the order form
- fill out the order form with the data of the selected book.
To implement the mentioned above, we need to handle the button click event. This can be done via the corresponding onClick property of the dataview widget. Inside the object of this property, we specify the css class of our button and set a handler to it as a value.
To fill out the order form fields, we can use its setValues()
method and pass the object with the needed data as a parameter.
As you remember, we have set the name for all form fields via the name property. In the data object, we use these names as the keys and set them to the corresponding values.
If we pass only the data object to the method, it will remove all the form values and set new values specified in the object. In this case, the delivery information will also be cleared and the user will need to enter the address again.
To avoid this inconvenience, we can pass the true
value to the method as the second parameter. So now the method will change only the values passed in the object.
After all the data manipulation, we can switch the multiview component to the next module using its show()
method.
How to Calculate the Order Price
Now we need to calculate the order price and update it every time when the user changes the number of goods or applies the insurance control.
To listen to any change in the form fields, we need to use the on property. In the object of this property, we can specify the desired event and its handler.
So now we need to create this handler. And it will look like this:
Here we get an object with the form values via its getValues()
method. It is worth noting that this object will include only the fields value with the name property.
The price variable stores the total goods price. It depends on the initial price (set in the hidden initial_price field) and the goods count.
Next, we need to check the insurance that can be applied in the "Delivery Information" section and will affect the order price. For this, we check the visibility of the "Delivery Information" section using the isVisible()
method. It returns true if the section is visible and false if not.
If the section is visible, we need to check the state of the switch control (if enabled - value 1
, if disabled - value 0
) and generate the final order cost. If the insurance is applied, we will increase the cost by 20%.
When the final order price is ready, we need to update it in the corresponding Price field. To do this, we can use the setValues()
method and update only the field we need.
Now the order price is updated dynamically when the user changes the number of goods and applies the insurance.
How to Navigate Between Forms
In each form of our application, we have created the "Back" and "Next" buttons to navigate between tabs.
The "Back" button will switch the multiview component to the previous module, and the "Next" button to the next one. To get the buttons to work, we need to catch the click events and set the appropriate handlers.
To handle the click event, the button control has a special click property.
In the handler of the "Back" button of the user form, we go back to the trade list via its show()
method.
function goBackCustomHandler(){
$$("trade_list").show(); //go to the trade list
}
In the same handlers of two other forms, we go to the previous module via the back()
method of the multiview widget, which contains the tabs with the forms.
function goBackCustomHandler(){
$$("form_multiview").back(); //go to the previous module
}
In the handler of the "Next" button, we also use the show()
method to display the desired module, included in the multiview component.
function goNextCustomHandler(){
$$("id_of_next_form").show();
}
In this way we will change tabs dynamically.
How to Validate the Forms
When the user clicks on the "Next" button and before switching to the next form, we need to validate the current form. This can be done in the "Next" button handler:
function goNextCustomHandler(){
if($$("id_of_current_form").validate()){
$$("id_of_next_form").show();
}
}
Here we run the validation of the current form via its validate()
method. It will return true if the validation is successful, and false if not. It should be noted here that the method checks only those form fields for which we applied the rules. Webix allows setting these rules for a separate field via its required and validate properties, and for the whole form via its rules property.
I hope you still remember that for the required fields we defined the required property. This property adds a red asterisk to the labels of the required fields. By default, such fields are validated by the built-in webix.rules.isNotEmpty
rule and must be filled. If at least one of these fields is not filled in, the validation method will return an error and the fields will be highlighted in red.
The unvalidated order form will look like this:
If all the required fields of this form are filled in, or the "Delivery Information" section is hidden, the multiview will be switched to the next form.
Besides the required property with the default validation rule, we can apply other built-in rules or define our own. Let's set such rules for checking the email and phone number in the user form. And this can be done in the object of the rules property of the form.
To check the email address, we use the built-in webix.rules.isEmail
rule, and for the phone number we use our own condition. Since the phone number consists of 11 digits, we will return an error if the entered value length is less than the required one.
To make the field validation more informative, let's add a hint that will be displayed if the validation method returns an error. For the required fields, we need to specify the invalidMessage property and set it to the error message.
The unvalidated user form will look like this:
In case the user navigates to the previous form and then comes back, let's clear the validation markers of the current form. To do this, we can use the clearValidation()
method of the form and call it in the "Back" button handler.
How to Make Order
In the last payment form, we have created the "Make order" button instead of the "Next" button. This button runs validation and, if successful, collects data from all three forms, clears the current form and displays the main page with the goods list. The handler code will look like this:
In a real example, the collected data needs to be sent to the server for further processing. But this is the topic of another article.
Conclusion
You can find the full source code and live demo here.
In this article, we have detailed how to create forms using the Webix library, and now we know:
- how to connect the library
- how to describe UI components using JSON syntax
- how to use component methods
- how to set the event handlers.
And most importantly, we have learned how to work with Webix forms, apply various controls and use methods for managing the form (collecting and setting data, validation, etc.).
As you can see, the widget names, their methods and properties are intuitive and easy to use. In fact, this is only a small part of everything that the library can offer. For more detailed information, you can go to the documentation, which contains a detailed description of all the possibilities with examples.
Top comments (2)
This article is well written. But, I will note some formatting stuff:
First of all, please format your code. For example, you made "clearValidation()" bold but that is incorrect, it should be formatted as code using backticks, like
clearValidation()
& likewebix.rules.isEmail
.Second of all, you should specify formating language when adding plain code, as you are using JavaScript, so you should add "javascript" above the three backticks when adding plain code to make the code highlighted. Like:
Better than:
Otherwise everything is great, thanks for the great article.
Thanks for your feedback, Kevin) I will take into account your suggestions