Coding a to do app in ... seconds :
A simple "To Do List" app in 31 lines.
Live Result :
Which is useless. But Yama is really lightweight: 7 KB minified.
You love jQuery? Good for you. Yama doesn't need it.
Yes. Why not?
It is a Yaml/Jade-like templating syntax to build the dom tree of your UI, and it binds automatically events and observables to your view object.
It's described as a copyright comment in draw function, so decent minifiers won't touch it.
Javascript is mature enough to have accessor handlers and Yama takes advantage of it.
Observables have seamless js syntax. Yama uses native javascript accessors to handle changes in view objects. It means that you can't get in trouble with digest loop because... you know, there isn't any.
Yama encourages forces you to describe your UI as a component. You can reuse it later inside another component.
It gets :
myComponent.title = "Hi" instead of myComponent.title("Hi")
yama.register({
name : 'myComponent1',
draw : function(){
/*!
root
div text:title
!*/
},
title : "Hello, world !"
});
var myComponent = new yama.components.myComponent1(document.body);
To create a UI, Yama encourages you to write it as a reusable component.
First, you have to name your component with name property. Here we named it myComponent1.
Second, you have to describe the way your component should be drawn. The UI is declared as a comment in draw function. In this example, we tell yama to create a div in the component's root dom and to bind it's text to the component's title property.
And that's it. In this example, we also add a custom property to our component : title. In draw function we told yama to bind the div's inner text to the value of title property with the directive text:title.
Now your component is ready to use. In plain javascript, you can call the constructor new yama.components.myComponent1();
If you want your component to be drawn into a specific dom element : var myComponent = new yama.components.myComponent1(document.getElementById("example1"));
yama.register({
name : 'myComponent2',
draw : function(){
/*!
root
div
div text:title1
div text:title2
div
span text:title3
span text:title4
!*/
},
title1 : "I am here",
title2 : "Me too !",
title3 : "Ho ho",
title4 : "How nice"
});
Here we build a UI with nested div and span elements. The indentation inside the draw function defines the dom tree of your component.
Text inside elements are bound to properties title1 to title4
yama.register({
name : 'myComponent3',
draw : function(){
/*!
root
div text:title
div:elapsedDiv
!*/
},
title : "Elapsed time : ",
});
var elapsed = 0;
var component = new yama.components.myComponent3();
setInterval(function(){
component.elapsedDiv.innerHTML = (elapsed++);
}, 1000);
The syntax div:elapsedDiv creates a div when drawing the component and binds the property elapsedDiv to this dom element.
You have access to this dom element even outside the declaration of your component with myComponent3.elapsedDiv
yama.register({
name : 'myComponent4',
draw : function(){
/*!
root
div text:title
!*/
},
title : "Not clicked yet.",
});
var component = new yama.components.myComponent4();
example4Btn.onclick = function(){
component.title = "Clicked !";
};
Properties of your component like title are actually hidden observables. When you set a new value to myComponent4.title, yama detects it and notifies all observers which are observing this property : in this example, the div created in draw function observes title. So when we assign a new value to title, the text inside the div element gets updated automatically.
Unlike Angular.js, there isn't any digest loop, so the detection works even outside your component's event handlers. In this example, the event is fired from outside, namely from example4Btn.
yama.register({
name : 'myComponent5',
draw : function(){
/*!
root
span:btn text:btnTitle onclick:btnClick
span text:number
!*/
},
number : 0,
btnTitle : "Click here to increment »",
btnClick : function(){
this.number++;
}
});
yama.register({
name : 'myComponent6',
draw : function(){
/*!
root
div
myComponent5
myComponent5
myComponent5
div
myComponent5
!*/
}
});
Now we can reuse our myComponent5 by simply putting its name instead of a native dom element in the draw function. Yama will render myComponent5 instead of a dom element.
Each of them behaves independently.
yama.register({
name : 'myComponent7',
draw : function(){
/*!
root
div:btn text:btnTitle onclick:btnClick
div
myComponent5
myComponent5:componentChild2
myComponent5
div
myComponent5
!*/
},
btnTitle : "Click here to set second component's value",
btnClick : function(){
this.componentChild2.number = 1337;
}
});
This example shows that custom components we wrote act exactly like a native dom element. They are rendered, you can access them and their properties by naming them.
As we saw in the previous example, each of them behaves independently. But their parent has access to their properties.
We named the second instance of myComponent5 : componentChild2. That means that we get a pointer to this specific instance of myComponent5 by calling myComponent7.componentChild2. For example myComponent7.componentChild2.number = 1337; will set the property number of this instance of myComponent5 inside myComponent7.
By clicking on the button you should see the second nested components value set to 1337.
yama.register({
name : 'myComponent8Outside',
draw : function(){
/*!
root
myComponent8Inside onMyFabulousEvent:childFiredEvent
!*/
},
childFiredEvent : function(){
alert("myComponent8Inside fired onMyFabulousEvent");
}
});
yama.register({
name : 'myComponent8Inside',
draw : function(){
/*!
root
div:btn text:btnTitle onclick:btnClick
!*/
},
btnTitle : "Click here to fire myFabulousEvent",
btnClick : function(){
this.onMyFabulousEvent();
}
});
The same way a native dom element like a div can fire onclick event, your custom component can fire custom events as well.
yama.register({
name : 'myComponent9',
draw : function(){
/*!
root
div text:title
div
foreach item:myArray
div text:item
!*/
},
title : "Hello",
myArray : [2, 4, 6]
});
foreach directive in the draw function iterates through an array and renders it's content for each item in the array.
yama.register({
name : 'myComponent9',
draw : function(){
/*!
root
div text:title
div:cont2 text:otherTitle
div:loopCont1
foreach:indVar1 iterator1:list1
div:it1cont
div text:it1text
div text:indVar1
div text:iterator1.title
div:loopCont2
foreach:indVar2 iterator2:iterator1.list2
div:it2cont
div text:it2text
div text:indVar1
div text:indVar2
div text:iterator2
!*/
},
it1text : "index1",
it2text : "index2",
title : "Hey",
otherTitle : "Other title",
list1 : [
{
'title':"onur",
'list2':[1,2,3]
},
{
'title':"test",
'list2':[4,5,6]
}
]
});
You have access to the index of the iterator during the iteration. We just name the index variable with foreach:indVar1 and foreach:indVar2.
yama.register({
name : 'myComponent11',
draw : function(){
/*!
root
div
div text:title
input value:title
!*/
},
title : "Initial title here, write something :"
});
Changes in the input element are bound to title property of our component. As each property is a hidden observable, when the input changes the value of title, all observers, namely the div which is observing for it's text attribute gets notified and it's inner text is set to the value of title as well.