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.