Version 1.x
https://www.gitbook.com/book/webix/webix-jet/details
https://github.com/webix-hub/webix-adminapp-demo
https://github.com/webix-hub/webix-jet
https://github.com/webix-hub/jet-demos
Docs
Big demo
Sources
Sources
class Box extends Shape {
    methodA(a){
       webix.confirm(a, function(){ this.done(); });
    }
    methodB(b){
       webix.confirm(b, () => this.done() );
    }
    done(){
        webix.alert("Done");
    }
}
const C = 123;
export Box;
export default C;import {Box, C} from "some";
export AnyName from "some";  //AnyName == C- close the demo app
git clone https://github.com/webix-hub/jet-start- install toolchain an start the app
cd jet-start
yarn install
yarn start- open app in the browser
http://localhost:8080- check code quality
yarn lint- get files for deploy
yarn buildAutomatic app updates
Check console for build errors
/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.
export default {
	template:"Start page"
};
export default class TopView extends JetView{
	config(){
		return {
			type:"line", cols:[
				{ view:"sidebar" }
                                { template:"Some here"}
			]
		};
	}
}/views/start.js
/views/top.js
import {JetView} from "webix-jet";
import {data} from "models/records";
export default class DataView extends JetView{
	config(){
		return { view:"datatable", autoConfig:true };
	}
	init(view){
		view.parse(data);
	}
}/views/data.js
Simple
Lifetime handlers
Custom methods
Local variables
Extending
export default class TopView extends JetView{
	config(){
		return {
                   view:"button", click:() => this.doClick("a")
		};
	},
        doClick(){
          alert("clicked");
        }
}export default class TopView extends JetView{
	config(){
		return {
                   view:"button", click:() => this.doClick("a")
		};
	},
        init(){
          this._counter = 0;
        }
        doClick(){
          this._counter++;
          alert("clicked " + this._counter);
        }
}export default class MyTopView extends TopView{
        doClick(){
          alert("Something different");
        }
}Composable blocks
Error resistant
Code reusage
export default {
	template:"Start page"
};
import start from "views/start";
export default class TopView extends JetView{
	config(){
		return {
			type:"line", cols:[
				{ view:"sidebar" }
                                start
			]
		};
	}
}/views/start.js
/views/top.js
// url : localhost:8080/#!/top/start
export default class TopView extends JetView{
	config(){
		return {
			type:"line", cols:[
				{ view:"sidebar" }
                                { $subview:true }
			]
		};
	}
}index.html#!/top/data
index.html#!/top/top/data
index.html#!/data
index.html#!/top
HashRouter
index.html#!/top/data
UrlRouter
index.html/top/data
StoreRouter
EmptyRouter
Can use Navigation keys in browser
Refresh friendly app
Nice for development
export default {
  template:'<a href="#!/top/data">Details</a>'
}export default {
  template:'<a route="/top/data">Details</a>'
}export default {
  view:"button", click:() => this.show("/top/data")
}
// From top.js this.show("./data2")
// From data.js this.show("../data2")export default {
  template:'<a href="#!/top/data?id=3">Details</a>'
}import start from "views/start";
export default class TopView extends JetView{
	config(){
		view:"text"
	}
        init(view, url){
          view.setValue(url[0].params.id);
        }
}export default class TopView extends JetView{
	config(){
		return { view:"button", click:() => {
			this.doSome();
                }}
	},
        doSome(){ alert("done"); }
}export default class TopView extends JetView{
	config(){
		return { view:"button", click:function(){
			this.$scope.doSome();
                }}
	},
        doSome(){ alert("done"); }
}export default class TopView extends JetView{
	config(){
		return { rows:[
                    { view:"button", some:1 },
                    { view:"button", some:2 },
                ] };
	}
        init(view){
            // top level view
            view.addView({ template:"some" });
            // button
            view.queryView({ some:2 }).hide();
        }
}import "./styles/app.css";
import {JetApp} from "webix-jet";
webix.ready(() => {
	var app = new JetApp({
		id:		APPNAME,
		version:	VERSION,
		start:		"/top/start",
                debug:          true
	});
	app.render();
});export default class DataView extends JetView {
      config(){
         return { view:"datatable", autoConfig:true, data:[
             { a:1, b:2 }
         ]}
      }
});BAD
import {someData} from "models/some"
export default class DataView extends JetView {
      config(){
         return { view:"datatable", autoConfig:true }
      }
      init(){
          view.parse(someData);
      }
});GOOD
//models/records
const data = new webix.DataCollection({ data:[
	{ id:1, title:"The Shawshank Redemption", year:1994 },
        /*...*/
]});
export function getData(){ return data; };
import {getData} from "models/records"
export default class DataView extends JetView {
      config(){
         return { view:"datatable", autoConfig:true }
      }
      init(){
          view.parse(getData());
      }
});//models/records
const data = new webix.DataCollection({ 
   url:"/some/records", save:"rest->/some/records"
]});
export function getData(){ return data; };
//models/records
export function getData(){
  return webix.ajax("data.php");
};
import {getData} from "models/records"
export default class DataView extends JetView {
      config(){
         return { view:"datatable", autoConfig:true }
      }
      init(){
          view.parse(getData());
      }
});//models/records
export function getData(){
  return webix.ajax("data.php");
};
export function saveData(id, operation, data){
  return webix.ajax().put("/some/records", data);
};
import {getData, saveData} from "models/records"
export default class DataView extends JetView {
      config(){
         return { view:"datatable", autoConfig:true, save:saveData }
      }
      init(){
          view.parse(getData());
      }
});import {getData} from "models/records"
export default class DataView extends JetView {
      config(){
         return { 
            view:"datatable", autoConfig:true, 
            url:"/some/records"
        }
      }
});import {getData} from "models/records"
export default class DataView extends JetView {
      config(){
         return { 
            view:"datatable", autoConfig:true, 
            url:"/some/records", save:"rest->/some/records"
        }
      }
});Relative small
Used many times
Shared Data
Relative big
Used once
Dynamic Data
AND
OR
Huge
Prototyping
Remote Models
OR
Be pragmatic!
export default class DataView extends JetView {
      config(){
         return { rows:[
              { $subview:true }
         ]}
      }
      init(){
          this.show("./form?id=1")
      }
});export default class FormViewextends JetView {
      config(){
           ...
      }
      urlChange(view, url){
            if (url[0].params.id){
                this.getRoot().setValues( getData(id) )
            }
      }
});export default class DataView extends JetView {
      config(){
         return { rows:[
              { $subview: true }
         ]}
      }
      init(){
          this.app.callEvent("do:clear:form")
      }
});export default class FormViewextends JetView {
      init(){
          this.app.on("do:clear:form", () => this.clear() )
      }
      clear(){
            this.getRoot().setValues({ a:1 })
      }
});export default class DataView extends JetView {
      config(){
         return { rows:[
              { $subview: true }
         ]}
      }
      init(){
          this.app.getService("form").clear();
      }
});export default class FormViewextends JetView {
      init(){
          this.app.addService("form", this)
      }
      clear(){
            this.getRoot().setValues({ a:1 })
      }
});Initialization
Data loading
Actions
Multiple receivers
Bottom -> Up
State requests
Any direction
Single service view
export default class DataView extends JetView {
      config(){
         return { 
            view:"button", click:() => this._popup.show()
        }
      },
      init(){
        this._popup = webix.ui({ view:"window" });
      }
      destroy(){
        this._popup.destroy();
      }
});import {PopupView} from "views/some_popup"
export default class DataView extends JetView {
      config(){
         return { 
            view:"button", click:() => this._popup.show()
        }
      },
      init(){
        //same as webix.ui
        this._popup = this.ui(PopupView);
      }
});export default class DataView extends JetView {
      config(){
		if (this.app.config.access != "writer"){
			return { };
		}
		return { template:"Access denied" };
      }
});app.attachEvent("app:guard", function(url, view, nav){
    if (url.indexOf("/admin") !== -1){
	nav.redirect = "/top/allowed1";
    }
});export default class DataView extends JetView {
      config(){}
      init(){}
      urlChange(){}
      ready(){}
      destroy(){}
});export default class DataView extends JetView {
      init(){
        this.use(plugins.Menu)
      }
});View
App
//app.js
import {plugins} from "webix-jet";
app.use(plugins.Locale);
//view.js
export default class SettingsView extends JetView {
    config(){
        const _ = this.app.getService("locale")._;       
        return {
            type:"space", rows:[
                { template:_("Settings"), type:"header" },
var app = new JetApp({
    debug: true // console.log and debugger on error
});
app.attachEvent("app:error", function(err){
    alert("Error");
});
app.attachEvent("app:error:resolve", function(err, url) {
    webix.delay(() => app.show("/some"));
});mkozhukh@ya.ru
What was missed ?
- App level events
- Fixed subviews
- Named subviews
- App as view
- Skins plugin
- User plugin
- Using webix.remote
- Custom routers
- View decorators
- Async views