Building Apps with Webix Jet

Navigation and Communication between app parts

Jet Routers

  • Can use navigation buttons in browser
  • Refresh-friendly app
  • Nice for development
  • Familiar URLs

Jet Routers

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

2. UrlRouter
     some.com/top/data

3. StoreRouter

4. EmptyRouter

default

Changing App Router

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

export default class MyApp extends JetApp{
    constructor(config){
	const defaults = {
	    router : UrlRouter,
	    start  : "/top/start"
        };

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

webix.ready(() => {
    new JetApp().render();
});

     some.com/top/data

//webpack.config.js
module.exports = function(env) {
    return  {
        ...
        devServer:{
            historyApiFallback:{
                index : "index.html"
    	    }
        }
    };
}

URL Router

Tune redirects on production server as well

Navigation:Links

    export default {
        template:'<a href="#!/top/data">Details</a>'
    }
    export default {
        template:'<a route="/top/data">Details</a>'
    }

HTML Links:

Jet Links:

<a> tag only

any HTML tag

Navigation:Methods

    //top.js
    {
        view:"button", click:() => this.show("./data")
    }

    //start.js
    {
        view:"button", click:() => this.show("../data")
    }

JetApp .show()

JetView .show()

    {
        view:"button", click:() => this.app.show("/top/data")
    }
    

top.js

  • start.js
  • data.js

Navigation:Methods

    config(){
        return { rows:[
            toolbar, 
            { cols:[
                { $subview:true, name:"left" }, 
                { $subview:true, name:"right" }
            ]}
        ]};
    }
    init(){
        this.show("./settings", {target:"left"});
        this.show("./data", {target:"right"});
    }
    

One dynamic subview

Several dynamic subviews: target

    config(){
        return { rows:[
            toolbar, { $subview:true }
        ]};
    }
    init(){
        this.show("./data");
    }
    

Independent

URL changes

URL modifications

const app = new JetApp({
    start: "/top/start",
    views: {
	"hi" : "start",
        "admin" :"settings"
    }
});
  • change whole path
  • show  key, load value
  • automatic redirects
const app = new JetApp({
    start: "/top/start",
    routes:{
	"/hi" : "/top/start",
	"/admin" : "/top/settings"
    }
});
  • change views name
  • show  key, load value
  • need to modify links

#!/top/start

#!/top/hi

#!/hi

Both valid:

Redirects to:

URL modifications

    class StartView extends JetView {
      config () {
      	return { template:"Start page"};
      }
    }
    
    class TopView extends JetView {
        rows:[
            { $subview:true }
        ]
    }
    
    
    const app = new JetApp({
        start: "/top/start",
        views:{
            top: TopView,
            start:StartView
        }
    });

Communication between Views

  • parameters
  • events
  • services
  • by globals
  • parent methods
  • by ID

URL Parameters

    //views/start.js
    export default class StartView extends JetView {
        config(){
            return {
                rows:[
                    { view:"button", click:() => {
                        this.show("../data?id=1");
                    }}
                ]
            };
        }
    });
    //views/data.js
    export default class Dataview extends JetView {
        config(){
            ...
        }
        urlChange(view){
            const table = this.$$("table");
            const id = this.getParam("id");
            
            if (id && table.exists(id)){
                table.select(id);
            }
        }
    });

Get and use

Pass to view

!#/top/data?id=1

URL Parameters

    //views/start.js
    { view:"button", click:() => {
        this.show("../top?state=2/data?id=1");
    }}
    //views/data.js
    urlChange(view){
        console.log(this.getParam("id")); //1
        console.log(this.getParam("state")); //undefined
        console.log(this.getParam("state"), true); //2
    }
 

Get parent param?

Yes!

Pass to view

!#/top?state=2/data?id=1

    //views/top.js
    urlChange(view){
        console.log(this.getParam("state")); //2
        console.log(this.getParam("id")); //undefined
    }

Get child param?

No!

URL Parameters

    //views/data.js
    import {JetView} from "webix-jet";

    export default class DataView extends JetView {
    	config(){ return {}; }
        init(view, url){
            const id = url[0].params.id;
        }
        urlChange(view, url){
	    const id = url[0].params.id;
	}	
    }

#!/top/data?id=1

[
    { 
        page:"data", 
        params:{ id:1 },
        index:2
    }
]

#!/top?state=2/data?id=1

    //views/top.js
    import {JetView} from "webix-jet";

    export default class DataView extends JetView {
    	config(){  return {}; }
        urlChange(view, url){
	    const state = url[0].params.state;
            const id = url[1].params.id;
	}	
    }
[
    { 
        page:"top", 
        params:{ state:2 },
        index:1
    },
    { 
        page:"data", 
        params:{ id:1 },
        index:2
    }
]

Child

params

URL Parameters

  • within handlers 
  • any moment

jetView.getParam()

handler argument

  • init(view, url)
  • urlChange
  • ready
  • get parent parameters
  • simple and clear
  • get child parameters
  • lower-level API

Events

    export default class DataView extends JetView {
        config(){
             return { 
               view:"datatable", id:"table"
             };
        }
        init(view){
            this.on(this.app, "onDataEditStop", (data) => {
	        if(data){
                    this.$$("table").add(data);
	    });
        }
    });
    export default class FormView extends JetView {
        config(){
            return { view:"form", elements:[
                 { view:"text", label:"Title" },
                 { view:"button", value:"Save", click:() => {
                     const data = this.getRoot().getValues();
	             this.app.callEvent("onDataEditStop", [data]);
                 }} 
            ]};
        }
    });

Dispatch event

Listen to event

Webix Events

    export default class DataView extends JetView {
        config(){
             return { 
                 view:"datatable", id:"table"
             };
        }
        init(view){
            this.on(view, "onAfterSelect", (id) => {
	        if(view.exists(id)){
                    //do something
                }
	    });
        }
    });

Listen to event

* only for views within one module

App level events

    app.attachEvent("app:error", function(err){
        alert("Error");
    });
    
    app.attachEvent("app:error:resolve", function(err, url) {
        webix.delay(() => app.show("/some"));
    });

Bad events

Good events

    app.attachEvent("app:render", function(view,url,result){
        if (result.ui.view === "button")
            result.ui.disabled = true;
    });
    
    app.attachEvent("app:route", function(url1, url2, ...){ 
        if(url2)
            webix.ajax(url2);
    });
    
    app.attachEvent("app:guard", function(url, view, nav){
        if (url.indexOf("/admin") !== -1){
            nav.redirect = "/top/start";
        }
    });

Services

    export default class DataView extends JetView {
        config(){
            return { rows:[
                { view:"button", value:"Add new",  click:() => {
		    if(!this.app.getService("state").getState())
		        //show window with form
		    }
	        }
            ]};
        }
    });
    export default class Toolbar extends JetView {
        config(){
            view:"toolbar", elements:[
                { view:"checkbox", label:"Readonly", on:{
                    onChange:(value) =>{ 
                        this.app.getService("state").setState(value);
                    }
                }}
            ]
        }
        init(){
            this.app.setService("state", {
                 getState:() => { return this.state; },
                 setState:(state) => { this.state = state; }
            });
        }
    });

Define service

Use service

Services

    //any view
    { view:"button", value:"Add new",  click:() => {
        if(!this.app.getService("state").getState())
	    //show window with form
    }}
     //helpers/state.js
    export function State(app){
	const service = {
	    getState(){
		return this.state;
	    },
	    setState(state){
		this.state = state;
	    },
            state:0
	};
	app.setService("state", service);
    }

Define service

Use service

    //myapp.js
    import {state} from "helpers/state.js";
    
    app.render();
    app.use(state);

Services

    //any view
    { view:"button", value:"Add new",  click:() => {
        if(!this.app.getService("state").getState())
	    //show window with form
    }}
     //helpers/state.js
    export default class State {
        constructor(app){
	    this.app = app;
            this.state = 0;
	}
	getState(){
	    return this.state;
	}
	setState(state){
            this.state = state;
	}
    }

Define service

Use service

    //myapp.js
    import State from "helpers/state.js";
    
    app.render();
    app.setService("state", new State(app));

Parameters

Initialization

Data loading

Events

Services

Actions

Multiple receivers

State requests

Global helper

Plugins

import {JetView, plugins} from "webix-jet";

export default class TopView extends JetView {
    init(){
        this.use(plugins.Menu, "top:menu");
    }
}

View-level plugins

App-level plugins

  • Menu
  • UnloadGuard
  • Status
  • UrlParam
  • User
  • Locale
  • Theme
import {JetApp, plugins} from "webix-jet";

const app = new JetApp({ ... });
app.render();
app.use(plugins.Locale);

Menu Plugin

    import {JetView, plugins} from "webix-jet";

    export default class TopView extends JetView {
        config(){
            return {
                cols:[
                    { view:"menu", id:"top:menu", data:[
                        { id:"start", value:"Start page"},
                        { id:"data", value:"Data page"}
                    ]},
                    {}
                ]
            }
        }
        init(){
            this.use(plugins.Menu, "top:menu");
        }
    }
  • navigates according to item id (configurable)
  • selects menu item on page load
  • can switch url parameter rather than url

Localization

    //view.js
    export default class SettingsView extends JetView {
        config(){
            const _ = this.app.getService("locale")._;       
    
            return {
                rows:[
                    { label:_("Language"), view:"segmented" }
                ]
            };
        }
    }
    //app.js
    import {JetApp, plugins} from "webix-jet";
    
    const app = new  JetApp();

    app.use(plugins.Locale);
    app.render();

Enabled on app level

Used in each view

Links

Demo:

Task for practice:

Follow-up text: click here

3-2. Webix Jet: Interaction

By ihelga

3-2. Webix Jet: Interaction

Navigation within Jet applications. Establishing ties between app parts.

  • 655