Webix Jet provides predefined plugins and the ability to create custom plugins.
View Plugins
These plugins are enabled for a specific view by view.use():
- the Menu plugin
- the UnloadGuard plugin
- the Status plugin
- the UrlParam plugin
App Plugins
These plugins are enabled for the whole app with app.use():
- the User plugin
- the Theme plugin
- the Locale plugin
The Menu plugin simplifies your life if you plan to create a menu for navigation:
- it sets subview URLs for menu options, buttons or other controls you plan to use for navigation;
- it automatically highlights the right menu option after a page reload or change of the URL.
The plugin must be enabled in the Jet view that contains the menu with view.use() (call it as this.use()). After the plugin name, you must specify the local ID of the Webix control or widget that you want to use as a menu:
// views/top.js
import {JetView, plugins} from "webix-jet";
export default class TopView extends JetView{
config(){
return {
rows:[
{
view:"menu", localId:"menu", data:[
{ id:"details", value:"Details" }, //show "/top/details"
{ id:"dash", value:"Dash" } //show "/top/dash"
]
},
{ $subview:true }
]
};
}
init(){
this.use(plugins.Menu, "menu");
}
}
Subview URLs are taken from menu option IDs or from values if there are no IDs. If you want to change some URL, you can add custom subview URLs in the plugin configuration:
// views/top.js
import {JetView} from "webix-jet";
export default class TopView extends JetView {
config(){
return {
rows:[
{
view:"menu", localId:"menu", data:[
{ id:"details", value:"Details" }, //show "/top/demo/details"
{ id:"dash", value:"Dash" } //show "/top/dash"
]
},
{ $subview:true }
]
};
}
init(){
this.use(plugins.Menu, {
id:"menu",
urls:{
details:"demo/details"
}
});
}
}
You can set the Menu plugin in the way that it does not go to a different path, but changes a certain URL parameter. Use the param config setting for this. param accepts a string with the parameter name, e.g.:
// views/top.js
import {JetView} from "webix-jet";
export default class TopView extends JetView {
config(){
return {
rows:[
{
view:"list", localId:"users", data:[
{ id:"1", value:"Jason Daniels" }, //show "/top?user=1"
{ id:"2", value:"Kittie Stark" } //show "/top?user=2"
]
},
{ /* ...UI */ }
]
};
}
init(){
this.use(plugins.Menu, {
id:"users",
param:"user"
});
}
}
If you use the Menu plugin with param setting together with the UrlParam, the parameter will be displayed in the URL as a URL segment:
// views/top.js
init(){
this.use(plugins.UrlParam, ["user"]);
this.use(plugins.Menu, {
id:"users",
param:"user"
});
}
//show "/top/1"
//show "/top/2"
The UnloadGuard plugin can be used to prevent users from leaving the view on some conditions. For example, this can be useful in the case of forms with unsaved or invalid data. The plugin can intercept the event of leaving the current view and e.g. show a confirmation dialogue.
The syntax for using the plugin is this.use(plugin,handler).
// views/some.js
import {JetView, plugins} from "webix-jet";
...
init(){
this.use(plugins.UnloadGuard, function(){
//do something
});
}
this.use() takes two parameters:
- the plugin name
- the function that will define the behavior of the plugin
The UnloadGuard plugin expects a function that returns true if the access must be granted and false if not. The function can also return a promise, resolved and rejected correspondingly.
UnloadGuard can be used for form validation, for example. Let's have a look at a form with one input field that must not be empty:
// views/form.js
import {JetView, plugins} from "webix-jet";
export default class FormView extends JetView {
config(){
return {
view:"form", elements:[
{ view:"text", name:"email", required:true, label:"Email" },
{ view:"button", value:"save", click:() => this.show("details") }
]
};
}
}
Let's enable UnloadGuard and show a confirmation window if the input is invalid. Webix confirmation windows and other modal boxes return promises.
// views/form.js
...
init(){
this.use(plugins.UnloadGuard, () => {
if (this.$$("form").validate())
return true;
return webix.confirm({
title:"Form is incomplete",
text: "Do you still want to leave?"
});
});
}
If the form input is not valid, the function returns a promise. Depending on the answer, the promise either resolves and UnloadGuard lets the user go to the next view, or it rejects. No pasaran.
This plugin is useful if you want to show the status of data loading in case it takes time, to confirm success or to show an error message.
These are the status messages that you can see:
- "Ok",
- "Error",
- "Connecting...".
Status is enabled with this.use() with two parameters:
- the plugin name;
- the plugin configuration (a string or an object).
The plugin configuration must contain at least the ID of the widget that will show the status. This is the simplest way to use the plugin to display the status of data loading into a datatable:
// views/data.js
import {JetView, plugins} from "webix-jet";
import {data} from "models/records";
export default class DataView extends JetView{
config(){
return {
rows:[
{ view:"datatable", autoConfig:true },
{ id:"app:status", view:"label" }
]
};
}
init(view){
view.parse(data);
this.use(plugins.Status, "app:status");
}
}
Status configuration can have several properties:
- target (string) is the ID of the widget where you want to display the status message;
- ajax (Boolean) enables asynchronous requests;
- expire (number) defines the time after which the status message disappears (by default, 2000 ms). If you set it to 0, the status message will stay as long as the view is open;
- data (string) defines the ID of the data component to track;
- remote (Boolean) enables webix.remote - a protocol that allows the client component to call functions on the server directly.
// views/data.js
...
this.use(plugins.Status, {
target:"app:status",
ajax:true,
expire:5000
});
The plugin allows treating URL segments as parameters. It makes them accessible via view.getParam() and correctly weeds them out of the URL.
UrlParam is enabled with this.use() with two parameters:
- the plugin name;
- an array with parameter name(s).
Let's consider a simple example with a parent view some and its child details:
// views/some.js
import {JetView} from "webix-jet";
export default class SomeView extends JetView{
config(){
return {
rows:[
{ $subview:true }
]
};
}
}
// views/details.js
const details = { template:"Details" };
export default details;
When loading the URL "/some/23/details", you need to treat the segment after some (23) as a parameter of some. Enable the plugin in the init() method of some:
// views/some.js
import {JetView,plugins} from "webix-jet";
export default class SomeView extends JetView{
...
init(){
this.use(plugins.UrlParam, ["id"])
// now when loading /some/23/details
const id = this.getParam("id"); //id === 23
}
}
details will be rendered inside some, and the fragment 23 will be displayed in the address bar, but will not be resolved as a view module.
The User plugin is useful for apps with authorization.
This section contains guidelines for using the plugin with a custom script.
Related demos:
To enable the plugin, call app.use() with two parameters:
- the plugin name,
- the plugin configuration.
The plugin configuration can include the following settings:
-
model is a specific session model configured by the developer according to the requirements needed for correct authorization;
-
login (string) is the URL of the login form, "/login" by default;
-
logout (string) is a logout link that could be reserved to automatically perform logout and redirect to
afterLogout
page (if specified, otherwise the app will use the default redirect to the login page), "/logout" by default;
this.show("/logout"); // from anywhere
-
afterLogin (string) is the URL shown after logging in, by default taken from app.config.start;
-
afterLogout (string) is a link that works only with logout. Provides specific redirect after logging out via logout link, "/login" by default;
-
user (object) includes some default user credentials;
-
ping (number) is the time interval for checking the current user status, 30000 ms by default;
-
public (function) allows adding pages accessible by unauthorized users.
import session from "models/session";
// ...
app.use(plugins.User, {
model: session,
ping: 15000,
afterLogin: "someStartPage",
user: { /* ... */ }
});
The session model should contain three main methods:
login()
logs the user in and also redirects either to the specifiedafterLogin
or App's start page (i.e. default redirect isapp.config.start
)
The parameters are:
- user - username;
- pass - password.
// models/session.js
function login(user, pass){
return webix.ajax().post("server/login.php", {
user, pass
}).then(a => a.json());
}
To implement logging in, you should call the login() method. Once login() is called and the response with some user credentials (JSON object with a name or a promise, for example) is received, the User plugin will automatically redirect the user to the specified start page and will consider the server response as user credentials.
Let's define the do_login() method that will call login() from a form:
// views/login.js
import {JetView} from "webix-jet";
export default class LoginView extends JetView{
config(){
return {
view:"form",
rows:[
{ view:"text", name:"login", label:"User Name" },
{ view:"text", type:"password", name:"pass", label:"Password" },
{ view:"button", value:"Login", click:() => {
this.do_login();
}, hotkey:"enter" }
],
rules:{
login:webix.rules.isNotEmpty,
pass:webix.rules.isNotEmpty
}
};
}
do_login(){
const user = this.app.getService("user");
const form = this.getRoot();
if (form.validate()){
const data = form.getValues();
user.login(data.login, data.pass).catch(xhr => {
//error handler
});
}
}
}
logout()
logs the user out, but does not trigger any automatic redirect until it is initiated by the user or app's logic
// models/session.js
function logout(){
return webix.ajax().post("server/login.php?logout")
.then(a => a.json());
}
This is how you can call the method:
const user = this.app.getService("user");
user.logout().then(() => this.show("/login"));
status()
returns the status of the current user
// models/session.js
function status(){
return webix.ajax().post("server/login.php?status")
.then(a => a.json());
}
Each method should return a promise.
Check out basic examples:
Both examples are nearly identical, all requests are implemented with webix.ajax().
E.g. the PHP session model contains requests to php scripts for logging in, getting the current status, and logging out.
Related demo:
The getUser() method returns the data of the currently logged in user.
The getStatus() method returns the current status of the user. It can receive an optional Boolean parameter the server: if it is set to true, the method will send an AJAX request to check the status.
The User plugin checks every 5 minutes (time lapse is defined by the ping setting of the plugin) the current user status and warns a user if the status has been changed. For example, if a user logged in and didn't perform any actions on the page during some time, the plugin will check the status and warn the user if it has been changed.
By default, unauthorized users can see only the "login" page. You can add other pages that all users are allowed to access. Use the public setting of the plugin configuration object. public must be a function that returns true for public pages and false for the rest:
app.use(plugins.User, {
model: session,
public: path => path.indexOf("top/data") > -1
});
If you need authorization with an OAuth service, you need to change the way to log in. logout() and status() in the session model are the same as for the custom script.
A demo of login with passport and Google OAuth2
This is a plugin for changing app themes.
This is how you can enable the plugin:
// myapp.js
import {JetApp, plugins} from "webix-jet";
...
app.use(plugins.Theme);
app.render();
The plugin has two methods:
- getTheme() returns the name of the current theme;
- setTheme(name) takes one obligatory parameter - the name of the theme - and sets the theme for the app.
The service locates links to stylesheets by their title attributes. Here are the stylesheets for the app, e.g.:
<!-- index.html -->
<link rel="stylesheet" title="flat" type="text/css" href="//cdn.webix.com/edge/webix.css">
<link rel="stylesheet" title="compact" type="text/css" href="//cdn.webix.com/edge/skins/compact.css">
You need to provide a way for users to choose a theme. For example, let's add a segmented button:
// views/settings.js
import {JetView} from "webix-jet";
export default class SettingsView extends JetView {
config(){
return {
type:"space", rows:[
{ template:"Settings", type:"header" },
{ localId:"skin", optionWidth: 120, view:"segmented", label:"Theme",
options:[
{ id:"flat-default", value:"Default" },
{ id:"flat-shady", value:"Shady" },
{ id:"compact-default", value:"Compact" }
], click:() => this.toggleTheme() /* not implemented yet, will be a method of this class */
}
{}
]
};
}
}
The option IDs of the segmented have two parts. The first part must be the same as the title attribute of the link to a stylesheet. The theme service gets the theme name from this part of the ID, locates the correct stylesheet and sets the theme.
Let's implement toggling themes as a method of the SettingsView class. The $$ method locates the segmented by its local ID and gets the user's choice. After that, the service sets the chosen theme by adding a corresponding CSS class name to the body of the HTML page.
// views/settings.js
import {JetView} from "webix-jet";
export default class SettingsView extends JetView {
...
toggleTheme(){
const themes = this.app.getService("theme");
const value = this.$$("skin").getValue();
themes.setTheme(value);
}
}
getTheme() can be used to restore the state of the segmented button after the new theme is applied. Let's get the current theme in config() and set the value of the segmented setting it to the correct value:
// views/settings.js
...
config(){
const theme = this.app.getService("theme").getTheme();
return {
...
{ name:"skin", optionWidth: 120, view:"segmented", label:"Theme", options:[
{id:"flat-default", value:"Default"},
{id:"flat-shady", value:"Shady"},
{id:"compact-default", value:"Compact"}
], click:() => this.toggleTheme(), value:theme }
...
};
}
This is a plugin for localizing apps.
You can enable the Locale plugin before the app is rendered:
// myapp.js
import {JetApp, plugins} from "webix-jet";
...
app.use(plugins.Locale);
app.render();
You must create files with all the text labels in English and their translations in the locales folder. This is an example of the Spanish locale file:
// locales/es.js
export default {
"My Profile" : "Mi Perfil",
"My Account" : "Mi Cuenta",
"My Calendar" : "Mi Calendario"
};
The default locale is English. You can change it while enabling the plugin:
// app.js
app.use(plugins.Locale,{ lang:"es" });
To translate all the text labels, you need to apply the _() method of the service to each label. The method takes one parameter - a text string. _() looks for the string in a locale file and returns the translation. E.g. this.app.getService\("locale"\).\_\("My Profile"\)
will return "Mi Perfil" if Spanish is chosen.
For example, let's apply the _() method to a small menu:
// views/menu.js
import {JetView} from "webix-jet";
export default class MenuView extends JetView {
config(){
const _ = this.app.getService("locale")._;
return {
view:"menu", data:[
{ id:"profile", value:"My Profile" },
{ id:"account", value:"My Account" },
{ id:"calendar", value:"My Calendar" }
],
template:(obj)=>{
return _(obj.value)};
}
};
}
}
If you want your app to be multilingual and let the users to choose a language, you can add a control for that.
To set a new language, use the setLang() method of the locale service. setLang() takes one parameter - the name of the locale file. When a user chooses a language, a locale file is located and the app language is changed.
{% hint style="danger" %} Do not call setLang() in lifetime handlers of JetView! It calls app.refresh() that re-renders all the views, which will start an infinite loop. {% endhint %}
Let's create a simple page for app settings with a segmented button that will be used to choose languages. Note that the IDs of the button options should be the same as the locale file names (e.g. "es", "en").
// views/settings.js
import {JetView} from "webix-jet";
export default class SettingsView extends JetView {
config(){
return {
type:"space", rows:[
{ template:"Settings", type:"header" },
{ localId:"lang", optionWidth: 120, view:"segmented", label:"Language", options:[
{ id:"en", value:"English" },
{ id:"es", value:"Spanish" }
], click:() => this.toggleLanguage() }, //will be implemented as a method of this class
{}
]
};
}
}
toggleLanguage() is a class method that will:
- get the value of the segmented button,
- pass it to setLang() that will set the locale.
// views/settings.js
import {JetView} from "webix-jet";
export default class SettingsView extends JetView {
...
toggleLanguage(){
const langs = this.app.getService("locale");
const value = this.$$("lang").getValue();
langs.setLang(value);
}
}
To check the current language, use the getLang() method. It can be useful for restoring the value of the settings control, e.g.:
// views/settings.js
import {JetView} from "webix-jet";
export default class SettingsView extends JetView {
config(){
const lang = this.app.getService("locale").getLang();
return {
...
{ name:"lang", view:"segmented", label:_("Language"), options:[
{id:"en", value:"English"},
{id:"es", value:"Spanish"}
], click:() => this.toggleLanguage(), value:lang }
...
};
}
...
}
To restore the last chosen language when the app is opened again, set the storage setting of the plugin, e.g. you can choose the local storage:
// app.js
...
app.use(plugins.Locale, { storage:webix.storage.local });
You can also set path to a subfolder (subpath) inside the jet-locale folder, in which you want the plugin to look for translations:
// app.js
...
app.use(plugins.Locale, { path:"some" });
You can also disable locale loading from jet-locales using the same setting:
app.use(plugins.Locale, { path:false });
The Locale plugin has the additional setting for using Webix locales alongside with the Jet app locale.
app.use(plugins.Locale, {
webix:{
en:"en-US",
// ...other locales
}
});
// calls webix.i18n.setLocale("en-US")
If a lot of text in your app needs to be translated, you can split localizations and load them when they are needed. The source of a locale can be anything, e.g. inline or a result of an AJAX request:
webix.ajax("/server/en/forms").then(data => {
data = data.json();
this.app.getService("locale").setLangData("en", data);
});
The Locale plugin uses the Polyglot library, which has settings of its own. You can use these settings in the config of the plugin:
this.use(plugins.Locale, { polyglot:{ /* ...Polyglot settings */ } });
You can create a service and use it as an app-level plugin.
//helpers/state.js
export function State(app){
const service = {
getState(){
return this.state;
},
setState(state){
this.state = state;
},
state:0
};
app.setService("state", service);
}
Such services should be included into the application in the start file (e.g. myapp.js):
//myapp.js
import {state} from "helpers/state.js";
const app = new JetApp({...}).render();
app.use(state);
And any view will be able to use it as:
{ view:"button", value:"Add new", click:() => {
if (this.app.getService("state").getState())
//do something
}}