Building Apps with Webix Jet

Configuring app and views

Links

Docs

Big demos

Starter

Samples

Prerequisites

 

First Start

clone the demo app

git clone https://github.com/webix-hub/jet-start

 install toolchain and start the app

cd jet-start
yarn install
yarn start

 open app in the browser

http://localhost:8080
cd jet-start
npm install
npm start

Tools

check code quality

yarn lint

get files for deploy

yarn build

Webpack's Magic

  • Automatic app updates
  • Check console for build errors
npm run lint
npm run build

The App!

Codebase

/sources/myapp.js

/ sources/views

/sources/models

/sources/locales

/sources/styles

- app's config

- UI chunks

- data and data related logic

- localization

- css, images, etc.

App Configuration

import {JetApp, EmptyRouter, HashRouter } from "webix-jet";

export default class MyApp extends JetApp{
    constructor(config){
        const defaults = {
	    id 	: APPNAME,
	    version : VERSION,
	    router : BUILD_AS_MODULE ? EmptyRouter : HashRouter,
	    debug : !PRODUCTION,
	    start : "/top/start"
	};

	super({ ...defaults, ...config });
    }
}

if (!BUILD_AS_MODULE){
	webix.ready(() => new MyApp().render() );
}
  • JetApp instance
  • start point
  • .render()

Views

some.com#!/top/start

Views

some.com/#!/top/start

some.com/#!/top/data

views

  • top.js
  • start.js
  • data.js
  • details
    • form.js
    • template.js

some.com/#!/top/details.form

some.com/#!/details.template

File structure

App URLs

models

Defining Views

    export default {
        template:"Start page"
    };
    import {JetView} from "webix-jet";
    
    export default class TopView extends JetView {
        config(){
            return {
                cols:[
                    { view:"menu" },
                    { template:"Something here"}
        	]
    	    };
        }
    }

/views/start.js

/views/top.js

Objects

Classes

Defining Views

    import {JetView} from "webix-jet";
    import {data} from "models/records";
    
    export default class DataView extends JetView {
        config(){
            return { rows:[
                { view:"toolbar" },
                { view:"datatable", autoConfig:true }
            ]};
        }
        init(view){
            view.queryView("datatable").parse(data);
        }
    }

/views/data.js

Import modules

Why views are necessary

Composable blocks

Error resistant

Code reusage

Classes

Objects

VS

Simple

Lifetime handlers

Custom methods

Local variables

Extending

Brief

Classes: Providing Handlers

    import {JetView} from "webix-jet";
    import {data} from "models/menu";

    export default class TopView extends JetView {
        config(){
            return {
                cols:[
                    { view:"menu" },
                    { template:"Something here"}
        	]
    	    };
        }
        init(view){
            view.queryView("menu").parse(data);
        }
    }

/views/top.js

  • config
  • init
  • urlChange
  • ready
  • destroy

Classes: Extending

    import Toolbar from "views/toolbar";
    
    export default class Bottombar extends Toolbar {
         init(view){
            view.queryView("label").hide();
        }
    }
    import {JetView} from "webix-jet";
    
    export default class Toolbar extends JetView {
	config(){
            return {  
                view:"toolbar", height:50, elements:[
                    { view:"label", label:"My First App!"},
                    { },
                    { view:"button", autowidth:true, value:"Click me"}
	        ]
            };
	}
    }

/views/toolbar.js

/views/bottombar.js

Classes: Methods and Properties

import {JetView} from "webix-jet";

export default class Toolbar extends JetView {
    config(){
        return { 
            view:"toolbar", height:50, elements:[
		{ view:"button", autowidth:true, value:"Click me", 
		    click:() => this.doClick("Clicked") 
		}
	    ]
	};
    }
    init(){
        //custom properties
	this._counter = 0;
    }
    //custom methods
    doClick(message){
	this._counter++;
	webix.message(message+" "+this._counter);
    }
}

* this points to Toolbar class here

Classes: Access to App

import {JetView} from "webix-jet";

export default class Toolbar extends JetView {
    config(){
        return { 
            view:"toolbar", height:50, elements:[
                { view:"button", autowidth:true, value:"Show some", 
		    click:() => this.app.show("/top/data");
		}
	    ]
	};
    }
}
  • this points to Toolbar class
  • this.app points to app instance
  • this.app.show renders UI according to the URL

Views - Composition

    import {JetView} from "webix-jet";

    export default class Toolbar extends JetView {
	config(){
            return { view:"toolbar", elements:[ ... ]};
	}
    }
    import Toolbar from "views/toolbar";
    
    export default class TopView extends JetView {
        config(){
            return { rows:[
                Toolbar,
                { cols:[ 
                    { view:"menu" }, {}
                ]}
            ]};
        }
    }

/views/toolbar.js

/views/top.js

Static subviews:Class or Object

Views - Composition

/views/toolbar.js

/views/top.js

Static subviews: Extra properties

    import {JetView} from "webix-jet";
    export default class Toolbar extends JetView {
	config(){
            return { view:"toolbar", elements:[ ... ]};
	}
    }
    import Toolbar from "views/toolbar";
    
    export default class TopView extends JetView {
        config(){
            return { rows:[
                { $subview: Toolbar}, // id, name, etc.
                { cols:[ 
                    { view:"menu" }, {}
                ]}
            ]};
        }
    }

Views - Composition

/views/toolbar.js

/views/top.js

Static subviews: Extra Properties

    import {JetView} from "webix-jet";
    export default class Toolbar extends JetView {
	config(){
            return { view:"toolbar", elements:[ ... ]};
	}
    }
    export default class TopView extends JetView {
        config(){
            return { rows:[
                { $subview: "toolbar"}, // "views/toolbar" will be loaded
                { cols:[ 
                    { view:"menu" }, {}
                ]}
            ]};
        }
    }

Views - Composition

    export default {
    	template:"Start page"
    };

/views/start.js

/views/top.js

Dynamic subviews

/views/data.js

    export default {
    	template:"Data page"
    };
    import Toolbar from "views/toolbar";
    
    export default class TopView extends JetView {
        config(){
            return { rows:[
                Toolbar,
                { cols:[ 
                    { view:"menu" }, 
                    { $subview:true}
                ]}
            ]};
        }
    }

Views - Routing

    export default {
    	template:"Start page"
    };

/views/start.js

/views/top.js

some.com/#!/top/start

some.com/#!/top/data

    export default {
    	template:"Data page"
    };

/views/data.js

import Toolbar from "views/toolbar";
    
export default class TopView extends JetView {
    config(){
        return { rows:[
            Toolbar,
            { cols:[ 
                { view:"menu" }, 
                { $subview:true}
            ]}
        ]};
    }
}

Views - Routing

some.com/#!/top/data

some.com/#!/top/top/data

some.com/#!/data

some.com/#!/top

Windows and Popups

    import {JetView} from "webix-jet";
    
    export default class WindowsView extends JetView {
        config(){
            return { 
                view:"button", width:200, value:"Popup", 
    		click:() => this._webixPopup.show() 
            }};
        }
        init(){
    	    this._webixPopup = webix.ui({
    	        view:"window"
    	    });
        }
        destroy(){
    	    this._webixPopup.close();
        }
    }

Create with webix.ui()

  • you need to destroy it!

Not recommended way

    import {JetView} from "webix-jet";
    import PopupView from "views/windows/popup";

    export default class WindowsView extends JetView {
        config(){
            return { 
                view:"button", width:200, value:"Popup", 
		click:() => this._jetPopup.showWindow() 
            };
        }
        init(){
            this._jetPopup = this.ui(PopupView);
	}
    }

Windows and Popups

Create with JetView.ui()

Recommended way

Windows and Popups

    import {JetView} from "webix-jet";

    export default class PopupView extends JetView {
	config(){
            return { view:"window" };
	}
	showWindow() {
            this.getRoot().show();
	}
    }

/views/windows/popup.js

   /views/toolbar.js

    import {JetView} from "webix-jet";
    import PopupView from "views/windows/popup";

    export default class ToolbarView extends JetView {
        config(){
            return {
                view:"button", width:200, value:"Show popup", 
		click:() => this._jetPopup.showWindow() 
            };
        }
        init(){
            this._jetPopup = this.ui(PopupView);
	}
    }

*JetView.getRoot() - access to a top Webix view within the module

JetView Constructor

   /views/griddata.js

    import {JetView} from "webix-jet";
    import GridView from "views/gridview";
    import data1 from "models/data1";
    import data2 from "models/data2";


    export default class GridData extends JetView {
        config(){
            return {
                rows:[
                    //embed directly 
                    new GridView(this.app,"", data1), 
                    //or wrap in { $subview: }    
                    { $subview: new GridView(this.app,"", data2) }

                ]
            }
        }
    }

Re-use modules with

  • similar configuration
  • specific parameters

JetView Constructor

    import {JetView} from "webix-jet";

    export default class GridView extends JetView {
        constructor(app, name, data){
	    super(app, name); //call parent constructor
	    this._gridData = data; //define specific parameters
	}
        config(){
            //reuse config
            return {
                rows:[ toolbar, datatable ]
            };
        }
	init(view) {
            //parse specific data    
            view.queryView("datatable").parse(this._gridData);
	}
    }

/views/gridview.js

Re-use modules with

  • similar configuration
  • specific parameters

Referencing Jet Views

    export default class Toolbar extends JetView {
	config(){
            return { 
                view:"button", value:"Click me", 
		click:() => this.doClick("Clicked") 
	    };
	}
	doClick(message){
            webix.message(message);
	}
    }
    export default class Toolbar extends JetView {
	config(){
            return { 
                view:"button", value:"Click me", click:function(){ 
		    this.$scope.doClick("Clicked");
		}
	    };
	}
	doClick(message){
	    webix.message(message);
	}
    }

this

this.$scope

Referencing Jet Views

    //views/toolbar.js
    export default class Toolbar extends JetView {
        config(){
	    return { view:"toolbar" };
	}
	init(){
	    console.log(this.getParentView());
	}
    }
    //views/top.js
    import Toolbar from "views/toolbar";

    export default class TopView extends JetView {
	config(){
            return { rows:[
		{ $subview:Toolbar, name:"top-toolbar"}		
	    ]};
	}
	ready(view){
            console.log(this.getSubView("top-toolbar"));
	}
}

.getParentView()

.getSubView()

Don't overuse these methods: module logic should be encapsulated!

Referencing Webix views

    import Toolbar from "views/toolbar";
    import Bottombar from "views/bottombar";
    import {data} from "models/menu";

    export default class TopView extends JetView {
    	config(){
            return { rows:[
                Toolbar,
                { cols:[
                    { view:"menu", localId:"top:menu"} //or id:"top:menu"
                ]},
                Bottombar
            ]};
    	}
        init(view){
            //searching for views
            view.queryView("menu").parse(data);
            
            view.queryView("button", "all").forEach((obj) => {
                obj.hide();
            });

            //accessing particular view
            this.$$("top:menu").parse(data);
        }
        doClick(){
            // access to top Webix view within the module
            this.getRoot()
        }    
    }

Configuring the App

import "./styles/app.less";                                    //! stylesheets
import "webix/appbutton";                                      //! custom widgets
import {JetApp, plugins, HashRouter } from "webix-jet";

export default class MyApp extends JetApp{
    constructor(config){
        const defaults = { 
            id : APPNAME, version : VERSION,
            router : HashRouter,
	    debug : !PRODUCTION,
	    start : "/top/start"
        };
	super({ ...defaults, ...config });
    }
}

webix.ready(() => {
    if (webix.CustomScroll && !webix.env.touch)             //! App-level Webix logic
	    webix.CustomScroll.init();

    const app = new MyApp();

    app.use(plugins.Locale);                                //! App-level Jet plugins
    app.render();

  • stylesheets
  • custom widgets

Include:

Use:

  • App-level Webix logic
  • App-level Jet plugins

Error Handling



    export default class MyApp extends JetApp{
        constructor(config){
            const defaults = { 
                id: APPNAME, version: VERSION,
                start: "/top/start"
                debug: true // console.log and debugger on error
            };
	    super({ ...defaults, ...config });
        }
    }
    
    const app = new MyApp();
    app.render();
    
    app.attachEvent("app:error:resolve", function(err, url) {
        webix.delay(() => app.show("/top/start"));
    });

Events:

Dev Guidelines

  • include webix.js
  • include debug:true to app config
  • handle “app:error:resolve” event
  • npm run lint before each commit
  • reuse similar views

  • don’t address views from other modules by ids

  • ensure that data is loaded before working with it

Safe developing

Wise developing

Links

Task for practice: 

Demo:

Follow-up text: click here

3-1. Webix Jet. Intro

By ihelga

3-1. Webix Jet. Intro

Preparing the environment and configuring the app. Providing and composing UI chunks.

  • 735