Mega-ui (beta)
Frontend MVVM framework like Angular 2 and React, but better =)
Don't use it! Please don't install it! Don't use! This is experiment I change API every day. And I come up with something new every day. It is not even beta, even not alpha! It's just my dream of an ideal framework =)
If you still want to immerse yourself in a fairy tale:
- Use ES6 with Babel
- Use Webpack to build application.
- Use Webpack loader to hot components livereload.
- Use Shadow DOM emulation and Shadow Styles emulation in components.
- Extend components cuz its just javascript classes.
- Enjoy =)
Basic example:
index.js:
// starting root component in bodyui
app.js:
static template = ` Hello {{name}} !!! ` { thisname = 'World' }
its render to:
Hello World !!!
Component example:
// this is custom html element name static tag = 'button' // default Class.tag = Class.name.toLowerCase() // this is shadow style (isolated style for component) // we can use any selectors, even * or tag names, // and they will act only within the component template static style = // webpack loader as css text // child components to be used in a template static components = // template static template = ` <div>Button text</div> ` { // This property will be seen in template as {{prop}} thisprop = 'val' }
usage:
// button component will be used in template static components = Button static template = ` Demo buttons: <button></button> <button></button> `
its render to:
Demo buttons: Button text Button text
In style we can use :host
selector to styling root component element.
If selector name is button
, after rendering component name is ui-button
,
to avoid conflict with the built-in browser elements.
ui-button element hav not display style, and you need set it:
This is a typical situation for the ShadowDOM
Global components
Add button to global component (used in all component templates)
Replace all button
tags in application to Button component.
ui
Template features
{{ 2+3 }} javascript expression injection undefined is interpreted as an empty string Hello {{ "World" }} !! Component fields available in expressions, and when field will change, then expression will instantly recalculate. Cuz framework use getters and setters to watch data changes. Global window fields not available in expressions, only component instance fields available. Expressions works even in all attributes Alias for class='class-name' if expression is true, class added to element, if false, class removed Call handler($event) on click event. In expression available $event variable for access to the event. If in javascript we emit custom event, element.emit('ololo', 11) its works too. $event === 11 Own click on element, not on children Directives like angular. Directive can control the behavior of the framework at a low level. {{ item }} -- On this place is replaced with the item value Repeat element by array. Current array element available as item. in - keyword item - you to choose a name variable array - expression that returns array or number Syntax variations: {{ item }} 1, 2, 3, 4, 5 Two way data binding element.prop = exp Example: Its automatic create text variable and we can use it {{ text }} We can bind even paths prop.prop.prop If in expression we use {{ }} brackets, Expression result interpreted as string Example: Expression {{ 100 }}px return string "100px" If element is component, data bind apply to component instance, instead of the host element component. In this way we can set components options If we want access to host element of component, we can use host property: All components has 3 mandatory properties: host - root element of component scope - scope of component app - reference to the root component ui.bootstrap(Component) Thus, in any place, in any terms, we can turn to the root component of the application, and take from there needed value. Create link to element in scope, and we can use it: {{ myInput.value }} If applying to component, link will refer to the component instance: content show popup In component links are available as as this.scope.myPopup
.classes
- .class-name // add class name alias class='class-name'
- .active='expression' // if expression is true then add class, if false then remove class
*directives
- *for="item in arr" // iterate element
- *if="exp" // create or remove element with expression
(events)
- (click)='handler($event)' // with click call
handler
function ($event is a internal variable) - (custom-event)='handler($event)'
<content>
if you use content element in you template, then original html content from host element, It will be placed there:
static tag = 'button' static template = ` Content: <content></content> ` static components = Button static template = ` <button>Ololo</button> <button> <span>Trololo</span> </button> `
render to:
Content: Ololo Content: Trololo
We can use select attribute to filter elements which will be replaced here:
Component methods
framework automatically extend your components classes adding following fields:
{ thisscope // Link to scope thishost // Link to root element thisapp // Link to root component on application } { // Set event handler to host // Alias for this.host.on(eventName, handler) } { // Set event handler to host, // event handler will be removed after the first call // Alias for this.host.one(eventName, handler) } { // Remove event handler to host // Alias for this.host.off(eventName, handler) } { // Set own event handler to host // Alias for this.host.own(eventName, handler) } { // Emit event from host // Alias for this.host.emit(eventName, value) } { // Watch expression with this as context, // and call handler if expression value will change. // For example, we can use watch prop of component: this thisprop = 11 // instantly call console.log(11), // cuz framework use end getters and setters thisa = 2 thisb = 4 this thisa = 100 // instantly call console.log(104) } { // Alias for: this // Example: thisa = 100 thisb = 200 this thissum // 300 thisb++ thissum // 301 } { // Alias for: this } { // Return parent component by tag. // If not found, then throw error. var tabs = this tabs // tabs component instance // If we no need throw errors, // we must add '?' symbol at the end. var form = this form // or form component or null }
Memory leak
Don't use setTimeout
, setInterval
, setImmediate
and requestAnimationFrame
.
Use ui.timeout()
, ui.interval()
, ui.immediate()
, ui.frame()
instead.
Parameters can be transferred in any order:
uiui
clearInterval analog:
// start intervalinterval = ui // stop intervalinterval
Automatic stop if component destroy:
static template = `value is: {{value}}` { thisvalue = 0 ui } static components = Panel static template = ` <Panel *if='state'></Panel> <button (click)='destroyPanel()'>Destroy!</button> ` { thisstate = true } { thisstate = false }
If user click "destroy panel" button, component Panel will be destroyed, and interval will be stopped automatically.
This is protection against memory leaks.
Actually you can destroy interval manually:
{ thisinterval = ui } { thisinterval }
But it's not as beautiful and convenient as automatic garbage cleaning =)