This year we have worked out a new concept of complex widgets and have already released the new ones: File Manager, Document Manager, Query, and User Manager. They were built from scratch as fully configurable Webix Jet app modules, which makes them flexible, powerful… and yet not very easy to master regarding the code. However once you grasp the concept, it opens great opportunities.
Let’s explore the technical features of the new widgets and learn of all the benefits of the Jet-based approach.
Why We Did it in the First Place
Webix complex widgets are basically small applications. While they can cope with their primary task well, it is not easy to customize them for specific purposes, as their public API does not allow to get to the inside logic too much. But people really need this flexibility.
That is why we came up with the idea to use Webix Jet to create complex widgets as mini apps. This solution keeps complex widgets well-structured and very flexible for customization and scalable for new functionality.
What’s New for End-Users
First of all, we have not simply rewritten the code, we have added a lot of new features to the end users. For example, File Manager 7.2 was enhanced with:
- Three-mode interface: list, cards and double view
- Preview of files
- Text editor
- Adding files and folders
- Downloading and uploading files
- Adaptive behavior to serve small screens on mobile devices
Document Manager went even further and added a number of features outside the scope of simple file-system tasks:
- Trash for storing deleted files and folders;
- Marking files as favorite;
- Sharing files with other users;
- The ability to open recently viewed files;
- Adding tags to files and folders;
- Search by names and tags;
- Commenting files;
- Viewing and editing Excel files.
Query emerged with a user-friendly design and convenient UX for filtering.
User Manager – did you hear that? – is an absolutely new widget for managing users and their access rights:
- managing user access rights
- uniting access rights into roles
- permissions and actions audit
This is what end-users will get from the Jet-based widgets out of the box. Anything that’s outside the scope can be easily added by you, developers.
What’s New for Developers
To the experienced Webix users, the Jet-based widgets can seem a bit unorthodox. They are built differently and require a different approach from a developer. While each widget has its own specific features, they all have these things in common:
- Webix Jet-based structure and OOP
- Reactive properties instead of conventional configuration settings and event system
- Customizable view classes and services instead of classic Webix API
- Overrides as a means of customization
Webix Jet and UI Customizations
Webix Jet takes care of neat code organization and the basic architecture of complex widgets. Its major features are:
- keeping the UI in separate modules (views) and combining them to create the needed interface
- keeping the app logic in special modules (services)
- nothing is private and everything can be reached and modified by code from outside a widget
With classic OOP inheritance, you can:
- extend views and services with new functionality
- override the current functionality
- add new features and components
For instance, to modify the UI, find the necessary view class and override its config method so that it returns a different Webix config. Let’s add a new option to the action menu of the Query widget:
config() {
const ui = super.config();
const list = ui.body;
list.data.push({ id: "custom", value: "Custom option" });
list.yCount = 5;
return ui;
}
}
To change the way specific components of a widget work, you need to override init and other view class methods. For example, to add an action for the above menu option, add a handler for it:
config() {
...
}
init(){
super.init();
this.on(this.app, "action", (id, item) => {
if (id === "log") this.LogFilter(item);
});
}
LogFilter(item){
console.log(`Filter ID: ${item}`);
}
}
Reactive State: How to change properties and watch changes
Alongside with the usual configuration settings like width, url, etc, Jet-based widgets have the reactive properties enabled by Jet reactive state. This technique was created to enable straightforward communication between all the widget parts. State properties can reflect the value of any parameter as well as notify about its change, so that any listener can react to it if necessary.
Compared to events and parameters, a reactive state:
- allows to avoid cumbersome current value checks for config parameters
- does not clutter the event bus, which happens sooner or later if you rely on app-wide events too much
- a listener does not have to exist at the moment a parameter changes; it can react to the change once it is created
You can define reactive properties right in the widget configuration:
view:"docmanager", id:"dm", source:"recent"
The reactive state of a widget is available via its getState() method:
After that you can:
- subscribe to a parameter and run your custom logic upon changes at any moment
view: "docmanager",
url: "https://docs.webix.com/docmanager-backend/",
on: {
onInit: app => {
const state = app.getState();
state.$observe("source", (v,o) => {
/* custom logic here */
});
},
}
}
- change parameters, and the widget will react to the change
You can change several state properties at once:
state.$batch({
source: "files",
path: "/Music",
mode: "cards"
});
You don’t need to refresh the widget afterwards. All listeners of the reactive state will do it on their own.
Overrides and Services: Customizing widget logic
Jet-based widgets do not have the usual public API, that’s why you won’t be able to customize them in the old way. Instead, widgets have service modules with the methods for the inner logic. You can redefine any service, e.g. the Backend service, if you require another logic of server-client communication.
Any method can be called directly, e.g.:
$$("filemanager").getService("backend").files("/").then(files => {
...
});
…and any method can be redefined. For example, you can redefine files to load the contents of directories in a different way:
// you can use calls to real backend server
files(id) {
return webix.ajax("//docs.webix.com/filemanager-backend/files", { id })
.then((data) => data.json());
}
// ... other methods
}
webix.ui({
view: "filemanager",
url: "https://docs.webix.com/filemanager-backend/",
override: new Map([[fileManager.services.Backend, MyBackend]]),
});
Common Customizing Guidelines
To know what exactly to override to get the result, you need to read the source code. However, there are basic guidelines on what to look for:
- changing the UI: look for a view class that contains the component (you can also consult class maps in the docs, e.g.) and redefine the config method of the view class: example
- changing the way the UI works: look for a view class that contains the component and redefine the init method or other class methods: example
- changing or adding functionality: add or redefine methods of view classes and services; e.g. Operations would be the service you want to look for if you want to handle app actions in a different way: example
- adapting the widget to your custom backend code: look for the Backend service or other services that deal with backend (e.g. Tags, Users), their local data counterparts (e.g. LocalData, LocalUsers, LocalTags) and override their methods: example
Nothing is properly private in Jet-based widgets. You can get to any method, property or component and change them. This is both a blessing and a great responsibility.
Flexibility is Responsibility
Flexibility means that you do not have to invent long and convoluted ways to change components and behavior. Everything is open to customization and improvement.
Flexibility also means responsibility …and the love for reading the source code. Since there is not much of a conventional API, we did not document every single method of any view or service – except for the Backend service, which is the first candidate for customization 🙂 We also provide general configuration guides and ‘How to’ pages with the ideas for the common tasks.
We are of course updating the documentation when our users find it insufficient. However, to truly understand how Jet-based widgets work, what the views and services are, etc, you will need to read the source code. If you have any problems, feel free to contact us. We are always happy to help you.
What’s Next
Meanwhile, we are already working on several Jet-based solutions to extend the collection of Webix complex widgets. If you are waiting for a specific tool, please share your ideas in the comments section.
Stay tuned for the updates and stay safe 🙂