Quantcast
Channel: Webix Blog
Viewing all 246 articles
Browse latest View live

Meet Webix 4.3 with Bullet graph, React integration and new controls

$
0
0

Webix UI library with React integration
The new release of the Webix Javascript library is available for download from now on. Both Webix Standard for open source projects and Webix PRO for commercial use have been updated. Let’s take a look at the main changes.

FormInput UI control

FormInput Javascript UI control
Live Demo >>
The new release contains the FormInput control that allows using any Webix widgets as form controls. Thanks to the FormInput component, you can create rich forms containing tables, fields, drop-down lists, etc. that possess unified look-and-feel.

DoubleList UI control

Doublelist UI JavaScript control
Live Demo >>
New JavaScript DoubleList control provides a more simple alternative for multicombo controls and allows to select multiple values in easy and intuitive way. The new control gives you a wide variety of selection possibilities. You can select separate items or all of them at once, use default or custom buttons, mouse clicks or drag-n-drop. DoubleList allows working with both lists individually using standard Webix API.

Bullet graph UI widget

Bullet chart UI Javascript
Bullet chart is a compact but the most informative way of data presentation. One chart with comparative marker and qualitative ranges performs three functions at once: display, comparison, evaluation.

It is possible to create both vertical and horizontal graphs, choose colors of the graph elements and adjust the animation speed.
Bullet graph UI JavaScript
Live Demo >>
Bullet graph is available in Webix PRO version.

React and other frameworks integration

The main improvement of the release is integration with React UI library. You can expand the functionality of Web applications built on React with 88 Webix UI widgets. An example of integration usage is available here.

Important note: starting from 4.3 release all files that are necessary for the integration with Angular, Backbone frameworks and JQuery library have been transferred from Webix source code to separate repositories on GitHub. If you want to integrate Webix with other framework, you need to download the corresponding package. Links to Github pages are available in our documentation. This change reduces the size of the library.

You can find more information at the 4.3 What’s new page.

Download Webix 4.3

The post Meet Webix 4.3 with Bullet graph, React integration and new controls appeared first on Webix.


Boosting Webix Pivot Performance: HTML5 Web Workers

$
0
0

Performance is a key feature of modern web applications. It is a mountain you have to climb to win your customer’s heart, but sometimes its top remains unreachable due to heavy tasks the application has to perform on the client side.

Take, for instance, Webix Pivot table JS widget. It does a hard job of calculating and aggregating data and may leave the UI unresponsive if the data is too big. Fortunately, from version 4.2 the widget comes supplied with an HTML5 Web Worker, which takes all the data processing into a separate thread.

web workers for high-performant web pages

Why Web Workers?

Javascript single-threaded nature presupposes a step-by-step order of commands’ execution. Like in a supermarket, each new command has to stand in a queue patiently waiting until the preceding tasks are accomplished. If a task takes a long time, other tasks (which may include the ones processing user actions) will not execute until it finishes. At this moment user interface is blocked, which is far from brilliant performance, eh?

HTML5 Web Worker mechanism allows spawning background scripts for such laborious tasks. These scripts, called web workers, run in their own threads, separate from the main execution thread of the application. At the same, time in case of several web workers, their execution threads are separated from each other. Such concurrency means that UI always stays performant, fresh and ready to respond to user actions.

Switching on a web worker for Pivot

Starting from version 4.2 JavaScript framework has added a web worker script for one of its most “hard-working” widgets, Pivot. The script takes all the data aggregation to the background and can be included on demand.

The script code is located in the package in the “codebase” folder. To use it with a widget instance, you need to set a path to it in the the configuration object:

webix.ui({
   view:"pivot",
   webWorker:"../../codebase/pivot.worker.js"
});

From now on, the widget works in the background and is only painted in a browser tab!

Using Web Worker (non-blockin UI)

Live demo >>

Each time users reset the structure of the widget (add filters, arrange columns), the web worker comes into action. You can easily control the moment it starts and ends executing the scripts with the related events: onWebWorkerStart and onWebWorkerEnd.

Within the event handler functions you can add some UI indicators of the operation being handled:

webix.extend($$("pivot"), webix.ProgressBar);
$$("pivot").attachEvent("onWebWorkerStart", function(){
   this.showProgress();
});
$$("pivot").attachEvent("onWebWorkerEnd", function(){
   this.hideProgress();
});

In the code above, the event handlers are responsible for showing and hiding the progress icon respectively.

Save your app from freezing up

Aborting long processing

It’s a little bit out of scope of this article, but together with a web worker in version 4.2 library added another handy feature to the Pivot widget. If the processing takes too much time with the current configuration, your users no longer need to wait until the browser crashes.

You can switch off the heavy operation if its execution takes more time than you accept by throwing an error within the related ping property function:

webix.ui({
   view:"pivot"
   ping:function(start){
      var now = (new Date()).valueOf();
      //break if processing is longer than 3 seconds
      if (now - start > 3*1000){
         alert("Too complex parameters, aborting");
         throw new Error("Aborting...");
      }
   }
});

Live demo >>

This option can be used regardless of whether a web worker is included into this Pivot object or not.

Conclusion

As you can see, HTML5 Web Workers opens a way to a multi-threaded architecture of the application, which enhances user experience by clearing the main application thread from heavy-weight tasks.

In general, developing web applications is like a breathtaking game where you have to constantly pursue a better solution, and our team is always eager to help you. For instance, we have recently discussed a Webix way to overcome Javascript loose variable typing. To learn about strict types for library widgets, follow the related Using Webix with TypeScript article in our blog.

Pivot is a complex widget of the UI library, which is distributed under the commercial license and can be included into Webix package. You can download Pivot to evaluate the widget.

The post Boosting Webix Pivot Performance: HTML5 Web Workers appeared first on Webix.

Creating a Rich Javascript Form with Webix UI

$
0
0

Only good-looking and powerful tools for interacting with users can help developers conquer the modern web. Forms are true visitor cards of your application, as the positive experience during filling them adds a lot to user loyalty. Let’s find out how many carma points you can achieve by choosing Webix Javascript form.
forms in javascript

HTML and Javascript Form

Webix Form answers a question of how to create a form in HTML using Javascript in the best way. The library’s UI Form widget is a fully-featured tool that acts both as a container for numerous controls and as a processor for their values.
HTML and Javascript Form

With our UI Form you can:

  • arrange input elements nicely;
  • set and get values of form elements in a scope;
  • validate form values with ready-made and custom functions;
  • parse server-side values into a form and save them back to server;
  • check form state and control focus within it.

The library offers two types of Javascript forms: Form widget and HTMLForm widgets. The former one is a pure Javascript Form with Webix controls as elements while the latter one is a wrapper for standard HTML inputs.

Like UI Form, the HTMLForm widget is supplied with form JS functions for getting and setting values, but its validation possibilities as well as the choice of controls is limited to HTML. So in this article we will speak about the UI Form widget only.

Form Controls and Input Types

The UI library offers a lot of Javascript controls to aid data inputting and visualize it as much as possible. There are various button and input types alongside with select boxes and value pickers, so you will never get bored even at the planning stage:

Buttons for clicking

Buttons for clicking

Although the library’s blue button is the best-known, there’s still a lot to click and push: colored buttons, directional buttons, buttons with images and icons, a two-state button (Javascript Toggle), a segmented button and clickable icons.

Text fields for typing

Text fields for typing

Apart from a standard text field, you will find similar inputs with autocompletion and autoformatting. There are also specific controls for searching (Javascript Search) and collecting homogeneous text values (Multitext).

text area
You may have a look at Textarea for placing a long text and, if you need to add styles to the typed text, there’s a dedicated rich textarea control (Richtext).

Selection controls for limiting user choice

Selection controls for limiting user choice
No Javascript form can get without advanced selection controls, so this section will be the richest one. You may need to select.

  • Numbers. In this case Counter, Slider, and RangeSlider controls are appropriate;
  • Dates. Here we can offer Datepicker and DaterangePicker;
  • Colors. As you might have already guessed, it’s Colorpicker that comes to your help;
  • Just “yes” or “no”. Checkbox is an unchallenged leader in this area, so use the same-name control 🙂
  • Any text value. For a simple selection use Radio or Richselect and, if you need to type text in the input field, have a look at Combo;
  • Several text values. For a simple selection use Multiselect and, if you need to type text in the input field, take Multicombo.

Helper controls to make the environment cute

Helper controls to make the environment cute

You can organize space in your Javascript form wisely by:

  • Dividing it into sections with a Template widget of a “section” type;
  • Placing same-purpose controls inside a Fieldset;
  • Providing tabs by combining a Tabbar control and a Multiview widget.

Some of the library controls like Text, Select, Radio and Checkbox are based on HTML inputs, while the majority is pure Javascript form controls.

Any Webix Widget can be a Form Control

Such a loud title is about to make senseless the above part, but let’s examine this concept closer.

Starting from version 4.3 the framework adds a powerful FormInput widget that allows placing any Webix widget into a form by making it labellable and fitting form design. It means that any JavaScript List or JavaScript Table or even another Form can be put into your Javascript form.

grid, list and form

Still, such a valuable addition has its own pros and cons. The FormInput deals only with a widget’s look-and-feel while embedding it into a form.

webix.ui({
view:"form", id:"myform", elements:[
{ view:"text", label:"Team Name", name:"team" },
{ view:"forminput", label:"Members", body:{
view:"datatable", select:true, autoConfig:true
}}
]
});

$$("myform").setValues({ team:"My team" });

But the FormInput control cannot pass the widget’s data for processing it with Javascript Form functions that set and get form values, unless this inner widget itself features the related setValue() and getValue() methods.

The Datatable doesn’t have such methods, but you can easily add them by creating a custom widget based on UI Datatable:

webix.protoUI({
name:"formgrid",
getValue:function(){
return this.serialize();
},
setValue:function(values){
this.clearAll();
this.parse(values);
}
}, webix.ui.datatable);

Now, you can safely provide the name property to a custom FormGrid widget and process its values within a parent form:

webix.ui({
view:"form", id:"myform", elements:[
{ view:"text", label:"Team Name", name:"team" },
{ view:"forminput", label:"Members", name:"members", body:{
view:"formgrid", select:true, autoConfig:true
}}
]
});

$$("myform").setValues({
team:"My team",
members:[
{ fname:"Alex", lname:"Brown", email:"alex.brown@gmail.com" },
{ fname:"Julia", lname:"Welner", email:"juliaw@gmail.com" },
{ fname:"Maksim", lname:"Resvik", email:"resvik12@hotmail.com" }
]
});

You can check the live demo by the link.

Javascript Form Building Tools and Widgets

Form Builder

If you need to create a lot of simple forms, but are tired of routine monkey-style coding, you can have a look at Webix Form Builder visual tool that can generate the code for you.

form builder
All you need is to provide the names for text fields and change their properties – all by visual means. After that, you will just need to copy the resulting Form code and paste it into your application.

You can learn more about Form JS builder in the related blog article.

Form Editor

The library can also offer a specific widget for effortless editing of JSON data in the form. The widget called Form-Editor can create a form layout automatically based on the data you need to edit.

Form Editor

Live demo >>

The tool is available as a free addition to the library’s commercial version and its source code is available in the Components repository on GitHub. For more information, please follow another blog write-up.

Conclusion

The possibilities of Webix forms are vast and can open the way to another article, but it is practice not speaking that makes perfect. Explore JavaScript Form controls, study its processing features and get ready for the summer with another breathtaking application made on Webix. See you!

The post Creating a Rich Javascript Form with Webix UI appeared first on Webix.

What is the Difference Between UI Widget, Control, Component, Complex Widget

$
0
0

When reading Webix documentation you cannot but notice the variety of terms like component, ui control, ui widget and complex widget used alongside with each other. You may easily wonder whether they are interchangeable and how to distinguish between them. In the article below we’ll put them all to order.

jquey like UI widget control component description

UI Control

Controls are small interface elements used for interacting with users. You can place them into a form or on a toolbar or simply use them as independent layout elements.
The most obvious example of a control is a button. Buttons allow users to navigate through a web application, submit forms and call for various actions.

button UI control

Live demo >>
Or, take UI checkbox for instance.This small control allows users to respond as “yes” or “no” to various application options.

checkbox ui control

Live demo >>

Standard Edition Controls

Currently the following controls are available in the Standard Edition:

  • Button
  • Checkbox
  • Colorpicker, an input with a colorboard for picking a particular color
  • Combo, an editable input field with a pop-up window that contains options for selection
  • Counter, a spin-box for entering numeric values
  • Datepicker, an input with a calendar for picking dates
  • Fieldset, a container for grouping same-purpose controls
  • FormInput, a container for rendering UI widgets within a form in a common style
  • Icon, clickable control with an icon
  • Label, a plain text label
  • Radio, a control for choosing a single item from several options
  • RichSelect, a non-editable input field with a pop-up window that contains options for selection
  • Search, an input with a “search” icon
  • Select, a wrapper for HTML select
  • Segmented, a button divided into the several sections
  • Slider, a control with a draggable marker for inputting numeric values
  • Tabbar, a switch control to navigate between application parts
  • Text, a simple input field
  • Textarea, a multiline input field
  • Toggle, a two-state button
  • RichText, an editor for entering and formatting text
  • Uploader, a control for file uploading

PRO Edition Controls

Webix PRO edition adds the following controls to the above mentioned list:

  • DateRangePicker, an input with related calendars for picking date ranges
  • Multicombo, an editable input field for choosing multiple items from the related popup
  • Multiselect, a non-editable input field for choosing multiple items from the related popup
  • Multitext, a set of input fields for entering multiple text values
  • RangeSlider, an advanced version of a Slider for entering a range of values

UI Widget

The UI Widget is an integral part of user interface. Unlike controls, it is an independent module with a specific purpose, e.g.:

Take, for example, JavaScript Charts. Their purpose is to present data in a graphical form.

charts ui widget

Live demo >>

Another example is the JavaScript Form. Its main goal is to help users share their information with the web application:

form ui widget

Live demo >>

Standard Edition UI Widgets

Webix JavaScript library contains a number of widgets in the Standard Edition:

  • Accordion, a list of collapsible panels
  • Double List, a two-part list
  • Calendar for displaying date and time
  • Carousel for arranging multiple application parts
  • Chart for data visualization
  • Colorboard for displaying a color palette
  • Context, a popup window that opens by a right mouse click
  • Contextmenu, a popup window with a menu that opens by a right mouse click
  • Datatable for advanced data management
  • Dataview for data management
  • Form for arranging input controls
  • Grouplist, a list for a grouped data set
  • Htmlform, a container for a simple HTML form
  • Iframe, a container for an iframe
  • Layout, a container for widgets and controls
  • List for displaying a plain data set
  • Menu for navigating between application parts
  • Multiview and tabview for arranging application parts
  • Pager, a helper component to enable paging within data management widgets
  • Popup, a simple window
  • Property, for managing simple data
  • Resizer for resizing application parts
  • Scrollview, a container for a long scrollable application part
  • Sidemenu, a navigation panel
  • Spacer, a placeholder for an empty application part
  • Template, a text or HTML wrapper
  • Toolbar for arranging buttons and similar controls
  • Tooltip, a small popup hint
  • Tree for managing hierarchical data
  • Treetable for advanced management of hierarchical data
  • Unitlist, a list with sections for related data items
  • Video for playing video files
  • Window, an advanced window
  • Maps, a wrapper for Google maps with data management logic

PRO Edition UI Widgets

The PRO edition adds another 10 widgets to the above list:

  • Barcode for displaying codes
  • Bullet Graph for displaying dynamic data
  • Daterange for displaying date and time period
  • Excel Viewer for rendering Excel data in a web application
  • Gage for displaying dynamics of a certain parameter
  • Organogram for visualizing hierarchical schemes
  • PDF Viewer for rendering PDF files in a web application
  • Portlet, a layout with draggable parts
  • RangeChart, a chart with a draggable frame for detailed data visualization
  • TreeMap for visualizing hierarchical data

Complex UI Widget

Complex widgets in Webix terms provide extended functionality. Placed on a web page they look like complete applications and perform in the same way.
Complex widgets are comprised of multiple Webix widgets and controls, but there’s no need to tune them in configuration as everything works out of the box.
For example, with the SpreadSheet UI widget you can create a web app for working with tabular data in Excel manner, but the initialization code is incredibly laconic:

webix.ui({ view:"spreadsheet", data:base_data });

And you get the fabulous app in a browser tab:

spreadsheet complex ui widget

Live Demo >>
Another example is a Kanban Board. Its initialization code is more extended as you need to provide settings for separate Kanban lists, but the idea remains the same.

webix.ui({
view:"kanban", type:"space", cols:[
{ header:"Backlog", body:{ view:"kanbanlist", status:"new" }},
{ header:"In Progress", body:{ view:"kanbanlist", status:"work" }},
{ header:"Done", body:{ view:"kanbanlist", status:"done" }}
],
data: base_task_set
});

kanban ui widget
Live demo >>

Sure, complex widgets are not black boxes. They can be configured and customized to a certain extent to match developer needs.
Complex Widgets are available in a PRO edition only. Here’s the list of them:

Components

difference between UI widget component control complex widgets

I guess, the “component” can be a key word of Webix documentation :), but how does it correlate with complex and simple widgets and controls?
Simply put, a component is a generalized notion and can easily substitute “widget” or “control” in a developer hangout. It’s an abstract concept of a tool you use, while a UI widget is its real-life implementation.
To cut a long story short…
Here’s a little memo with the key findings:

  • Controls always presuppose interaction with users. They are small, clickable and primarily visual. It is developers who attach actions to them.
  • Widgets are integral parts of user interface. They have a particular goal and a lot of specific features to achieve it.
  • Components acts as an alias to widgets treated from a developer point of view
  • Complex widgets are self-sufficient applications which can be used standalone as well as embedded into Webix or HTML layout.

From now on, possible misunderstandings will not confuse you anymore and you will confidently pursue your goal in a web app world with Webix right behind your back.

The post What is the Difference Between UI Widget, Control, Component, Complex Widget appeared first on Webix.

D3 Charts: Integration and Examples

$
0
0

Being keen on data management and visualization, Webix always stays flexible and open for interaction with other frameworks, so your creativity will never be limited. The UI library can easily welcome your favourite maps, text editors and visual data-driven tools like Sigma, Raphael and D3 charts in addition to JS Chart widget.
D3 Charts: Integration and Examples

Although the integration of several JS libraries seems to be a challenge, you will not need to invent a bicycle, since basic integration patterns were created by our team long ago. Let us learn now how to develop visual interfaces by combining our UI framework with D3.js tools.

The ready-made integration code is located outside the main library package, in the repository of custom components on GitHub. In the related “d3” folder you will find the necessary adapter and the basic sample.

Creating reusable charts with D3.js

To start using D3 charts while developing Webix UI, you need to include the adapter right after the library’s script into the head section of your document:

<script src="http://cdn.webix.io/edge/webix.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" src="./d3.js"></script>

The adapter adds a specific widget called “d3-chart” to the library collection, so the creation of D3 charts doesn’t differ from initialization of any other library’s widget. Let’s place a header and a D3.js data chart into a basic vertical layout:

webix.codebase = "//cdn.webix.com/components/d3/d3.js";
webix.ui({
      rows:[
            { type:"header", template:"D3 Chart"},
            { view:"d3-chart", url:data_url, ready:function(){
                  var svg = d3.select(this.$view).append("svg")
                        .attr("width", this.$width).attr("height", this.$height);
                  //further chart initialization code
            } }
      ]
});

That’s it! Now, you have a working chart in your web app:
D3.js chart for web app
Live demo >>

Basically, Webix creates a container for an SVG D3 chart element to smoothly embed it into the application. It is accessible as this.$view in the ready handler of the “d3-chart” widget.

You can initialize any of the D3 charts (Bubble or Sunburst, etc.) within this container:
bubble Sunburst d3 chart
Live demo >>

Moreover, you can include several instances of D3 chart into the page, which highlights reusability of this custom widget:

webix.ui({
      rows:[
            { view:"d3-chart", resize:true, url:data_url, ready:function(){
                  //init bubble chart
            } },
            { view:"d3-chart", resize:true, url:data_url, ready:function(){
                  //init sunburst chart
            } }
      ]
});

chart integratin d3 bubble and sunburst

Live demo >>

The D3 charts can be made resizeable by the corresponding resize setting. In this case, the inner chart adjusts its dimensions each time viewport or container size changes. You can easily check it in the demo by dragging the resizer line between the two charts.

D3 Charts Integration Basics

If you want to learn what’s hidden in the black box, you can have a look inside the adapter (the code is distributed unminified) and admire its laconic and clear style:

webix.protoUI({
      name:"d3-chart",
      defaults:{},
      $init:function(){ ...},
      $setSize:function(x,y){ /*resize inner d3 chart*/ },
      _render_once:function(){ /*render d3 chart*/ }
}, webix.AtomDataLoader, webix.EventSystem, webix.ui.view );

This is a common pattern for developing Webix custom widgets. The protoUI constructor contains the following arguments:

  • widget settings such as:
    • widget name (later on it will be used as value of the view parameter);
    • default properties (optional);
    • private and public methods;
  • the dependency chain, which includes all the modules this custom widget is based on.

The integrated D3 charts view inherits from the basic view, a simple HTML container, AtomDataLoader to enable data loading and EventSystem to call Webix events.

Also, we have included specific methods into the prototype:

  • $init – contains the logic called during initialization, and here it describes basic rendering pattern;
  • $setSize – contains the logic called when the widget is resized in Webix environment, and here it forces the D3 chart to adjust to the new dimensions.

Following these simple steps you can embed any visual instrument into the application based on our UI library. This is a valuable investment into your future projects, but at this very moment you can use the D3 charts in their full beauty just as:

webix.ui({ view:"d3-chart", url:data_url, resize:true });

Conclusion

D3 charts are bright and spectacular data visualization tools. You can add a lot of charm and value to your application without worrying about possible integration collisions. The one who can choose the best pieces of software and combine them all together to achieve the best result wins in the competitive online world.

The post D3 Charts: Integration and Examples appeared first on Webix.

Printing Functionality and Other Features of Webix UI 4.4

$
0
0

We are happy to announce our new release 4.4 of JavaScript UI components that will be available for download in a week. It includes 3 main features: printing directly from an application, advanced filtering and demos of integration with backend platforms.

JavaScript UI components library announsment

Printing Functionality

Print API is the most important feature of release 4.4 that helps users print data directly from a web application. This functionality can be used with all Webix UI widgets but it’s particularly useful for working with tables and forms. For instance, the complex widget Spreadsheet will allow printing with preset settings by clicking “Print” button or pressing specially assigned hot keys.

Query Builder Widget

Query Builder is a new widget designed to expand filter functionality and simplify working with big data. It will help to set complex filtering rules. You will be able to group the rules and even apply several groups of rules at the same time.

Demos of Integration with PHP, Node.js and .NET

Webix library can be integrated with any server side platform due to its use of universal data formats such as JSON, XML, JSArray, and CSV. Our new release includes samples of Webix UI integration with PHP, Node.js, and .NET. Now you don’t need to reinvent the wheel.

The new release will be available for download on June, 27.

The post Printing Functionality and Other Features of Webix UI 4.4 appeared first on Webix.

CSS on the Grid-iron: Grid Layouts

$
0
0

This is a guest post from our friends DevelopIntelligence.com

Pity the poor table. Designed originally for the purpose of displaying row and column data, it has often had to serve double duty as a generalized layout tool, despite the many quirks that tables have for layout purpose and the fact that syntactically, a table is intended to contain data. Other solutions involved absolute positioning of display blocks, but these are often highly problematic in devices that can have windows of varying dimensions.CSS Grid Layout

In March 2017, the grid layout was reintroduced into the CSS specifications after having been retooled for the better part of two years, and Chrome, Mozilla, and Vivaldi wasted no time in supporting the grid functionality. Given the central role that layout plays in CSS, the grid layout provides a degree of functionality that can radically simplify the HTML code being generated, though it comes at the cost of a bit of a learning curve.

A good example to illustrate a grid is a four by four grid rectangle using area names. The HTML describes five areas used as part of a game output:

<!-- html game grid -->
<div class="grid">
    <div id="button-bar" class="grid-item">Button-Bar</div>
    <div id="title" class="grid-item">Title</div>
    <div id="search" class="grid-item">Search</div>
    <div id="score" class="grid-item">Score</div>
    <div id="stats" class="grid-item">Stats</div>
    <div id="board" class="grid-item">Board</div>
    <div id="controls" class="grid-item">Controls</div>
</div>

Assuming the grid as uniform in size, the grid-items can be laid out as follows:

.grid {
    display: grid;
    margin-top: 10px;
    max-width: 500px;
    grid-template-areas:
      "header header header header"
      "sidebar board board board"
      "sidebar board board board"
      "sidebar footer1 footer1 footer2";
}
.grid-item {
    border: solid 1px black;
}
#title {
    grid-area: header;
    text-align: center;
    background-color: navy;
    color: white;
}
#score {grid-area: sidebar;}
#board {grid-area: board;}
#stats {grid-area: footer1;}
#controls {grid-area: footer2;}

In this case, the grid-areas property within the containing .grid element provides a map defining each area. The <div> elements with the corresponding ids are then mapped via CSS to each respective area. Thus the contents of the <div> element with id=”title” takes up the header area, #score takes up the sidebar and so forth:

Game Grid with grid areas

There are several things of note here. First, with grid-areas, there is no need to create nested divs. A block is placed into a location, regardless of the order that it is listed in. This is actually a powerful tool because the layout can be reconfigured based upon media dimensions or similar characteristics, without having to change the HTML. For instance, you can create the above layout if the minimum device screen width is 501px, or a stacked layout if the size is below that threshold (as would be appropriate for most smart-phones):

.grid {display: grid;}

@media screen and (min-width: 501px){
  .grid {
    grid-template-areas:
      "header header header header"
      "sidebar board board board"
      "sidebar board board board"
      "sidebar footer1 footer1 footer2";
    }
}

@media screen and (max-width: 501px) {
  .grid {
    grid-template-areas:
      "header"
      "board"
      "controls"
      "sidebar"
      "footer1"
      "footer2";
  }
}

This is, by itself, worth the price of admission for grids. By being able to create different types of layout configurations without having to change the underlying markup, you can significantly reduce the amount of extra coding (and server-side media device detection) that needs to happen to better facilitate adaptive layouts.

But wait, there’s more. The second and third column in the grid are identical. Rather than repeating the column, another approach would be to combine the two columns but make this new column twice the width of the fourth. While at it, the left-hand sidebar should in general always be a fixed width, say 240px. This can be down with the grid-template-column property. Similarly, rows two and three also repeat, and can also be reduced to

.grid {display: grid; width: 8in; min-height: 250px;}

.grid {
  grid-template-areas:
    "header header header"
    "sidebar board board"
    "sidebar footer1 footer2";
  grid-template-columns: 240px 2fr 1fr;
  grid-template-rows: 32px auto 40px;
}

.grid-item {
  border: solid 1px black;
}
#title {grid-area: header; text-align: center;}
#score {grid-area: sidebar;}
#board {grid-area: board;}
#stats {grid-area: footer1;}
#controls {grid-area: footer2;}

The grid-template-columns property controls the width (either relative or absolute) of each named region from left to right while grid-template-rows covers definitions of the grid from top to bottom. Thus

grid-template-columns: 240px 2fr 1fr;
grid-template-rows: 32px auto 40px;

establishes a three-by-three grid where the first cell is 240px wide and 32px deep. The second cell in the row has a measure of 2fr, or two “free space” units, while the third has one free space unit. Free space is determined by taking the overall dimensions of the grid (if known), subtracting all of the fixed dimensions, then dividing the resulting free space by proportion (2fr is twice the size of 1fr). If the grid is not otherwise constrained it will be the available width of the page, so if the width of the grid’s container is 700px then 1fr in the above will be set to 220px, 2fr will be 440px. Should the container be resized, the free space will be recalculated.

Grids can be defined by area, but they also can be defined by the bounding lines. Names can be established from the templates by using square-bracket enclosed names for the spaces between the area widths. For instance, the streets in Seattle use Street for east/west lines and Avenue for north/south lines, so the following would identify a relevant line:

grid-template-columns: [1st-Street] 1fr [2nd-Street] 1fr [3rd-Street] 1fr [4th-Street] 1fr [5th-Street];
grid-template-rows: [1st-Ave] 32px [2nd-Ave] auto [3rd-Ave] 40px [4th-Ave];

You can then identify the board area as:

#board {
  display: block;
  grid-column: [2nd-Ave]/[5th-Ave];
  grid-row: [2nd-Street]/[4th-Street];
}

which would correspond to

grid-template-areas:
  "header header header header"
  "sidebar board board board"
  "sidebar board board board"
  "sidebar footer1 footer1 footer2";

where the “/” character should be read as “spanning to”, e.g.,

grid-column: 2nd-Ave/5th-Ave;

reads as:

grid column: 2nd Ave spanning to 5th Ave;

The actual numeric positions can be used in this way as well:

grid-column: 2/5;

will span from the second north-south line to the fifth.
This is a short-hand for the grid-column-start and grid-column-end commands (ditto for row):

grid-column-start: 2nd-Ave;
grid-column-end: 5th-Ave;

/* or */

grid-column-start: 2;
grid-column-end: 5;

There are a number of good resources for digging deeper into grids:

CSS Grids have the potential to change the way that web pages are laid out. They give the user a much more precise tool for laying out content that is nonetheless responsible to device limitations and dimensions, creates a separation of concerns between content and layout, and makes both HTML and CSS clearer in terms of intent. They should become a major go-to feature for any web designer now and in the future.

The post CSS on the Grid-iron: Grid Layouts appeared first on Webix Blog.

Using reCaptcha in the Webix Form

$
0
0

Bots are around us. Some of them are good and they help us in everyday tasks, but some are evil and attempt to steal our information. If you are familiar with web development and forms in particular, you may have faced certain security issues like abusive form input from bots. Measures need to be taken to make your website or web application more safe, and using reCaptcha is one of them. Despite Webix does not offer such a widget of its own, there is a solution to it.

Let’s take a closer look at this way of protecting websites and web applications from spam and abuse. I will not discuss the server side of reCaptcha validation in the article, I will only show you the client side logic.

Webix Form with reCAPTCHA

Why reCaptcha?

By adding reCaptcha v2 to a site, you can block automated software while helping your welcome users to submit their input with ease. This version of reCaptcha launched by Google in 2014 doesn’t require form visitors to type anything (looking at those nasty pieces of distorted text) into a validation field. Now they simply have to check a box, while the system verifies whether the user is a human or not with some clues, for example, such as mouse movements within the reCaptcha area. Only if it registers something suspicious, users will have to pass some additional validation.

How to use reCaptcha in a common HTML form

Let’s start our journey to adding reCaptcha to a webpage. It’s pretty easy, and first of all you need to get the keys for your web site on the official site.

Registering the domain on reCaptcha site

  • You need to be logged into your Google account to sign up for a key pair. This pair consists of a site key and a secret key. You will need the site key to invoke reCaptcha service on the client side of your application. The secret key will be necessary for server-side JavaScript captcha validation.
  • Then you are to choose the type of reCaptcha among the three options – v2, invisible, or Android Library – and fill in authorized domains or package names. Let’s choose reCaptcha v2.
  • After you accept the terms of service, you can click Register button to get your key pair.

Voila, you’ve completed the first step. Now you can add some code to your page.

Adding JS and HTML code to a page

Rendering reCaptcha v2 requires only two lines of HTML.

<div class="g-recaptcha" data-sitekey="6LcOjCUUAAAAADvFhArNTzH4-3psPpbJS8dKcReL"></div>
<script src='https://www.google.com/recaptcha/api.js'></script>;

You can check the live demo.

As you can see, all you need to do is include the necessary JavaScript resource and a div element with the class name g-recaptcha. You are to put your site key in the data-sitekey attribute.

Adding ReCaptcha to Webix Form, naive implementation

However, it isn’t so easy to add a working reCaptcha if you create web apps with Webix. You cannot just put it in the form like we did in the previous section, because there is no such view as “captcha”. There is a straight solution to it, and in this section we will show you how to validate captcha in JavaScript.

webix.ui({
    width: 340, rows:[
        { view:"form", elements:[
            { view:"text", label: "Name", name:"name" },
            { view:"text", label: "Email", name:"email" },
            { id:"capcha" },
            { view:"button", value:"Register", click:function(){
                $$("out1").setHTML( JSON.stringify(this.getFormView().getValues()) );
            }},
            { view:"template", id:"out1" }
        ]}
    ]
});

window.grecaptcha.render($$("capcha").$view, {
    sitekey:"6LcOjCUUAAAAADvFhArNTzH4-3psPpbJS8dKcReL"
});

Check out the live demo.

This is how our form looks like:

As you can see, here we create a form, populate it with controls and create an empty view for reCaptcha with id:”capcha”. Then we invoke reCaptcha’s method render with that empty “capcha” view as a container parameter and an object with the site key as the second parameter. It successfully renders reCaptcha, but it still doesn’t serve any purpose, because users can submit the form input with or without checking reCaptcha.

The first step to solve this problem is to create a special reCaptcha view on the basis of webix.ui.view. This is how we do it.

webix.protoUI({
    name:"recapcha",
    $init:function(){
        this.$ready.push(function(){
            window.grecaptcha.render(this.$view, {
                sitekey:"6LcOjCUUAAAAADvFhArNTzH4-3psPpbJS8dKcReL"
            });
        })
    }
}, webix.ui.view);

You can check the live demo.

Here we define a new recaptcha view with the help of a custom control method, so that the render method would be invoked during the initialization of the reCaptcha. Besides, we give it a name.
After we defined the view, we are free to place it into our form.

webix.ui({
    width: 340, rows:[
        { view:"form", elements:[
            { view:"text", label: "Name", name:"name" },
            { view:"text", label: "Email", name:"email" },
            { view:"recapcha" },
            { view:"button", value:"Register", click:function(){
                $$("out1").setHTML( JSON.stringify(this.getFormView().getValues()) );
            }},
            { view:"template", id:"out1" }
        ]}
    ]
});

This solution is better than the previous one, because now we don’t need to trace the moment of the initialization of the form and invoke render strictly after it.
Still reCaptcha doesn’t interact with the submission button, so this is not the solution we seek for.

Adding ReCaptcha as a fully fledged widget

Finally, here is the good news, this is a final step before our solution.

First of all, we add getValue function, that will get the value of reCaptcha’s response out of its hidden textarea, and give a name to the control.

webix.protoUI({
    name:"recapcha",
    //other code
    getValue:function(){
        return this.$view.querySelector("textarea").value;
    },
    //other code
    defaults:{
        name:"g-recaptcha-response",
        //other code
    }
}, webix.ui.view);

Now we have a working reCaptcha that just needs some styling.

It’s time to add all other updates to polish the final widget.

webix.protoUI({
    name:"recapcha",
    $init:function(){
        this.$ready.push(function(){
            var key = this.config.sitekey;
            window.grecaptcha.render(this.$view, {
                sitekey:key
            });
        })
    },
    getValue:function(){
        return this.$view.querySelector("textarea").value;
    },
    setValue:function(){ /*do nothing*/ },
    focus:function(){ /*do nothing*/ },
    defaults:{
        name:"g-recaptcha-response",
        borderless:true,
        height: 85
    }
}, webix.ui.view);

Check out the live demo.

Here, we add the sitekey property into the definition of the “recapcha” view. Besides, we define additional methods – setValue and focus. In the above code they are empty. They allow the “recapcha” view to be treated just like the rest form controls. Finally, we give two more default properties to our new widget for a nicer look.

Now we are ready to put it into the form.

webix.ui({
    width: 340, rows:[
        { view:"form", elements:[
            { view:"text", label: "Name", name:"name" },
            { view:"text", label: "Email", name:"email" },
            { view:"recapcha", sitekey:"6LcOjCUUAAAAADvFhArNTzH4-3psPpbJS8dKcReL" },
            { view:"button", value:"Register", click:function(){
                $$("out1").setHTML( JSON.stringify(this.getFormView().getValues()) );
            }},
            { view:"template", id:"out1" }
        ]}
    ]
});

There is a slight change in this piece of code, because we assign our site key to the newly created sitekey property during initialization.

Eventually, this is the solution we wanted.

Conclusion

In this article, we’ve discussed the process of integrating reCaptcha v2 into the Webix Form. Now you see that creating web applications with Webix and being able to protect it with reCaptcha isn’t so complex. You just have to add a few lines of code, and you’ll have a fully functioning reCaptcha widget on your page.

ReCaptcha v2 is not the only technique for weeding out bots. Google created other versions that were briefly mentioned – the invisible reCaptcha and Android Library reCaptcha for mobile apps. Besides, there is also an “irresistible honeypot” for that, which seems pretty good. For more information, you can visit the guide to reCaptcha provided by Google and a nice tutorial on integrating reCaptcha into a website.

The post Using reCaptcha in the Webix Form appeared first on Webix Blog.


Webix UI 4.4 Release Overview

$
0
0

The new release 4.4 of Webix JavaScript UI library is available for download. Let’s have a look at the changes and new functionality: Print API, Query Builder, Demos of Integration with PHP, Node.js and .NET.
JavaScript UI framework with Print API

Printing from Applications

The new release allows printing directly from applications. You can print any Webix UI widget, such as JavaScript List, Grid, or Chart. With the help of Print API you can set the way to call the print function, for example, on a button click or on pressing specially assigned hotkeys. Moreover, you can set printing options, for instance:

  • the print area
  • the paper size
  • the margins
  • page orientation and scale
  • headers, grid lines and empty rows printing

The variety of printing options depends on the type of a widget. The complex widget Spreadsheet features a built-in printing functionality:
JavaScript print
Live Demo >>

Advanced Filtering

Query Builder, the 89th Webix widget, is a constructor of complex rules for filtering. You can use it with any data components of the library. From now on, the DataTable widget allows using the Query Builder as a built-in filter.
Query Builder javascript

Live Demo >>

The new widget allows building complex filters by grouping several rules (or even groups of rules) based on the specified conditions (AND or OR). Each rule includes a criterion that the filtered values should meet. Query Builder widget is available only for Webix PRO.

Clear Integration with Backend Platforms

The new demos, that can be found on GitHub, are aimed at simplifying integration with:

  • PHP
  • Node.js
  • .NET

The demos show how to connect Webix UI with these backend platforms. We are planning to expand the list of demos on integration with backend platforms.

The Site Update

You must have noticed that the Webix site looks different. We are doing our best to keep up with the times and gradually adding new parts to the site. We have added a page with video tutorials, simplified the form on the Trial download page of Webix PRO with all complex widgets, upgraded the license page. From now on you can add the necessary number of licenses for complex widgets to your Team Pack.

Minor Changes

Disabled menu items

We added the special attribute for the Menu widget. This attribute allows disabling certain menu items during the initialization of the component.

Updated integration with Google Maps

Integration with Google Maps was improved and included into the library as a separate widget. Since version 4.4 we have removed the outdated integration from the components repository on GitHub and from CDN.

You can find the complete list of changes on the What’s new page.

Try the Trial version of Webix PRO with complex widgets Pivot, Kanban, Scheduler, Spreadsheet, File Manager and feel free to give your feedback. Clients with an active license can download the new release from the Client Area or via CDN.

Meanwhile we are working on release 5.0. It will include awesome updates and long-expected features 🙂

The post Webix UI 4.4 Release Overview appeared first on Webix Blog.

Preserving Data during Page Reloads

$
0
0

No one is exempt from making mistakes and getting browser crashes. This can be really annoying, if you have worked hard for a long time filling in an extremely complicated form, looking for data in a big spreadsheet with complex filters or have just set a nice and convenient layout scheme to work on a webpage — and everything is lost just because the page is reloaded or closed. It is a good idea to save and store temporary data to rescue your users from such a disappointing experience and help them not to lose their time doing everything all over again or readjusting their working space every time they open the same page.

If you create applications with forms, tables and complex layouts and want to help your end-users restore the data that haven’t been saved locally or sent to a server due to some accidents and mistakes (page reloads, browser crashes, laptop shutdowns, etc) or just want to let them preserve the state of an app for any other reason, I will show you how.

preserving data on page reload

Common Data Saving

Most Webix data components have a built-in means of saving data to a server or locally that allows doing this quite effortlessly. Such components as DataTable, List, Tree, Organogram and their derivatives have the save property that takes the URL you want to save your data to as its value:

webix.ui({
    view:”list”,
    //config
    url:”myprecious.php;
    save:”myprecious.php;
});

The data is automatically updated on the server every time you change the items in the data component.

If you want more flexibility, you can attach functions to component events to process the data before sending it to a server via Ajax POST request or saving it locally. This is the way Form and input data are typically saved (apart from pushing the data to the component it is bound with).

Beside actual data, Webix SpreadSheet allows saving and loading the current state of the datatable (the sizes of rows and columns, spans, filters and conditions, the style of cells, and math formulas).

You can see, that saving component data and, in some cases, even styles and filters on demand isn’t a problem. However, this doesn’t save the current state of the app and temporary input, so this isn’t the solution we crave. Be patient, in a few seconds I’ll show you the first way to achieve this glorious aim.

Saving Sizes of UI

If you create apps with different complex layout components like Accordion, you might want to save their state. Let’s create a simple layout with resizers, and I’ll show you how to restore its visual scheme after a page reload.

webix.ui({
    id:"top",
    rows:[
        { type:"header", template:"Resize and Save"  },
        { view: "resizer" },
        {
            cols: [
                { template:"You can write any code here"},
                { view: "resizer" },
                { template: "And here" }
            ]
        },
        { view: "resizer" },
        { template:"And even here" }
    ]
});

And now let’s add a few lines of code to make the visual scheme renewable:

var state = webix.storage.session.get("appState");
if (state)
    webix.UIManager.setState(state);
 
$$("top").attachEvent("onDestruct", function(){
    var state = webix.UIManager.getState($$("top"), true);
    webix.storage.session.put("appState", state);
})

Live demo >>

In the code snippet shown above, I made use of the Webix storage local and Webix UIManager objects to save the state of the app in the variable using the local storage on unloading the page and restore it when the page is loaded again.

Webix UIManager allows saving the current visual scheme of the component, its ‘state’, with the following methods:
getState() – to capture the current state of a Webix application;
setState() – to set the saved state for the application.

Methods put() and get() of the local store save and restore the state, correspondingly. You can also use the session store, if you don’t want to preserve data on browser closing.

In the above example, I stored data locally, but you can easily store it on a server, if you send an Ajax request instead of working with the Webix storage objects.

Now I’ll create a more complex layout to show you how to track active tabs in case of a multiview:

webix.ui({
    id:"top",
    cols:[
        { header:"sidebar", body: {
            view:"list", data:list_data
        }},
        { template:"Left"},
        { view:"resizer"},
        { rows: [
            { view:"tabbar",options:["Tab A", "Tab B"],
                  multiview:true, value:"Tab A" },
        { view:"multiview", animate:false,  cells: [  
                { id:"Tab A", template:"Content A" },
            { id:"Tab B", template:"Content B" }
            ]}
        ]}
    ]    
});
 
var state = webix.storage.session.get("appState");
if (state)
    webix.UIManager.setState(state);
 
$$("top").attachEvent("onDestruct", function(){
    var state = webix.UIManager.getState($$("top"), true);
    webix.storage.session.put("appState", state);
})

Live demo >>

As you can see, we are using exactly the same code to save a more complex structure. Try moving the resizers, selecting the second tab of the multiview or hiding the sidebar, then reload the page – and you’ll see that everything you did is intact.

Saving State of a Datatable (hidden columns, sizes, filters, etc.)

Now let’s see how to save the state of a datatable and restore it after page reload. I’ll create a datatable and populate it with data:

var people = [
    { id:"1", fname: "Jane", address: "7635 Garfield Drive South Lyon, MI 48178",  year: "1991"},
    { id:"2", fname: "Alex", address: "85 Airport St. Mebane, NC 27302",  year: "1878"},
    { id:"3", fname: "Mary", address: "14 West Lafayette Dr. Utica, NY 13501", year: "1989"},
    { id:"4", fname: "Edward", address: "47 Meadowbrook Drive Massapequa Park, NY 11762",  year: "2001"},
];
 
var grid = webix.ui({
    view:"datatable",
    id:"datatable",
    columns:[
        { id:"id", header:"ID", sort:"int", width:30},
    { id:"fname", header:"First name", sort:"string"},
    { id:"address", header:"Address", sort:"string", fillspace:true },
    { id:"year", header: ["Born",{content:"selectFilter"}], sort: "int" }
 ],
    resizeColumn:true,
    resizeRow:true,
    select:"row",
    data:people
});

In the snippet given above, I created a datatable with sorting and filtering. Now let’s make it renewable.

webix.attachEvent('unload', function(){
    webix.storage.local.put("state", grid.getState());
});
 
var state = webix.storage.local.get("state");
if (state)
    grid.setState(state);

Here, apart from the local object, I used the getState() and setState() methods of the grid object to save and restore the state on the page unload.

Live demo >>

The same technique can be applied to TreeTable and DataTree.

Saving State of a Form (Values)

At last we’ve come to the trickiest part of our quest to save temporary data, at the end of which you will create the Eternal Form that will restore all your unsubmitted input values and grant you eternal glory in the eyes of your end-users.

Let’s set off, guys:

webix.protoUI({
    name:"safe-form",
    $init:function(obj){
        //code
        this.attachEvent("onDestruct", function(){
            var name = this.config.saveName;
            if (name){    
                if (this.isDirty()){  
                    webix.storage.session.put(name, this.getValues());
                }        
            }
        });
    }
}, webix.ui.form)

Here I created a new view safe-form on the basis of webix.ui.form and enhanced this mighty tool with the ability to restore its input on init. As you can see, we save the name of a form during the destruction of the form, and if the form isDirty, its contents is saved to the local storage (again I remind you that it could have been an Ajax request to send the input to a server as well).

And finally, the Eternal Form itself:

webix.protoUI({
    name:"safe-form",
    $init:function(obj){
        var name = obj.saveName;
        if (name){
            var data = webix.storage.session.get(name);
            if (data)
                this.$ready.push(function(){ this.setValues(data) });
        }
        this.attachEvent("onDestruct", function(){
            var name = this.config.saveName;
            if (name){    
                if (this.isDirty()){  
                webix.storage.session.put(name, this.getValues());
            }        
            }
        });
    }
}, webix.ui.form)

Here I added the restoring logic. If something has been put into the form, it will appear again as you reload the page and the form is ready.

Live demo >>

Conclusion

O Captain! my Captain! our fearful trip is done, the ship has weather’d every rack, the prize we sought is won… Now you can create web UI components that can save temporary data and the current state of a web app, and your end-users can forget about worrying about their precious form input or any changes they make to the content or UI visuals. From now on, if you want to create a layout, a datatable or a form that will renew their state, you know how.

There is a drawback in these techniques, though: now you need to write extra code to reset the component back to its initial state, but that’s another story.

For further reading on the subject, you can resort to:

The post Preserving Data during Page Reloads appeared first on Webix Blog.

Master-Details Pattern with Webix UI

$
0
0

I guess, the master-details pattern needs no special introduction for practised web developers who create business apps that work with users, sales and such. Nearly any real life app has such a relation model and demands clear and easy navigation and inspection of information. Whether you plan to develop an email app, an address book, an RSS reader, a CRM, a social net app or any other app that is based on a list-details layout, the master-details pattern is a relevant solution.

Webix UI Library provides a number of ways to implement this technique: from simple to more complex, from pretty awkward to really handy ones. I’ll demonstrate them all while creating a mock email management app, and at the end of it all you’ll see the most efficient, reliable and good-looking solution. Well, without further ado, let’s get down to the details of our master plan.

Master-details pattern with Webix UI

Why Use the Pattern?

The concept of the master-details pattern is pretty easy to grasp. The app designed according to it has 2 views: a master view (usually a list or a grid) and a details view for content. When an item in the master view is selected, the details view is updated accordingly.

The master-details pattern is very useful, because it allows your end-users to:

  • manage a collection of complex content (lists of contacts, products, etc.)
  • quickly edit and remove items
  • switch between items frequently and stay in the same context (e.g. email apps)

There are 2 styles for implementing the pattern:

  • the stacked style (only one view is visible at a time: the master or the details; good for small screens);
  • the side-by-side style (the master view and details views are visible at the same time; good for bigger screens).

I will show you how to implement the pattern according to the second style. I’ll focus only on the pattern itself and won’t go into other aspects.

Sub-views in a Grid

First, guys, I’ll show you how to how to render nested data using subviews in Webix DataTable. Let’s create a datatable and populate it with inline data:

var grid = {
  view:"datatable", columns:[
    { template:"{common.subrow()}", width: 40},
    //other columns
  ], on:{
    onSubViewCreate:function(view, obj){
      view.setValues(obj.person);
      view.config.masterId = obj.id;
    }
  }, subview:subview,
  pager:"pagerU",
  data:emlist,
  autoheight:true, scroll:false
};
//a pager for better navigation through the datatable
var pager = { view:"pager", id:"pagerU", size:5 };

Master–detail interface pattern with Webix

As you can see, the first column of the datatable contains a button for opening a subrow, that will contain the details for a particular row of the datatable. For this purpose, I defined an event handler that will push the relevant data into the subrow, which in this case contains a form. Let’s look at the form:

var subview = {
  view:"form", elements:[
    { margin:20, cols:[
      //input controls
    ]},
    { align:"right", inputWidth:100, view:"button",
     value:"Save", click:function(){
       var form = this.getFormView();
       var values = form.getValues();
       var grid = form.getMasterView();
       var id = form.config.masterId;
       
       grid.updateItem(id, { person: values });
       grid.closeSub(id);
     }},
     actions
  ]
};

The form, beside the input fields and a table with actions, has the Save button, which updates the data in the master datatable with the input data from the form.

Now I add a toolbar:

var toolbar = {
  view:"toolbar", cols:[
    { view:"label", label:"Subscribers" },
    { view:"icon", icon:"envelope" }
  ]
};

and I am ready to initialize the UI of the email list.

Master–detail pattern

Live demo >>

So far so good. The solution is simple and does the trick, but the opened subviews take a lot of your screen space, and the datatable gets longer and longer.

Details as a Popup

To avoid making your datatable as long as Rapunzel’s hair, you can move all the details to a popup.

Subscribers view with Master–detail pattern

For that I’ll add a different event handler to the master datatable:

var grid = {
  view:"datatable", columns:[
    { template:"<span class='webix_icon fa-cog'></span>", width: 40},
   //other columns
  ], onClick:{
      "fa-cog":function(ev, id){
        $$("details").show(ev);
       
        var view = $$("details").getBody();
        view.setValues(this.getItem(id).person);
        view.config.masterView = this;
        view.config.masterId = id;
      }
    },
  //other unchanged code
};

This time, the first column of the master view contains icons for showing popups with the details. The popup will contain the same dear old form, though I moved the controls to a variable. This will help to make the code look better and to style the form:

var fields = { padding:10, rows:[
  {  margin:20, borderless:true,
    cols:[
      ...//input controls
   },
   {
      align:"right", inputWidth:100, view:"button",
      value:"Save", click:function(){
        ...//unchanged code
        grid.updateItem(id, { person: values });
        form.getParentView().hide();
      }
   }]
};

webix.ui({
  view:"popup", id:"details", minWidth:200, maxWidth:600, padding:0,
  body:{
  view:"form", padding:0, elements:[
      fields,
      actions
    ]
  }
});

Live demo >>

This solution is as simple as the previous one and doesn’t waste the screen space. However, as a popup is easy to close, a single twitch of your hand can bring on a disaster — and say goodbye to your unsaved input. Moreover, the position of a popup is relative, so it will block the next item you want to open. Let’s fix it.

Side Panel

Side panel with Master–detail pattern

Let’s perfect our app. First, I will slightly change the event handler, so that the position of the popup would be the same every time:

var grid = {
  view:"datatable", columns:[
    //columns
  ], onClick:{
      "fa-cog":function(ev, id){
        $$("details").show();      //delete the ev parameter
        //unchanged code
      }
    },
  //unchanged code
};

Next, I’ll change the event handler for the Save button in the details form:

var fields = { padding:10, rows:[
    ...//input controls
    { align:"right", inputWidth:100, view:"button",
     value:"Save", click:function(){
        ...//unchanged code
        form.hide();
    }}
]};

and add the last stroke to fixing the position of the popup:

webix.ui({
  view:"popup", id:"details", padding:0,
  position:function(state){
     state.width = 340;
     state.left = state.maxWidth - state.width;
     state.top = 0;
     state.height = state.maxHeight;
  },
  body:{
   ... //same body
  }
});

Live demo >>

Okay, this solution is pretty good, but you might have a few questions:

  • Why use icons, if we can click records to open details?
  • What if I do not want the popup to block the view of the datatable?

Here’s the answer, my dear friends, here’s the answer. Instead of a popup, I will create a hidden form, that will appear every time a user selects a row in the datatable.

First, let’s enable selection for the master datatable, change the size of its columns and change the event handler accordingly:

var grid = {
  view:"datatable", select:"row", columns:[
    { id:"email", header:"Email", template:"#person.email#", sort:"text", fillspace:1 },
    { id:"name",  header:"Name", template:"#person.name#", fillspace:1 },
    { id:"comments", header:"Comments", template:"#person.comments#", fillspace:2 }
  ], on:{
      onAfterSelect:function(id){
        $$("details").show();
        var view = $$("details");
        ...//unchanged code
      }
    },
  //unchanged code
};

Next, let’s replace the popup with a hidden form:

var details = {
  id:"details", hidden:true, minWidth:200,
  view:"form", padding:0, elements:[
    fields,
    actions,
    {}
  ]
};

Now I can initialize the UI.

Side panel UI with Master–detail pattern

Live demo >>

This is the optimal solution, as it both works well and looks good. In case you want to have the best result and have time for it, dive into the next example.

Custom View

If you don’t really fancy the way the master datatable looks like after the form is shown, it’s not a problem, as you can completely change the master view. I will replace the datatable with a list.

Custom view with Master–detail pattern

First, let’s change the master datatable a bit:

var grid = {
  view:"datatable", id:"master", select:"row", columns:[
    //columns
  ], on:{
      onItemClick:function(id){
        $$("details-view").show();
        $$("details-list").select(id.row);
        ...//same four lines of code
      }
    },
  //other unchanged code
};

Note that this time the event and its handler are different: it’s going to make a list with details visible. Let’s have a look at this mysterious list:

var list = {
  view:"list", select:true, id:"details-list", item:{
    width: 320, height:65
  }, template:"#person.email#</br>#person.name#", on:{
    onItemClick:function(id){
      var view = $$("details");
      view.setValues(this.getItem(id).person);
      view.config.masterId = id;
    }
  }
};

A click on an item from the list will populate the form with the data from the same dataset.

Next, let’s enhance the details form with a toolbar, that will contain a button for switching back to the master view without saving the input:

var details = {
  id:"details",
  view:"form", padding:0, minWidth:200,
  elements:[
    { view:"toolbar", cols:[
      { view:"label", label:"Details" },
      { view:"button", value:"Hide", width:100, click:function(){
        $$("master-view").show();
      }}
    ]},
    { padding:10, rows:fields },
    actions,
    {}
  ]
};

Finally, let’s put everything together again:

webix.ui({
  rows:[
    toolbar,
    {
      type:"space", rows:[{
        animate:false, cells: [
          { id:"master-view", margin:5, rows:[ grid, pager, {} ] },
          { id:"details-view", margin:10, cols:[list, details] }
        ]
      }]
    }
  ]
});

$$("details-list").sync($$("master"));

I’ve put the master and the details view into a multiview (this isn’t stated directly, but whenever you specify cells, not rows or cols, Webix knows that you mean to create a multiview). I’ve disabled the animation effect, but if you want to have it, don’t set the animate property at all. Finally, I’ve synced the master datatable and the details list.

Live demo >>

Nesting: Sane and Not Really Sane Use-Cases

Apart from using the master-details pattern for quite normal apps, you can try to create vast hierarchies of nested views, like this:

And who knows, maybe you will surpass Quintus Teal, and we’ll slip into the fourth space dimension through the UI of your app.

Conclusion

The master-details pattern is great for nearly any real-life app that works with lists of clients, products, etc, easy to master and implement. However, the devil’s in the details. You’ve seen how, by changing the latter, we moved from quite awkward and fragile apps to something more and more useful; how we perfected the app and, finally, plunged into a bit of insanity.

Useful links for further reading:

The post Master-Details Pattern with Webix UI appeared first on Webix Blog.

In-App Navigation Patterns

$
0
0

When the content of your web app gets bigger and bigger, it’s a good idea to split it into multiple screens. This, in its turn, demands convenient navigation, so that users could quickly find what they need. User experience isn’t supposed to turn into the quest for the Holy Grail.

Numerous navigation patterns have been designed to achieve this honorable goal. So if you are looking for ways to make your apps more handy and good-looking, join me on my ship, and together we’ll find our way in the sea of In-App Navigation and visit four remarkable islands of the Webix Archipelago: Sidebar, Tabbar, Top Menu, and Winmenu.

navigation design patterns

Modern Approach – Sidebar

Ahoy there! Our first stop is the Isle of Sidebar, which can offer you a modern and handy approach to designing in-app navigation. Sidebar provides comfy navigation in limited space, as quite a lot of options can be placed onto its vertical panel. Moreover, Sidebar is suitable for screens of all sizes: if you develop mobile apps, you can use the collapsible version of a sidebar. But enough words, let’s take a closer look at the example.

Sidebar as navigation design pattern

To be able to use all the treasures of Sidebar, you can include its files by setting links to Webix CDN:

<link rel="stylesheet" href="//cdn.webix.com/components/sidebar/sidebar.css"
   type="text/css" media="screen" charset="utf-8">
<script src="//cdn.webix.com/components/sidebar/sidebar.js"
   type="text/javascript" charset="utf-8"></script>

After you do this, you can effortlessly initialize a sidebar and populate it with data:

webix.ui({
  type:"wide", cols:[
        {
          view:"sidebar", id:"sidebar", data:menu_data,
          ready:function(){                            
            this.select("home")
          }
        },
        {
          //main view
        }
      ]
});

As a result, we have a fully functioning sidebar that awaits your content.

Live demo >>

Off we sail now to our next destination.

Older, but Still Hip Classic – Tabbar

We have reached the great Isle of Tabbar, that can boast older traditions. Tabbar provides web developers with classical approach to designing in-app navigation which is still relevant and will probably be such in the nearest future.

Top Tabbar

First let’s explore the northern part of the island – Top Tabbar.

Top Tap bar as navigation design pattern

This navigation pattern is also handy and helps save screen space, though the number of tabs is limited: the more tabs you have, the smaller their headers become. The tab headers that don’t fit can be put to a popup. However, in this case users won’t be able to see all the headers at once, and the navigation won’t be so convenient anymore.

Okay, here’s the code for initializing a top tabbar:

webix.ui({
  type:"space", margin:0, rows:[
    { view:"tabbar", id:"tab", options:menu_data, multiview:true },
    { cells:[
      //cells with the content of the multiview
    ] }
  ]
});

For tabbar navigation, you have to enable multiview and afterwards create the multiview with arbitrary content.

Live demo >>

And now let’s pay a visit to Bottom Tabbar.

Bottom Tabbar

The only difference from the previous navigation pattern is that the layout with Bottom Tabbar has tabs at the bottom of the page, which is especially convenient for mobile apps.

Bottom Tabbar as UI navigation design pattern

Beside placing the tabbar below the multiview, I specified the "bottom" type of the tabbar, since by default it is top:

webix.ui({
  rows:[
    { type:"header", template:"Company Page"},
    { cells:[
      //cells with the content of the multiview
    ] },
    { view:"tabbar", type:"bottom", id:"tab", height:70, options:menu_data, multiview:true }
  ]
});

Live demo >>

As we sail through the Webix Archipelago, we’ve come to one of its oldest parts, and here is our next destination – the Isle of Top Menu.

Old, and Probably Not Very Trendy – Top Menu

Not so many web developers sail to Top Menu nowadays. This approach is a bit less handy than the previous ones, because the options are displayed in popups, which are pretty fragile. A single movement of the mouse pointer in the wrong direction can close the submenu. Besides, with this navigation pattern users don’t see their current position within the app.

Top Menu as navigation design pattern

I have placed the menu on a toolbar at the top of the page. Here is the code that defines the menu and loads it with data:

var menu = {
  view:"menu", id:"menu", subMenuPos:"bottom",
  data:menu_data,
  type:{
    subsign:true,
    height:40
  },
  on:{
    onMenuItemClick:function(id){
      $$("details").setValues(this.getMenu(id).getItem(id));
    }
  },
  ready:function(){                            
    this.select("home")
  }
};

subMenuPos sets the relative position of popups with submenus. The subsign property defines whether to display the submenu icon.

Live demo >>

Now it’s time to say goodbye to Top Menu and sail to the last island.

Non-Standard UI – Winmenu from Components

On the outskirts of the archipelago, there is an extraordinary place – the Isle of Winmenu. It can offer an approach designed for developers who want to try something uncustomary. For example, if you want to recreate Windows 8 Start menu, this approach can offer exactly what you need.

Windows 8 Start Menu as ui navigation pattern

To get access to the riches of Winmenu, link to its files on Webix CDN first:

<link rel="stylesheet" href="//cdn.webix.com/components/winmenu/winmenu.css"
   type="text/css" media="screen" charset="utf-8">
<script src="//cdn.webix.com/components/winmenu/winmenu.js"
   type="text/javascript" charset="utf-8"></script>

Each menu option has two compulsory properties (value and img) and a number of optional ones. You are to set them, when you define data for Winmenu:

var menu_data = [
  { value:"Home", color:"#ffcc66", img:link+"home.png", x:1, y:1, width:2, height:2 },
  { value:"Projects", color:"#33cc33", img:link+"12.png", x:3, y:1},
  //other options
];

In the x and y properties I specified the relative position of each menu option, and for some of the options I set larger size parameters.

Now I will initialize Winmenu:

var menu = {
  view:"winmenu", data:menu_data,
  xCount:4, yCount:3,
  on:{
    onItemClick:function(id){
      webix.message(this.getItem(id).value);
    }
  }
 };

webix.ui({
  width:800, height:480,
  padding:30, rows:[menu ]
});

Pay attention to the xCount and yCount properties, that set the size of Winmenu in special measurement units equal to the number of the small option panels that could fit within the menu borders.

Live demo >>

As you can see, this navigation pattern is modern-looking, convenient and colorful and can be a good alternative to more traditional approaches.

Conclusion

It’s time to end our voyage, guys. I thank you for joining me and I hope that the voyage has been full of pleasant and curious discoveries. Together we’ve looked at several approaches to making web apps easier to navigate: Sidebar, Top and Bottom Tabbar, Top Menu and, finally, Winmenu.

Navigation patterns evolve, and only the sky’s the limit, so maybe very soon a better approach will appear and will become the new black of in-app navigation. What do you think? On that note I say goodbye to you. May you have many a victorious voyage in the JavaScript Ocean. See you later!

Meanwhile, you can consider some related texts to read:

The post In-App Navigation Patterns appeared first on Webix Blog.

Creating a Gmail-like Interface with Webix

$
0
0

An app logic well done is only half a job accomplished. The other half is designing such a UI that would enhance the functionality of the app and make it rational and easy to navigate. Besides, it’s important to create a nice-looking interface to make apps recognizable and visually appealing.

Gmail has one of the most recognizable interfaces on the web. It’s neat and clean and can be used to rationally organize the massive app contents. In fact, Gmail UI can be used not only for email management but for other tasks as well. If you would like to know how to recreate such an interface and don’t know where to begin (or in case you are just curious), follow me. I’ll show you how to implement Gmail’s approach to designing interfaces with the Webix UI library.

Gmail-like interface with Webix

Designing the Layout

The first thing is planning the layout. Basically, the whole Gmail page consists of four main parts. So, let’s create the following panels:

  • a top panel topHeader for the logo and avatar;
  • an app panel appHeader that will be a toolbar with buttons and other controls;
  • a sidebar panel for navigation;
  • a tabs panel infoTabs for displaying lists of data.

Gmail interface layout with Webix

Next, let’s change the height and width of the panels according to the planned contents:

Gmail interface layout resized with Webix

Live demo >>

Creating a Sidebar

Let’s begin with designing the sidebar.

Top Options

First, let’s place a short List of email categories on the sidebar:

var topOptions = {
    rows:[
        { view:"list", select:true, data:categories, scroll:false, autoheight:true }    
    ]
};

Gmail UI has a big Compose button placed above the list. Let’s add a similar button as well:

var topOptions = {
  rows:[
    { view:"button", value:"Compose"},
    //list
  ]
};

At this stage the top part of the sidebar looks like this:

Gmail interface sidebar options with Webix

It looks OK, but it could look better with a bit of built-in CSS. Let’s align the button and style it. button_raised is a Webix CSS class for 3D-styled buttons, that will give the button the desired Gmail-like look.

inputWidth: 120, css:"button_raised"

Next let’s give them more space with the padding and the margin, so that the contents didn’t stick to the left border:

var topOptions = {
  padding: 10, margin:10, rows:[ /* button and list */ ]
};

Have a look at the sidebar now:

Gmail interface sidebar options styled with Webix

Bottom Tabs

The list of categories doesn’t take much space on the sidebar, so you can place something useful below it. In Gmail, you can see a contact list and other extra features there. Let’s do the same and create a MultiView for displaying this element of the UI.

Note that in Gmail UI the bottom tabbar, not the top tabbar, is used. The sidebar definitely looks better if the tabbar doesn’t clash with the list above:

Gmail interface sidebar tabs with Webix

var bottomTabs = {
  type:"clean", rows:[
    { animate:false, cells:[
      { id:"a1", template:"Contacts" },
      //...
    ]},
    { view:"tabbar", value:"a2", type:"bottom", multiview:true, options:[
      { id:"a1", value:"<span class='webix_icon fa-user'></span>" },
      //...
    ]}
  ]
};

For a fast change of contents in the multiview, the animation effect is switched off. To make all the three tabs visible on the sidebar, let’s make them narrower:

{ view:”tabbar”, ..., tabMinWidth: 50, ... }

Next, change the color of the icons on the tabs:

.webix_item_tab .webix_icon{
  color: silver;
}
.webix_item_tab.webix_selected .webix_icon{
  color: #222;
}

You probably noticed that when the Gmail tabview renders no contacts or chats, real contents are substituted by plain center-aligned text. Let’s create similar templates for the example:

{ id:"a1", css:"draft", template:"<p class='title'>Contacts</p>" },...
.draft .webix_template{
  display: table;
  width: 100%;
}
.draft .webix_template .title{
  display:table-cell;
  text-align:center;
  vertical-align:middle;
}

Let’s put everything together and you’ll see the completed sidebar:

Gmail interface sidebar styled with Webix

Live demo >>

Initializing the Top Panels

The Top Panel

The top panel is a suitable place for logos, avatars, and other main app info. If there’s enough space and you feel like it, you can place a couple of handy controls there, just like it’s done in Gmail.

First, let’s place a logo and a user avatar on the left part of the panel:

var topHeader = { type:"clean", cols:[
  { template:" ", css:"logo", height: 60, width:200 },
  {},
  { template:"<div class='avatar'>M</div>", css:"user_info", width:60 }
]};

The avatar is actually a div with a white letter on a colored background.

gmail interface top panel logos with Webix

As you see, there’s a lot of space left, so let’s use it for a search control and a small icon button. By the way, on a white background, the search control may look better with only the bottom border. You can style the search control with your own CSS class, but a suitable class already exists within Webix. To use it, let’s create a form view instead of a simple layout and place the controls inside:

var searchBar = { view:"form", paddingY:10, paddingX:0, margin:0, cols:[
  { view:"search" },
  { },
  { view: "button", type:"iconButton", icon:"bell-slash", width:50 }
]};

Have a look at the top panel now:

gmail interface top panel styled with Webix

The App Panel

The app panel is the toolbar of the app. In Gmail, most of the controls are placed there. Besides, they are divided into two groups and put onto the opposite sides of the toolbar. Let’s create a similar toolbar:

var appHeader = { cols:[
  { view:"button", value:"Mail", width: 200 },
  { view:"button", type:"iconButton", icon:"check-square-o", width:50  },
  { view:"button", type:"iconButton", icon:"history", width:50 },
  { view:"button", value:"More:", width:90  },
  {},
  { view:"label", label:"<b>1-50</b> of <b>2300</b> ", align:"right" },
  paging,
  { view:"button", type:"iconButton", icon:"arrows-alt", width:50 }
]};

Without any additional styling the toolbar looks like this:

gmail interface toolbar with Webix

Let’s change the style of the buttons and make them more recognizable. This time, it’s relevant to use another Webix CSS class for buttons — webix_el_button:

.webix_el_button.button_silver.button_raised button{
  color: #666;
  border: 1px solid #dcdcdc;
  background-image: linear-gradient(to bottom,#f5f5f5,#f1f1f1);
  text-transform:none;  //no upper case
}

The Mail button placed above the sidebar deserves a special style:

.webix_el_button.big_button button{
  font-size: 20px;
  text-transform:none;  //no upper case
}

gmail interface toolbar styled with Webix

Live demo >>

Designing the Main Panel

Finally, it’s time for the last panel — the main view for displaying long data lists, whether it’s email, orders or goods in a shop. So let’s create a scrollable DataTable without headers:

var mailTable = {
  view:"datatable", scroll:"y",
  columns:[
    { id:"check", template:"{common.checkbox()}", width: 40 },
    { id:"star", template: function(obj){
      return "<span class='webix_icon orange_star fa-star"+(obj.star?"":"-o")+"'></span>"
    }, width:40 },
    { id:"from", width: 150 },
    { id:"subject", fillspace:true },
    { id:"date" }
  ], header:false, data:/* data_source */
};

gmail interface data list with Webix

If you want to structure your data further and display them in separate lists like it’s done in Gmail, you can create a multiview with a top tabbar. To improve the design, you can add a light-gray margin around each tab by changing the type of the layout to space:

var infoTabs = { view:"tabview",
  tabbar:{ optionWidth:200 },
  cells:[
    { header:"Primary", body: { rows:[ mailTable ],  type:"space"}},
    { header:"Social",  body: { rows:[ socialTable ], type:"space"}},
    { header:"Updates", body: { rows:[ updatesTable ],  type:"space"}}
]};

gmail interface final with Webix

The data for the example is generated by a special function generateData().

Live demo >>

Conclusion

Gmail interface is not the only one worthy of being noted, though it’s one of the most widely-known UIs in the world. This quite simple and clear approach to design can be used for a number of web apps, moreover, it’s not difficult to recreate and enjoy. The example with Webix can be further modified and used for real life apps.

If you are interested in further reading on UIs, consider these articles:

The post Creating a Gmail-like Interface with Webix appeared first on Webix Blog.

How to Create Custom Report Engine with Webix Spreadsheet

$
0
0

Software can make your routine simpler and with better results. For instance, making clear and good-looking reports would turn into a real battle without a helpful tool. One of these tools is Excel, a well-known mega-beast of valiant deeds and immortal glory. Yet, if business-warriors have to make some custom report, it’s developer-warriors who get their share of the job. This might be a problem. Every time you have to involve extra people in routine procedures, it’s costly and time-consuming (to say nothing of the fact that developer-warriors have a lot of other battles to fight).

create custom report

It would be good to design a report engine — another mega-beast that is as powerful and helpful. The complex widget SpreadSheet, one of the components of the Webix UI library, is a relevant core element for a decent mega-beast to win the deathmatch and save your end-users from extra expenses. Join me, developer-warriors, and I’ll show you how to enhance SpreadSheet and make it serve business-warriors well in their battles. Together we’ll create a spreadsheet-based report engine that will allow your end-users to make a custom report themselves by choosing templates from a list and editing them to their liking.

Create report engine with SpreadSheet

Constructing a Skeleton

Let’s begin creating the report engine with the skeleton of our beast. It’ll have two flanks, the overall design is this:

Constructing a Skeleton of report engine with Webix SpreadSheet

Let’s begin with the left flank, which will contain a list of shop names. Later I’ll load the list with data that contains statistics for these shops. For now, there will be just an empty list:

var departments = {
  view:"list", scroll:false, borderless:true,
  id:"departments",
  width:200,
  select:true
};
var listPure = {
  view: "form", id:"right",
  rows:[
    { view: "label", label: "Select a shop" },
    departments
  ]
};

Let’s move on to the bigger right flank with a toolbar and a spreadsheet area. Here’s the toolbar with a richselect that will have a list of reports and a button for switching between edit and readonly modes:

var toolbar = {
  view:"toolbar",
  cols:[
    { view:"richselect", labelAlign:"right", label:"Reports",  
      id:"reports", options: [], width: 320 },
    {},
    { view:"button", value:"Edit", width: 100, id:"edit" }
  ]
};

The spreadsheet area at this point will contain an empty datatable, which is to be expected. Here’s the whole right flank:

var reportPanel = {
  rows:[
    toolbar,
    { id:"ssheet", view:"spreadsheet", readonly:true }
  ]
};

After putting all the bones together, we have the skeleton:

webix.ui({
  type:"space", cols:[
    listPure,
    reportPanel
  ]
});

The skeleton of our beast is ready, so let’s move on to the next step.

Augmenting the Skeleton with some Muscle and Brain

Let’s give the beast the first bit of knowledge — data for displaying reports. I have the data in a JSON file (data/reports.json), the contents of which is an array of three objects. Each object contains data and styles for a single spreadsheet, e.g.:

{ "id":1, "value":"Sales", "sheet":{
 "styles":[
   ["wss3",";#efefef;;;;;;;;;;;;;;"],...
 ],
 "data": [
   [1, 1, "Sales 2016", "wss7"],
   [2, 1, "Store", "wss4"], ... ,
   [3, 1, "={{value}}", "wss3"],[3, 2, "={{janinc}}"], ...
 ],
 "spans": [
   [1,1,14,1]
 ],
 "sizes":[
   [0,1,225]
 ]
}},

Mind that instead of real values in data I specified placeholder properties, for instance, “={{value}}”. During the last step, I’ll load real data into them.

Now let’s load the data into a DataCollection:

var datasource = new webix.DataCollection({ url:"data/reports.json" });

Our beast will show three different reports, but in real tasks a number of reports can be much bigger. That’s why, instead of creating separate spreadsheets for each type, let’s define a function that will recreate the same spreadsheet with different parameters every time another type of report is chosen:

function showSpreadsheet(id, readonly){
  webix.ui({
    view:"spreadsheet", readonly:readonly, id:"ssheet",
  }, $$("ssheet"));

  if (id)
    $$("ssheet").parse(datasource.getItem(id).sheet);
};

The first parameter is the ID of a report from the DataCollection, the second one will be needed later when you’ll teach the beast to edit reports (for now I’ll always set this parameter to true). The second parameter of the webix.ui constructor is the view to be replaced by the first object parameter. Therefore all you need to specify during its first initialization is the id of the spreadsheet view:

var reportPanel = {
  //...
    { id:"ssheet" }
  ]
};

Now it’s time to add some meaning to the richselect: an event handler that will recreate the spreadsheet with new data if another report in the richselect is chosen.

var toolbar = {
  View:"toolbar", cols:[
    { view:"richselect", labelAlign:"right", label:"Reports", id:"reports", options: [], width: 320,
      on:{
        onChange:function(){
          showSpreadsheet(this.getValue(), true);
        }
    }},
    //...
  ]
};

Finally, let’s initialize the spreadsheet and load the data. The promise object datasource.waitData makes sure that our beast will act only when it gets the data.

datasource.waitData.then(function(){
  showSpreadsheet(0, true);
  $$("reports").getList().parse(datasource);
  $$("reports").setValue(1);
});

At this point, the app doesn’t show any real data, only three templates for reports:

Augmenting the Skeleton of reporting in SpreadSheet

Teaching the Beast to Edit

Good. Now the app allows choosing a report type from a list, but you cannot edit reports, not yet. It’s the time to change that. Let’s begin by modifying the showSpreadsheet() function:

function showSpreadsheet(id, readonly){
  if (readonly != $$("ssheet").config.readonly){
    webix.ui({
      view:"spreadsheet", readonly:readonly, id:"ssheet"
    }, $$("ssheet"));
  }
 
  if (readonly){
    $$("ssheet").hideGridlines(true);
    $$("ssheet").hideHeaders(true);
  }
  //parse data
};

Now the function really uses the readonly parameter: first, it reinitializes the spreadsheet with a different mode according to the value of readonly, and second, hides unnecessary for the readonly mode grid lines and headers. Be careful to put the code of the latter before parsing the data, because otherwise hiding headers will interfere with the correct rendering of the data.

Let’s change the toolbar. Remember that Edit button? This is the moment to program it for some action. Let’s add a property to it:

{ view:"button", value:"Edit", width: 100, id:"edit", sheetState:true }

As the spreadsheet is initialized as readonly: true, so will be the sheetState property. When you click Edit, the state will change to false. Let’s add an event handler to the button:

click:function(){
  var readonly = this.config.sheetState = !this.config.sheetState;
  var reportId = $$("reports").getValue();
  //recreate report view
  showSpreadsheet(reportId, readonly);
}

Apart from displaying spreadsheets in the edit mode, the function loads data from a corresponding placeholder object for editing. For saving edited reports, there’s no need to define another button. Let’s just add a few lines of code to the above handler:

click:function(){
  var readonly = this.config.sheetState = !this.config.sheetState;
  var reportId = $$("reports").getValue();

  if (readonly){
    //save data
    datasource.updateItem(reportId, { sheet:$$("ssheet").serialize() });
  }
  showSpreadsheet(reportId, readonly);
  //change the label on the button
   this.config.label = readonly ? "Edit" : "Save";
   this.refresh();
}

Depending on the mode of the spreadsheet, the same button will act as either the Edit or Save button.

The readonly mode for the richselect should be defined by the state of the Edit button as well. This is necessary if a user switches between reports without exiting the edit mode:

var toolbar = {
  //...
    { view:"richselect", //...config
      on:{
        onChange:function(){
          showSpreadsheet(this.getValue(), $$("edit").config.sheetState );
        }
    }},
    {},
    //the Edit btn config
  ]
};

At this point you can switch between modes, edit reports, and save changes.

Creating report with SpreadSheet step by step

Giving the Beast More Knowledge

ERROR looks so hopelessly wrong and disappointing. It’s time to provide the mega-beast with the real placeholders for spreadsheet data. The placeholders are in the shops.json file in the same data folder. Let’s load them into the list on the left panel and enable selection of right placeholders for every shop on the list:

var departments = {
  view:"list",
  //...
  on:{
    onAfterSelect:function(id){
      $$("ssheet").setPlaceholder(this.getItem(id));
    }
  },
  url:"data/shops.json"
};

To keep the same placeholders when another report type is chosen, let’s add a few lines of code to the showSpreadsheet() function:

function showSpreadsheet(id, readonly){
  //...
  var placeholders = $$("departments").getSelectedId();
  if (placeholders)
    $$("ssheet").setPlaceholder($$("departments").getItem(placeholders));
};

Finally, let’s ensure that the data will be loaded correctly when ready, and select the first placeholder on initialization:

Promise.all([$$("departments").waitData, datasource.waitData]).then(function(){
  //...
  $$("departments").select(1);
});

As you see, now we iterate through two promises from both the reports and the placeholders.

Now the app is ready to load, render, edit, and save the data into reports. Here the data is saved into a collection, but you can save it to a server, of course.

How to create report using SpreadSheet

Live demo >>

Conclusion

Great, the SpreadSheet mega-beast is ready for battles. It can load the data, edit, present it in different ways and hopefully make this world a better place. You can define your own reports and enhance the app further to let users create their own report templates and add them to the list. If you have ideas that you want to share with us, drop us a line below.

You can have a look at the source code and download it from the Github repository. I wish you and your end-users many a glorious battle, and may the SpreadSheet be with you.

For further info, you can check out the following related links:

The post How to Create Custom Report Engine with Webix Spreadsheet appeared first on Webix Blog.

Using Excel Files with Webix Widgets

$
0
0

As a rule, companies work with a lot of data and already use Excel files in their workflows. Among other things, business process automation requires shared access to files. For sharing, Excel files can be published as web pages or uploaded to web services like Google Docs. And still, there is a problem because such services do not allow you to embed spreadsheets into web applications and web sites. I’ll show you how Webix UI library can help you with this task.

Excel with Webix Widgets

Benefits of Using Excel Files

Excel is probably the most popular software in the world of business and finance. It can be used for complex statistical analysis, yet its interface is intuitive. A lot of people are used to Excel, some have been working with it for decades. So they are unlikely to switch to something completely different. Thus using Excel files along with web apps can be a good idea.

There are some problems, though. For instance, it’s difficult to keep track of the changes made by people who work with the same files. A solution is embedding Excel files into web apps to let users access them together and always get the latest edit. Webix allows doing this in several ways.

ExcelViewer

To let users view all or part of the data from Excel files in your applications, you can create ExcelViewer on the page. You can do it with just a few lines of code: specify the view type and set the URL with the path to the target file.

webix.ui({
  { view:"excelviewer",
    url:"binary->//webix.com/packages/articles/data.xlsx" }
});

To load data from Excel, you should use the binary proxy. binary is an object for loading and reading the contents of files as an ArrayBuffer.

To display the headers from your Excel files, add a property to the excelviewer view: excelHeader: true. Besides, if you want to display the list of the files alongside with the contents of the file, add an ExcelBar, which will create two bars – Data and Files:

webix.ui({
  rows:[
    { view:"excelbar", id:"bar1" },
    { view:"excelviewer", excelHeader:true, toolbar:"bar1",
        url:"binary->//webix.com/packages/articles/data.xlsx" }
  ]
});

Webix ExcelViewer

Live demo >>

SpreadSheet

Besides viewing the data, you can also enable editing just like in desktop applications. SpreadSheet might be a solution for you here. To be able to use it, you need to include SpreadSheet files first. After that, you can effortlessly initialize a spreadsheet and load data into it:

webix.ui({
  view:"spreadsheet",
  url:"binary->//webix.com/packages/articles/data.xlsx",
  datatype:"excel"
});

The data loading is very similar to that in ExcelViewer. Don’t forget to set the datatype property to “excel”, that’s all. Now your users can view and edit the Excel file.

Excel in Webix SpreadSheet

Live demo >>

Presenting Excel Data: Loading Data into DataTable

If you want to create your own UI, process the data and present it in your app in various ways, you can load it in different components. For instance, you can load the data into DataTable. This solution is also easy to implement:

webix.ui({
  rows:[
    { view:"datatable", datatype:"excel",
      url:"binary->//webix.com/packages/articles/data.xlsx",
      autoConfig:true
    }
  ]
});

The autoConfig property simplifies the configuration of the datatable, but if you use it, you might not like the way your data looks:

Excel in Webix DataTable

Formatting and Data Processing in DataTable

The good news is that you can decide for yourself. If the headers of the Excel file are meaningful words like “title”, you can make use of the columns property of DataTable. Alas, sometimes people who create the files are not so clairvoyant. Therefore you need to change these names and after that have the datatable to display them as headers. Besides, you might want to display only some of the columns. Meet datatable.scheme, which is meant to save the day:

scheme:{
  $init:function(obj){
    obj.title = obj.data0;
    obj.year = obj.data1;
  }
}

By setting the $init property, you can change the initialization of the datatable. For instance, only two columns will be shown with different headers here. Now you can set the columns:

columns:[
  { id:"title", header:"Title", fillspace:true },
  { id:"year", header:"Date" }
]

Now there’s no need in displaying the headers in the first row of the datatable, so you can hide them. Change the url property for that:

    url:"binary->//webix.com/packages/articles/data.xlsx@Data[1]"

Here I added a delimiter and two parameters:

  • Data that tells the datatable which sheet to display;
  • [1] that specifies the rows for displaying (beginning from the second, thus omitting the headers).

Excel in Webix DataTable

In case you want to format dates before displaying them, it’s not a problem either. For instance, if you want to display the contents of the second column as dates in some particular format, e.g. mm/dd/yyyy, you can change the type of the column data:

    obj.year = new Date(obj.data1, 0, 1);

After that, change the format of displaying this column in the columns property:

    { id:"year", header:"Date", format: webix.i18n.dateFormatStr }

webix.i18n.dateFormatStr converts a Date object to a string in the default date format. Other ways can be chosen as well.

formatting Excel data in Webix DataTable

Live demo >>

Reading Data from Excel with Arbitrary Code

You might also want to load the data and process it in some other way without displaying it. You can load, then parse the data, store them in a JSON variable and use it in your app. Make use of webix.ajax(), which returns a promise, and create a whole chain of promises:

webix.ajax()
  .response("arraybuffer")
  .get("//webix.com/packages/articles/data.xlsx")
  .then(function(data){
    return webix.DataDriver.excel.toObject({ data, options:{ ext:"xlsx"}});
  }).then(function(parsed){
    var sheet = webix.DataDriver.excel.getSheet(parsed, { name:"Data" })
    webix.message("Data in 5th row - " + sheet.data[5].join(", "));
  });

In the code above, first I specified the format of a response, then sent the GET request. When the promise resolves with success, the data will be parsed and then stored in a variable, which can be used further.

Live demo >>

Exporting Data to Excel

Apart from letting users view Excel files, you might want to let them save the data back to Excel. Webix has the toExcel() method for exporting data that can be applied to all data components. The method takes the ID of the component or its object variable as a parameter. Let’s add this functionality to the datatable:

webix.ui({
  rows:[
    { view:"button", value:"Export", click:function(){
      webix.toExcel($$("d1"));
    }},
    { id:"d1", /* same old datatable */ }
  ]
});

Live demo >>

Users may need to select specific columns, format them and unite some columns into a new one with some custom name. You can do that by providing a second parameter for toExcel(): an object with needed datatable configuration.

Export of Any Arbitrary Data

You can export any data to Excel, not just data from visual components. For example, the data can be stored as a JS array. To send it to Excel, you can parse it into DataCollection, a mixin for storing non-hierarchical data, and apply the method to this collection:

webix.ui({
  rows:[
    { view:"button", value:"Export", click:function(){
      var d = new webix.DataCollection();
      d.parse([
        ["Alex Brown", "72", "1144-55" ],
        ["Donna Marlou", "67", "1187-52" ]
      ], "jsarray");
      webix.toExcel(d);
    }}
  ]
});

Live demo >>

You can also format the data. For example, you can provide headers and set colspans:

webix.toExcel(d, {
  columns:[
    { id:"data0", header:"Name", width:200 },
    { id:"data1", header:"Age", width:100 },
    { id:"data1", header:"HD Number", width:100 }
  ]
});

Live demo >>

With toExcel(), you can export data from other data components (e.g. List, Tree, TreeTable) and change the format of the data in a lot of ways. Tabular data can also be exported to CSV.

Conclusion

Using Excel files in web applications is great if you process a lot of tabular data and need to share and co-edit it with your colleagues quickly and effectively. Such widgets as ExcelViewer could be a good solution for sharing, and SpreadSheet can be a tool for editing that follows a familiar Excel pattern. Further data processing can be achieved by loading and exporting data to and from other widgets. Besides, you can use the data from Excel in any other way with JavaScript. You can also export any data back to Excel files, including data from widgets.

Apart from working with Excel files, Webix provides export and import from/to PDF files:

You can also export charts or other visual data as PNG-images:

The post Using Excel Files with Webix Widgets appeared first on Webix Blog.


Creating your own Trello. Step 1: Building UI

$
0
0

This week’s adventure is another visit to the kingdom of the Most Recognizable User Interfaces. Trello provides one of the best interfaces for task management. A single glance reveals all the latest tasks, who’s working on them and what tasks are planned. Let’s look at how the same interface can be created with Webix UI library.

The kingdom is populated by UI architects and builders, so during this trip, you’ll watch the construction of a small but amazing castle. The main unit for the castle will be Webix Kanban. You can see the live demo and get the blueprints from GitHub. And now the adventure begins.

create your own trello alternative with webix

Setting Up the Frame: Layout

The first thing is constructing the layout. At the end of this part of the tour, you’ll see a frame like this:

the layout of apps like trello with webix

Every architect needs tools and building materials. You can get them by including Webix files into index.html. The styles and logic will be kept in separate files. Let’s include them into the page as well:

<script type="text/javascript" src="./app.js"></script>
<link rel="stylesheet" type="text/css" href="./app.css">

The foundation is ready. Let’s move on to creating panels for the UI components. Here are two toolbars for controls:

var toolbar = {
    css:"draft", template:"<span class='title'>Toolbar</span>", height: 40
};
var subbar = {
    css:"draft", template:"<span class='title'>Subbar</span>", height: 40
};

Next come the main unit for task boards and the side menu that later on will be toggled:

var boards = {
    css:"draft", template:"<span class='title'>Boards</span>"
};
var menu = {
    css:"draft", template:"<span class='title'>Menu</span>", width: 340
};

The CSS classes applied to the frame are created purely for the demonstration at this stage. You can find them in app.css. Next, the pieces of the frame are put together:

webix.ready(function(){
  webix.ui({
    rows:[
       toolbar,
       { cols:[
         { rows:[ subbar, boards]},
         menu
    ]} ]
  });
});

You can check out the complete blueprints for this step on GitHub.

Raising the Towers: Toolbar and Basic Styling

The kingdom of User Interfaces is a curious one. You can begin building castles even from the towers after the frame and foundation are ready. Look, this is how the Top Toolbar tower is set up and filled with necessary controls:

var toolbar = {
    css:"topbar", padding:1, view:"toolbar", height:40, cols:[
        { view:"button", type:"icon", icon:"arrow-left", width: 40 },
        { view:"button", type:"icon", icon:"dashboard", label:"Boards", width:100 },
        { view:"search", width:300 },
        { view:"label", label:"<a href='https://webix.com/kanban/'>Webix Boards</a>"},
        { view:"button", type:"icon", icon:"plus", width: 40 },
        { view:"button", type:"icon", icon:"question-circle-o", width: 40 },
        { view:"button", type:"icon", icon:"bell-o", width: 40 }
    ]
};

Next, the toolbar needs a change of style and the whole frame needs coloring. Look at the castle now:

webix toolbar for a trello alternative

You can find the stylesheet on GitHub. Let’s have a closer look at the styles for toolbar buttons. A 3D effect is created with the help of a transparent gradient:

.webix_layout_toolbar.topbar button{
    color: #fff;
    text-align: center;
    border-radius: 3px;
    background: linear-gradient(rgba(255, 255, 255, 0.3) 0px, rgba(255, 255, 255, 0.2) 100%);
}

To render the graphic effect of interacting with buttons, different gradients are added to pseudo-classes hover and active:

.webix_layout_toolbar.topbar button:hover{
    background: linear-gradient(to bottom,rgba(255,255,255,0.4) 0,rgba(255,255,255,0.3) 100%);
}
.webix_layout_toolbar.topbar button:active{
   background: linear-gradient(to bottom,rgba(255,255,255,0.45) 0,rgba(255,255,255,0.35) 100%);
}

Complete blueprints for the step are available, feel free to check them out for more details.

The Subbar Tower

Now that the upper tower is ready, the Subbar tower will be built. These are the controls that are placed on the subbar:

var subbar = {
    css:"subbar", padding:1, view:"toolbar", height:40, cols:[
        { view:"label", label:"<h3>App Roadmap</h3>", width: 150 },
        { view:"button", type:"icon", icon:"star-o", width: 28 },
        { view:"button", type:"icon", icon:"briefcase", label:"Private", width:72 },
        {},
        { view:"button", type:"icon", icon:"ellipsis-h", label:" Show menu", width:100 }
    ]
};

After brightening up with CSS, the subbar looks just like planned:

webix toolbar as a subbar for trello alternatives

For more details, have a look at the blueprints for this step.

Constructing the Rose Window: Kanban Board

Necessary tools for the main unit are stored in Kanban files, so they are included into index.html as well.

Kanban board needs data, and it’s time to provide them. In real life, data are loaded from a server. For this tour, however, a JSON file will do. The data file contains an array of JSON objects, for instance:

[{
    "id": 1,
    "status": "new",
    "text": "Printing in PDF Viewer",
    "tags": "webix,docs"
}, {
    "id": 2,
    "status": "work",
    "text": "Google map: Possibility to draw data based on data item type",
    "color": "red",
    "tags": "webix"
},
//...other tasks
]

A Trello board usually has several panes (aka lists) for cards. The board of this castle will have lists for new tasks, for tasks in progress, for testing tasks, and the last one for accomplished work. For convenience sake, the architects defined a special function that will return lists:

function getList(id, header){
    return {
        borderless: true, headerHeight: 30, header: header,
        body: {
            view: "kanbanlist", width: 300, status: id
        }
    };
}

The function initializes a kanbanlist view and sets the status and the header for the view. The status parameter links a list to tasks in the data file, so each task object should have the status property. Now Kanban is initialized with four lists:

var boards = {
    view:"kanban", css:"kanbanarea", type:"space", cols:[
        getList("new", "Backlog"),
        getList("work", "In Progress"),
        getList("test", "Testing"),
        getList("done", "Done")
    ], url:"data.json"
};

For bigger castles with more projects and processes, boards usually have more than four lists. It would be swell to see them all, so a horizontal scroll is just what’s needed. Webix ScrollView suits well for that. Look how Kanban is enveloped into ScrollView:

var boards = {
    view:"scrollview", scroll:"x", body:{
        /* Kanban*/
    }
};

To render a nice-looking custom scrollbar instead of the standard browser scrollbar, the CustomScroll mixin is initialized before the whole castle initialization:

webix.ready(){
    webix.CustomScroll.init();
    webix.ui({
    rows:[
       toolbar,
       { cols:[
         { rows:[ subbar, boards]},
         menu
    ]} ]
  });
}

After that, style is applied to the board. Mainly, it includes shades of gray for lists and cards and no top padding for each card:

.webix_kanban_list_item{
  background: #e2e4e6;
  padding: 0px 7px 7px 7px;
}

trello kanban board for alternatives to trello with webix

For more details, the blueprints for the step are also available.

A Secret Door: Menu Toggle

A castle is not charming enough without a door to secret corridors. It’s time to hide the menu and let users open it by pressing a special button.

The menu can be hidden by setting the hidden property of the menu to true. Next, the builders are going to give the Show menu button the power to show the menu back to the world. To refer to the menu from code, the menu ID needs setting:

var menu = {
    width: 340, css:"menuarea", id:"sidemenu", hidden:true, type:"head",
    css:"draft", template:"<span class='title'>Menu Content</span>" }
};

menu similar to trello kanban board online with webix

And here’s the mechanism for the Show menu button:

var subbar = {
    view:"toolbar", /* style */ cols:[
        //other controls
        { view:"button", type:"icon", icon:"ellipsis-h",
            label:" Show menu", id:"show-btn", width:100, click:function(){
                $$("sidemenu").show();
                this.hide();
    }}]
};

In Trello, the menu is closed with a different button. Let’s add a header with this button on top of the menu:

var menu = {
    width: 340, css:"menuarea", id:"sidemenu", hidden:true, type:"head", rows:[
        { cols:[
            { label:"Menu", view:"label" },
            { view:"button", type:"icon", icon:"close", width: 30, click:function(){
                $$("sidemenu").hide();
                $$("show-btn").show();
            }}
        ]},
        { css:"draft", template:"<span class='title'>Menu Content</span>" }
    ]
};

The UI of the castle is ready, and you can download the blueprints from GitHub.

Live demo >>

Conclusion

This is the end of the tour. I have shown you the construction of a Trello-like UI. Now you can construct a similar UI for your project management apps. If you ever wanted to create your own Trello-like boards, hopefully, this tour is a good starting point. Besides, you can get all the blueprints from GitHub and play around with them. In one of the future tours, you’ll see how to create a backend for the app. See you later, and good luck!

The post Creating your own Trello. Step 1: Building UI appeared first on Webix Blog.

Greetings to New Webix 5.0 with Updated Jet Framework

$
0
0

Webix team is proud to introduce the newly released Webix 5.0. It includes the updated Webix Jet framework, three new widgets and some useful feature upgrades, such as export of widget styles to Excel. Enjoy!

Webix Jet updated to version 1.0

The new version of the Webix Jet framework allows to use Webpack-based toolchain and ES6. Coding is now simpler, while routing and view resolving are much more flexible. The updated Jet can reuse the code written for the previous versions of Webix Jet, so it should be rather easy to migrate to the new codebase.

Other benefits:

  • Smaller resulting code;
  • Better debugging;
  • Rich plugins ecosystem (due to rich functionality of Webpack plugins);
  • Ability to use Hash, URL or any custom router;
  • Ability to define custom URL to view mappings;
  • Ability to define load and unload guards;
  • Classic OOP approach without JS quirks.

You can get the new Webix Jet on GitHub or via npm package manager and dive into its documentation here.

GeoChart widget

Created with Google Maps API, GeoChart widget displays information about countries and regions based on the provided data. With this widget you can embed Google GeoCharts into your application while loading and processing its data using standard Webix API.

Live Demo >>

Hint widget

With the new Webix Hint widget you can guide your users through your website or application, which is especially useful in case of complex contents and multiple controls.

Live Demo >>

Vertical mode for Slider widget

Starting from the new version, the existing horizontal Slider can be placed vertically, so you can create your own sound equalizer with Webix 🙂 In addition, both horizontal and vertical sliders now feature a dynamic title that is moved together with a drag element.

Live Demo >>

Upgraded Excel Import and Export

With the new update, it is possible to output cell styles (such as colors, fonts, borders and alignment) when exporting data from Webix DataTable, Spreadsheet and Excel Viewer to an Excel file.

Live Demo >>

Similarly, when you load Excel files into the Spreadsheet or Excel Viewer, style information is imported alongside with data and the widgets play the same bright colors like in your Excel document.

And I cannot but mention the implementation of another frequently asked feature. From now on, you can export the data of several widgets into a single Excel file with multiple sheets.

Live Demo >>

Other significant updates

  • Loading CSV data to Spreadsheet;
  • Ability to select all options in the Multicombo widget with the related button;
  • Ability to sort data by multiple columns through QueryBuilder;
  • Compact data presentation mode for Organogram;
  • Improved accessibility and keyboard navigation in Datatable;
  • Extra math related API in SpreadSheet;
  • Improved WebWorkers support in Pivot.

The number of changes is too big for this article, so feel free to explore the release What’s New list.

Webix 5.0 is available for download, on CDN and via Bower, NuGet and npm package managers. Clients with an active license can get the new version from the Client Area or via npm. If you’re new to Webix, today is a good day to give it a shot by downloading the trial version. Your feedback and questions are welcome in the comment section or by email.

The post Greetings to New Webix 5.0 with Updated Jet Framework appeared first on Webix Blog.

iFact: Developing a Webix-based invoicing application

$
0
0

This is a guest post by Dragos Stoica.

iFact: Developing a Webix-based invoicing application

The initial task

My objective was to design and implement a simple application allowing me to manage my freelance business. The application is called iFact and is available on github. The basic activity consists in getting contracts with clients, performing the work or service and invoicing the client. Also, I needed to manage the invoice flow and to have a minimal dashboard for cash flow control. The volume of invoices is low: from one to a couple of invoices per month. The number of clients and contracts per client is low, it means 5 to 10 clients with 1-3 contracts per client, normally one single contract active at any given moment in time.

The primary entities are:

  • supplier or service provider – your data
  • a list of clients
  • one or more contracts per client

An invoice will combine the data from supplier, client and invoice details into one single PDF document. The life cycle of an invoice is new, due and paid. This can be monitored inside the application. The dashboard section allows you to have an overview of the cash flow (invoiced vs. paid) and to compare monthly invoiced sums for each year. Also, you can export in Excel the data in order to further process and analyze the data.

The criteria for choosing a UI library

From the beginning I aimed for a Single Page Web application that can be fully described in JSON format, no CSS or extra HTML. I searched for an out of the box fully functional framework, including: programmatic widget control, data processors, server-side integration, AJAX capabilities, and, most importantly, support, documentation, and code examples. I found only a couple of such frameworks: DHTMLX and Webix were up to the task. Webix was much better-suited for my needs.

Webix was also a better choice because of the Proxy component. I used CouchDB as a web server and database so the communication with the server side had to be customized. Integrating external libraries, such as PDFMake and SheetJS, was direct and natural. I do bly recommend Webix as the first choice for new web applications. For me, developing with Webix was fun, easy and rewarding. Getting almost instant result in a couple of lines of code is unbeatable.

Development process and challenges

The architecture is two-tier: Single Page Web application for the front end served from CouchDB which is both web server and NoSQL database. The communication mechanism is exclusively based on AJAX, this implies no page reloads.

The first challenge was to write the custom Webix Proxy allowing the communication with CouchDB server or similar, like IBM Cloudant. The CouchDB web server exposes the database via the REST API. Basic GET, PUT, POST, DELETE HTTP requests can be used to manipulate data. It is a NoSQL document oriented database that stores the information in JSON format.

Here’s my proxy for CouchDB created with webix.proxy. The response from CouchDB may be used for DHTMLX components as well. It was tested with DHTMLX Scheduler.

webix.proxy.CouchDB = {
   $proxy: true,

   //loading and saving patterns
   //...
};

The read operation from database is done via GET requests and the data is extracted from CouchDB via map-reduce functions called views. Views are written in JavaScript and stored as JSON documents inside CouchDB – those are called design documents. Here’s how I defined the loading pattern:

/* loading pattern for CouchDB proxy */
load: function(view, callback) {
   webix.ajax(this.source, callback, view);
}

I send a GET request to database/design_document/_list/[list_name]/[view_name]. This can be the url attribute of the widget or load string. The response from the server is an array of objects.

The insert and update operations are done via POST and processed by updates functions inside design documents. There are no delete operations in the application, but they can be handled in the same way as inserts and updates.

One important piece of information that has to be kept in sync between Webix and CouchDB is document’s _id and _rev fields. Those are special fields that are needed for all update operations. You may notice that after creation and modification of a document those special fields are updated before sending the data to the UI component.

So, this is the Save data pattern for update operations:

save: function(view, update, dp, callback) {
   if (update.operation == "update") {
       webix.ajax().header({
           "Content-type": "application/json"
       }).post(dp.config.url.source + "\/" + update.data._id, //your data must contain _id
           JSON.stringify(update.data), [function(text, data, xhr) {
               var msg = data.json();
               if ('action' in msg) { //the response will contain an action field
                   var item = view.getItem(update.data.id);
                   item._rev = xhr.getResponseHeader('X-Couch-Update-NewRev'); //getting _rev property and setting the value at data level
                   view.updateItem(update.data.id, item);
                   view.refresh();
               }
           }, callback]
       );
   }
}

The address is present as the save attribute of the widget and it points to an update function in a CouchDB design document. Update implies that the document exists in CouchDB and must have attributes _id and _rev, those are stored in the widget data also as hidden fields.

Insert operation is equivalent with creation of a new document in CouchDB. We must get back _id and _rev of the newly created document for further update operations. Have a look at the pattern for inserts:

save: function(view, update, dp, callback) {
   if (update.operation == "update") {
       /* update */
   }
   if (update.operation == "insert") {
       webix.ajax().header({
           "Content-type": "application/json"
       }).post(dp.config.url.source,
           JSON.stringify(update.data), [function(text, data, xhr) {
               var msg = data.json();
               if ('action' in msg) { //the response form CouchDB will contain action field
                   var item = view.getItem(update.data.id);
                   item._id = xhr.getResponseHeader('X-Couch-Id'); // getting the _id attribute from the response and adding it to the data of the widget
                   item._rev = xhr.getResponseHeader('X-Couch-Update-NewRev'); //getting _rev property and value for it at data level
                   view.updateItem(update.data.id, item);
                   view.refresh();
               }
           }, callback]
       );
   }
}

An example for a widget:

{
   view:"activeList",
   id:"customersList",
   ...
   url: "CouchDB->../../_design/globallists/_list/toja/customer/getcustomer",
   save: "CouchDB->../../_design/customer/_update/rest"
}

The design document _design/customer/_view/getcustomer containing the view – the map function, this will be called with GET and is the load part of the Proxy:

function(doc){
   if (typeof doc.doctype !== 'undefined' && doc.doctype == "CUSTOMER") {
       emit(null,doc);
   }
}

The design document _design/globallists/_list/toja containing the list – transforming the result of the view in an array of objects:

function (head, req) {
   // specify that we're providing a JSON response
   provides('json', function() {
       // create an array for our result set
       var results = [];

       while (row = getRow()) {
           results.push(row.value);
       }

       // make sure to stringify the results :)
       send(JSON.stringify(results));
   });
}

The design document _design/customer/_update/rest the update function – performing the insert and update operations, delete is also implemented here but not used. This is called by save part of the Proxy and a POST will be emitted.

The request method will determine the action:

  • PUT: Update existing doc
  • POST: Create new doc if doc._id is not present, or Update an existing document if doc._id is present
  • DELETE: Delete existing doc, CouchDB way 😉

The main part of the response (JSON):

{
   action: 'error' | 'created' | 'updated' | 'deleted',
   doc: new_doc
}

And here’s the update function itself. If the request type is PUT:

function(doc, req){
   var payload = JSON.parse(req.body);
   var fields = [ 'nume', 'NORG', 'CUI', 'TVA', 'adresa', 'banca', 'sucursala', 'IBAN'];

   if(req.method == "PUT"){
     //update document
       fields.forEach(function(elm, idx){
           doc[elm] = payload[elm];
       });
       return [doc,JSON.stringify({"action":"updated","doc":doc})];
   }

   //...other request types

}

If the request type is POST, the existing document will be updated and if nothing is found, a new one will be created:

if(req.method == "POST"){      
   if(doc === null){
       //Create new document
       var newdoc = {
           _id: req.uuid,
           doctype: "CUSTOMER"
       };
       fields.forEach(function(elm, idx){
           newdoc[elm] = payload[elm];
       });
     
       return [newdoc, JSON.stringify({"action":"created", "sid":req.id, "tid":req.uuid, "doc":newdoc})];
   }else{
       //Update existing document
       fields.forEach(function(elm, idx){
           doc[elm] = payload[elm];
       });

       return [doc, JSON.stringify({"action":"updated", "sid":req.id, "tid":req.uuid, "doc":doc})];
   }
}

If the request type is DELETE, the deleted document is kept in history and may be ‘undeleted’:

...
if(req.method == "DELETE"){
   doc._deleted = true;
   return [doc, JSON.stringify({"action":"deleted"})];
}
...

And, finally, for unknown requests it sends error with request payload:

...
return [null, JSON.stringify({"action":"error", "req":req})];
//end of function

Check out the complete code on GitHub.

This was the major challenge that I had to deal with. The rest of the application is based on Webix samples and documentation. A great thank you for the work done here by the Webix team!

From technical point of view, the application is hosted and served from a design document inside CouchDB. The deployment process consists in creating a design document called app and attaching all the necessary files to it. This can be done in a straightforward manner using ACDC tool. The deployment of iFact was done 100% with this tool. This way one can have both the application and data served from the database.

The app

Prerequisites:

  • install CouchDB and configure an admin user,
  • get ACDC tool in order to deploy the iFact application,
  • a web browser: Firefox, Chrome or Safari.

If you deploy it on the localhost, the most common address where you will find the application is:

    http://localhost:5984/ifact/_design/app/index.html

And you should get the login screen. In order to authenticate, please use the CouchDB username and password.

webix couchDB ifact login form

The landing screen presents your information – The Supplier:

webix couchDB ifact the supplier page

There is basic legal and fiscal information that is necessary for invoicing and getting paid. On the right side of the screen there is the invoice counter and some administrative functions: export all data as JSON, import data from JSON and synchronize with another instance like Cloudant. Also you can export the financial statement in Excel.

On the top left you will find a drawer menu that allows you to navigate to other pages.

webix couchDB ifact Drawer Menu

Next you will find the Clients and Contracts page that will allow you to manage the list of Clients and their associated Contracts.

webix couchDB ifact Clients and Contracts page

The main purpose of this application is to produce invoices, so on the next page we will find the appropriate interface. You may choose a template, the supplier bank account, the client, enter invoice details and preview the invoice before issuing it.

webix couchDB ifact Invoice

The newly created invoices will enter the New-(Due)-Payed life cycle. You can trace your invoices in the Payments view.

webix couchDB ifact client contracts view

The Dashboard view allows you to see how your business is performing and the cash flow.

webix couchDB ifact Dashboard view

Other Webix-based projects

I found Webix a couple of years ago. Before this project, I already have used Webix in a couple of small projects: microKanban, Sales Representative Manager, designEditor and as a teaching material at university for undergraduate CS engineers. All projects are open source and can be found on https://github.com/iqcouch

If you like working with Webix and want to contribute to the community, we are more than delighted to get you on our github team. We also have projects based on Go, Framework7 (mobile), PhoneGap/Cordova, and Java.

The post iFact: Developing a Webix-based invoicing application appeared first on Webix Blog.

Interactive Map with Webix GeoChart

$
0
0

New Webix 5.0 includes the GeoChart widget created with Google Maps API. GeoChart is a pretty neat service for creating map charts for presenting data associated with countries and regions. I’ll show you how to use the main features of the widget to create an interactive world map. If you are not yet familiar with the widget, I hope that GeoChart will become your faithful friend after I introduce it to you. If you already love GeoChart as much as I do, you will probably gain a better insight into things you can do with it.

Webix with Google GeoChart

Basic GeoChart to Display Data

First, let’s show data in the readonly mode. The layout of the app will have three panels:

  • the map
  • the side panel with controls
  • the bottom panel with data records

initial visualization GeoChart

Before initializing GeoChart, don’t forget to get your personal key.

A few lines of code will add GeoChart into the app. Here’s a simple map with small data:

webix.ui({
    view:"geochart",
    id:"map",
    // provide your own Google API key
    // https://developers.google.com/maps/documentation/javascript/get-api-key
    key:"AIzaSyAi0oVNVO-e603aUY8SILdD4v9bVBkmiTg",
    data:[
{id:1, country:'Germany', code:"DE", area:357,  population:83 },
      {id:2, country:'United States', code:"US", area:9834, population:323 },
      {id:3, country:'Brazil', code:"BR", area:8516, population:207 },
      //...
    ]
});

In real-life apps, data are more often loaded from a server. For the demo, I’ll create a DataCollection and sync the map with it. I’ll do this because I plan to show the same data on the GeoChart and as a list of data records below the map.

var mapdata = new webix.DataCollection({data:[
  {id:1, country:'Germany', code:"DE", area:357,  population:83 },
  {id:2, country:'United States', code:"US", area:9834, population:323 },
  {id:3, country:'Brazil', code:"BR", area:8516, population:207 },
//...

Note that GeoChart doesn’t have its own data source, as the data is taken from the DataCollection:

var map = {
    view:"geochart",
    id:"map",
    // provide your own Google API key
    // https://developers.google.com/maps/documentation/javascript/get-api-key
    key:"AIzaSyAi0oVNVO-e603aUY8SILdD4v9bVBkmiTg"
};

For data records, I’ll create a DataTable:

var grid = {
  view:"datatable", id:"grid", autoheight:true,
  scroll:false, autoConfig:true
}

Here’s the layout:

webix.ui({
  cols:[
    form,
    { view:"scrollview", body:{
      type:"clean", rows:[
        map,
        grid
      ]
    }}
  ]
});

form for now is just a template that reserves space for controls on the side panel. The map and the DataTable are enclosed into a scrollview to adapt the demo for resizing. For the same purpose, I disabled the default scroll of the grid.

To synchronize the GeoChart and the DataTable with the DataCollection, call sync:

$$("map").data.sync(mapdata);
$$("grid").data.sync(mapdata);

Live demo >>

GeoChart Modes

GeoChart has a lot of options that can change the look of the map. For example, there are three ways to mark areas with related data:

  • regions – colors areas of the map (used by default);
  • text – adds colored labels of different sizes;
  • markers – adds colored bubbles of different sizes.

JavaScript interactive map Webix GeoChart with markers

I can change the mode by setting the chart.displayMode property. For instance, to show bubbles on the chart, I’ll set the property to markers:

webix.ui({
  view:"geochart",
  //...config
    chart:{
      displayMode:"markers"
    }
});

If the data have several columns, the size of bubbles depends on the second numeric column – population in my data.

I can also change the mode dynamically with setDisplayMode(“markers”). For example, let’s add a segmented button that will switch the GeoChart to different modes. I’ll place the button on the side panel:

var chartForm = {
  view:"form", id:"chartForm", elements:[
    {view:"segmented", id:"modes", value:"Regions",  
      options:["Markers","Regions","Text"],
      click:function(){
        $$("map").setDisplayMode(this.getValue().toLowerCase());
    }}
  ]
};

Webix GeoChart modes

I’d like to be able to hide the side panel. That’s why I’ll put the form into Webix Accordion:

var form = {
  header:"Configure", body:{
    width:300, type:"wide", rows:[
      chartForm
    ]
}};

Legends and Colors

Let’s brighten up GeoChart. First, I’ll alter the color scheme of the legend and paint the map in the rainbow colors. To do so, I’ll set the chart.colorAxis property of GeoChart:

chart:{
  colorAxis: {  
    colors:['violet','indigo','blue','green','yellow','orange','red']  
  }
  //...
}

Of course, the colors attribute also accepts hexadecimal values. The color scheme of the legend can also be changed dynamically by setting config.chart.colorAxis. Let’s add other color schemes and a button to switch between them:

{ view:"segmented", id:"colors", value:"Rainbow",
  options:["Rainbow", "Volcano"],
  click:function(){
    var color = this.getValue();
    switch(color){
      case "Rainbow":
        $$("map").config.chart.colorAxis = {colors: ['violet','indigo','blue','green','yellow','orange','red']};
        $$("map").refresh();
      break;
      case "Volcano":
        $$("map").config.chart.colorAxis = {colors: ["#e7711c", "#4374e0"]};
        $$("map").refresh();
      break;
    }
}}

Don’t forget to refresh GeoChart after changing color schemes to re-render the chart with new colors.

Now I’ll change the background color with the chart.backgroundColor property. The color of areas with no data can be set with chart.datalessRegionColor.

Webix GeoChart with different colors - gif

Live demo >>

The legend of the map can be further configured and even hidden. For more details, check out the Google GeoChart API.

Regional and Country GeoChart

I also want my interactive map to show specific regions or countries. This can be done with the chart.region property. It accepts country codes like ‘US’ or ‘BY’, region number codes like ‘142’ (for Asia) and ‘world’. You can find the list of supported codes in Continent Hierarchy and Codes.

Apart from setting the initial regional config of GeoChart, I can change the displayed region dynamically with setRegion(“code”). Let’s add a radio control to the side panel. Radio will switch GeoChart from the world view to other continents:

{view:"radio", id:"regions", value:1, vertical:true,
  options:[ {id:1, value:"World"}, {id:2, value:"Europe"} ],  
  click:function(){
    var region = $$("regions").getValue();
    switch(region){
      case "1":
        $$("map").setRegion("world");
        break;
      case "2":
        $$("map").setRegion("150");
        break;
}}}

Webix with Google GeoChart region Europe

Tooltips

In addition to visual presentation, Webix GeoChart has HTML tooltips that show details on each area in the dataset. Tooltips appear when users hover a mouse pointer over a region or a bubble. By default, tooltips show a list of all the data columns. I can change the default content of tooltips by enumerating the columns I want to show, e.g. with this code the tooltip will show only area:

tooltip:"<i>Area</i>: #area#"

Besides, I can style tooltips. I’ll do this by accessing the .google-visualization-tooltip class:

.google-visualization-tooltip {
    background-color: lightblue;
    border: 1px solid #ccc;
    box-shadow: 0 2px 2px 0 rgba(204, 204, 204, 0.6);
}

Webix GeoChart a different tooltip

You can check out this useful post on Stack Overflow. You will find more detailed advice on styling tooltips there.

Data with Multiple Columns

In the case of data where items are compared by several parameters, for example, by area and population, I can choose which parameter to display on the map. The rest of the columns can be displayed in tooltips. To get this behavior, I’ll change the order of the fields in the columns property. Let’s make population the column that will define chart colors. Have a look:

var map = {
    view:"geochart",
    id:"map",
    key:"...",
    columns:["country", "population", "area"],
    //...
}

Webix GeoChart with population map

The size of bubbles now depend on the area.

Live demo >>

Interactive GeoChart

Interactivity in Webix GeoChart is enabled by default. I can click on regions and bubbles. If there are related data, the area will be highlighted. I will expand interactivity further with the onItemClick event. E.g. let’s select a row in the DataTable with data on the same area:

var map = {
  view:"geochart",
  id:”map”,
  key:"...",
  on:{
    onItemClick:function(id){
      $$("grid").select(id);
    }
  }
};

onItemClick fires when you click on a colored region or a bubble, meaning areas with data.

I want to do something when I click any area, including those without any data. One more click event is available – onRegionClick. This event fires only in the regions mode. For example:

var map = {
    view:"geochart",
    id:"map",
    key:"...",
    on:{
        onRegionClick:function(obj){
          webix.message(“Region code ”+obj.region);
        }
    }
};

The event handler receives an object with the region code. In the next step, I’ll show you a more useful example of handling these events.

Using GeoChart to Edit Data

As GeoChart is a data component, I can load, edit, and remove data from it just as with other data components. Let’s add a form for editing, adding and deleting GeoChart items:

var editForm = {
    view:"form", id:"editForm", elements:[
      { view:"combo", name:"country", options:countries },
      { view:"text", name:"area", label:"Area" },
      { view:"text", name:"population", label:"Population" },
      { view:"button", value:"Add/Update Item" },
      { view:"button", value:"Remove Item" }
    ]
};

Webix GeoChart edit form

combo takes its options from a DataCollection that contains all country names and region codes:

var countries = new webix.DataCollection({
  url:"https://docs.webix.com/samples/36_geochart/data/countries.json"
});

/* countries.json
  [
   {
    "value": "Afghanistan",
    "code": "AF",
    "id": "Afghanistan"
   },...
  ]
*/

When a country is chosen, I want to fill the form with data if the country already exists in the dataset. If there are no data for the selected area, let the form be empty. To look for data items, I can call the find method of the GeoChart. However, as the map in the demo is synced to a DataCollection, I can look for items with the same method of mapdata:

{ view:"combo", name:"country", options:countries,
  on:{
    onChange:function(newv){
      //is the country in data?
      var item = mapdata.find(function(obj){
        return obj.country.toLowerCase() === newv.toLowerCase();
      });
         
      if(item.length)
        this.getFormView().setValues(item[0]);
      else {
        //if not, get its code
        var itemc = countries.find(function(obj){
          return newv === obj.value;
        },true);
        this.getFormView().setValues({country:newv, code:itemc.code, id:", area:", population:"});
      }
  }
}}

If I select a country from the combobox, this country name will be compared to names in the map data. mapdata.find returns an array with the item from the dataset if it has been found. If the country hasn’t been found, it returns an empty array.

Next, if there are no data related to the selected country, the region code will be taken from the big data collection with countries.find.

If the item array is not empty, the form is filled with real values. Otherwise, it gets the country name and the code, other controls are filled with empty strings. Note the empty string passed to id. I’ll use it later as a flag to check whether the country is already in the DataCollection.

geochart edit combo list

Adding and Updating Items

Now I want to add and update data items. There are two cases here:

  1. the country is already in the dataset;
  2. I’m adding a new country.

To deal with the first case, let’s use the updateItem method of the mapdata DataCollection. GeoChart has the same method. updateItem takes two parameters: the ID of the item and the item itself as an object.

{ view:"button", value:"Add/Update Item", click:function(){
  var values = this.getFormView().getValues();
  if(values.id)
    mapdata.updateItem(values.id, { area:values.area, population:values.population});
}}

values.id is truthy if the country is in DataCollection.

Now let’s deal with the second case: adding a new country to the dataset. I’ll use the add method that receives one mandatory parameter – the country object.

{ view:"button", value:"Add/Update Item", click:function(){
    var values = this.getFormView().getValues();
    if(values.id)
      mapdata.updateItem(values.id, { area:values.area, population:values.population});
    else{
      mapdata.add({ country:values.country, code:values.code, area:values.area, population:values.population});
    }
}}

It would be a good idea to validate form inputs. First, I’ll add validation rules to the form:

var editForm = {
    view:"form", id:"editForm", elements:[
      //elements    
    ],
    rules:{
        area:webix.rules.isNumber,
        population:webix.rules.isNumber,
        country:function(value){ return value != 0; }
    }
};

Now I’ll add validation to the Add/Update button handler:

{ view:"button", value:"Add/Update Item", click:function(){
  if(this.getFormView().validate()){
    var values = this.getFormView().getValues();
    if(values.id)
      mapdata.updateItem(values.id, { area:values.area, population:values.population});
    else{
      mapdata.add({ country:values.country, code:values.code, area:values.area, population:values.population});
    }
  }
}}

As I add or update an item in mapdata, the list of records in the DataTable is also updated.

Webix with geochart interactive map cameroon added

Removing Items

Now let’s enable removing items from GeoChart. I must check if the item exists before removing it. I’ll use the id value of the form as a flag again. Both GeoChart and DataCollection have the remove method, let’s use it.

{ view:"button", value:"Remove Item", click:function(){
    if(this.getFormView().validate()){
      var id = this.getFormView().getValues().id;
      if(id){
        mapdata.remove(id);
        this.getFormView().clear();
      }
    }
}}

remove deletes the country only after the input is validated and if the item exists within the dataset. After the item is deleted, the form is cleared.

One More Thing with Interactivity

Here is one more example of how to enhance interactivity with onClick events. It’s pretty handy to edit items with a click on the map. To update countries from the DataCollection, I will handle onItemClick:

on:{
  onItemClick:function(id){
    $$("editForm").setValues(this.getItem(id));
    $$("grid").select(id);
  }
}

Well, that was easy. Next, I want to add new countries by clicking dataless regions. This task will be trickier, because onRegionClick fires for regions with and without data.

I will take the region code received by the event and compare it to codes in the list of countries marked on the map. Then if the search result is empty, a Webix confirm dialogue will ask users whether they want to add a new data record. If yes, they can proceed to add a new item.

onRegionClick:function(obj){
  var code = obj.region;
  var item = mapdata.find(function(obj){
    return obj.code === code;
  });
     
  if(!item.length) {
    $$("grid").clearSelection();
    webix.confirm("Add a new country?", function(){
      //...add the country
    });
  }
}

Then I will get the country name by comparing codes from the list of all countries. After the name is found, the combo value will be changed.

webix.confirm("Add a new country?", function(result){
  if (result) {
    var newv = countries.find(function(obj){
      return obj.code === code;
    },true);
    $$("editForm").setValues({country:newv.value});
  }
});

Webix GeoChart Interactive Map

And here’s the live demo with complete source code.

Before You Dash Off to Enjoy GeoChart…

GeoChart is good for presenting different statistical data associated with regions, countries or states. You can adapt GeoChart to your needs and use it in apps for business, education, reports, or just for the fun of it. If you have more ideas and tips on GeoChart, I’ll be happy to hear from you 🙂

Get the demo and play with it. If you feel like digging deeper for more details about GeoChart, check out Webix documentation and Google documentation.

The post Interactive Map with Webix GeoChart appeared first on Webix Blog.

New Webix Jet and the Good Stuff It Can Do

$
0
0

The New Webix Jet has come to light with the latest release of Webix UI library.

Webix Jet is a micro framework for single-page applications created with Webix UI components. It allows you to divide app code into modules as well as combine and reuse diverse UI components. The framework provides a solution for device-responsive apps and can be used on both Desktop and mobile touch devices that run iOS, Android, etc.

With Webix Jet, you can easily create and develop perfect applications with loosely-coupled code. Sounds intriguing? Let’s look closer at what’s new in Webix Jet 1.0 and recap the useful features that migrated from the previous version.

You can also read Jet GitBook and download demos from GitHub.

webix jet 1 overview

New Jet in a Nutshell

Coding in the new Webix Jet is now simpler, while view configuring is much more flexible, because in addition to raw JS objects you can use ES6 classes. With the new Jet, you can create multilevel apps by including app modules as subviews. Routing has also become more flexible due to a number of predefined routers and the ability to define custom ones. Webix Jet uses a Webpack-based toolchain that makes apps easily configurable. The updated framework can reuse code written for Webix Jet 0.x, so it should be rather easy to migrate to the new codebase.

Useful Links

To get started with new Webix Jet, download or clone the Jet Start repo. The php branch has a demo with authorization/authentication of users and an example of a PHP backend.

Jet Demos repo has examples of creating guards, working with plugins and routers, and demos with elegant solutions for common tasks.

For more details, read a tutorial on GitBook. The tutorial is also available on GitHub, so you can find out about all the changes in the book at once.

You can also check out this online demo to see what complex UIs can be created with Jet.

webix jet 1 admin app demo in modules

Webix Jet Core Concepts

Modules

The core concepts of Webix Jet are view modules and routing. Code and UI are split in isolated modules. Modules can be reused in many places across the app. Splitting into modules helps you keep your code loosely-coupled and makes it more difficult to break the app. All modules can be developed and tested separately.

UI modules are called views and are grouped into apps, which represent applications or application modules. Views can enclose other views or even apps. Enclosed views are called subviews. View modules can be defined in three ways that produce more or less the same result. The most flexible one is ES6 classes. Apart from UI config, view classes have redefinable methods for major events like initialization and URL change. You can also check out View API in the tutorial.

Compare two versions of the same simple layout created with the old and new Jets:

webix jet versions syntax compared

It’s good practice to keep UI and data loading and saving logic separately. There is one more type of modules for data called a model.

App Modules

Apps are defined as class instances:

import {JetApp} from "webix-jet";

const app = new JetApp({
   id:"MyApp",
   ver:"1.0",
   start:"/layout"
}).render();

The configuration of the class instance includes app config such as the app ID, version, initial URL, and others parameters. You can find JetApp API in the GitBook.

Routing

The URL of the page reflects the current state of the UI. The URL instructs the app which view modules to show. You can use several ways to show different modules of a single-page app, including Jet links or native URL links.

webix jet 1 hashbang url

Besides, the new Jet has more flexible routing. You can choose a router for in-app navigation among four predefined types or define a custom one. For example, if you prefer to display the URL without a hashbang, you can choose UrlRouter:

import {JetApp,UrlRouter} from "webix-jet";

const app = new JetApp({
   id:"MyApp",
   ver:"1.0",
   start:"/layout",
   router: UrlRouter
}).render();

Latest Features of Webix Jet

Webpack-based Toolchain

As the new Webix Jet uses a Webpack-based toolchain, it is easier to resolve module dependencies, to organize code and reuse components among multiple entry points of your app. Single-page apps become easily configurable. For example, you can change the default app config in webpack.config.js if you want your app to have several entry points.

package.json contains predefined scripts for common tasks. For instance, when your app is ready for production, run npm run build to create minified bundles for every entry point of your app. After that, all you have to do is upload the contents of the codebase folder and your HTML file to the production server.

Plugins for Task Solution

New Jet has a set of plugins that are designed to solve common tasks such as creating menus, access control, warning about unsaved data, localization, status notifications and applying skins. For example, the User plugin will help you control access to your app. And if for some reasons, like unsaved form data or insufficient access rights, you want to block routing, use UnloadGuard. You can also define your own plugins for other tasks.

webix jet 1 unloadGuard plugin

Better Debugging and Event Handling

For more insights into the nature of errors that might occur, you can enable debugging in your app config and make use of error events.

import {JetApp} from "webix-jet";
const app = new JetApp({
   id:"MyApp",
   ver:"1.0",
   debug: true,
   start:"/layout"
}).render();

Besides error events, Webix Jet has other inner events that can be useful.

Instead of Hundreds of Words

Webix Jet is more than can be said in nine hundred words. You can try it and evaluate it yourself. Download the start package, read the reference on GitBook, and develop apps. If you have any questions, feel free to leave comments in the reference.

The post New Webix Jet and the Good Stuff It Can Do appeared first on Webix Blog.

Viewing all 246 articles
Browse latest View live