UPD: The article is actual for Matreshka 1. Check out a special repository for examples and tutorials to get the latest information about Matreshka 2.

(Repo on Github)

Hi there! In this article I’ll tell you how to use Matreshka with the help of three simple examples. We’ll explore basic features of Matreshka, get to know how to work with data and study collections.

Let me remind you that Matreshka is a front-end framework which complies with the principles, such as

No logic in HTML

Entity minimum

Arbitrary architecture

Matreshka implements a simple two-way data binding syntax and makes active use of accessors (getters and setters).

this.bindNode("x", "input.my-node");

this.on("change:x", function() {

alert(this.x);

});

this.x = "Wow!";

A programmer has access to data like to ordinary object properties, and collections independently generate HTML tree while adding, deleting and sorting elements. Having specified a few rules described in the documentation, you don’t need to pay attention to the view state — you just work with data only.

1. Hello World!

Let’s start with something very simple: add necessary scripts to the page and bind “x” property to two nodes on the page: text field (two-way binding) and ordinary <div> (one-way binding). Output the message to the console on property changing.

First, let’s create an HTML file.

Now let’s create a JS file — js/app.js with the following content:

Now open the console and enter:

app.x = "Wow!";

As you may notice, three things have happened:

The input field value has been updated The content of HTML block has been updated The information about “x” being changed, has been displayed in the console

On the text entering the text field:

“x” property has been updated The content of HTML block has been updated The information about “x” being changed, has been displayed in the console

As you see, it isn’t necessary to catch the input event in the text field manually; you don’t have to set values to HTML nodes manually on property being changed; you don’t need to announce a descriptor yourself.

Remember, it even works in Internet Explorer 8.

2. The form of authorization. Get acquainted with the «model» (Matreshka.Object)

The following example is the implementation of the authorization form on the site. We have got two text fields: login and password. There are two checkboxes: «show password» and «remember me». There’s one button: «sign in». Let’s say that the validation of the form is completed when the login length is not less than 4 characters and the password one — 5 characters.

A little theory: Matreshka.Object plays the role of the class which creates objects of a key-value type. In each class instance the properties which are responsible for the data (the ones that will be passed to the server, for example) can be separated from other properties (the ones that the server doesn’t need but define the application behaviour). In this case, login, password and “remember me” are the data which we send to the server, but the property that defines if the form is valid is not passed.

You can find more detailed and actual information about this class in the documentation.

So, let’s create a class which is inherited from Matreshka.Object (or MK.Object for short).

var LoginForm = Class({

"extends": MK.Object,

constructor: function () {

// …

}

});

As “the application” is very small, all logic can be placed in the class constructor.

First of all, let’s declare default data.

.jset({

userName: "",

password: "",

rememberMe: true

})

jset method does not only sets values but it also declares properties which are responsible for the data. That is “userName”, “password” and “rememberMe” must be passed to the server (in this example we will just print JSON on the screen).

Let’s declare “isValid” property which depends on “userName” and “password” properties. On changing any of these properties (from the code, console or with the help of the bound element), “isValid” property will also be changed.

.linkProps( "isValid", "userName password",

function(userName, password) {

return userName.length >= 4 && password.length >= 5;

})

“isValid” will equal “true”, if the length of a user’s name is not less than four characters and the length of a password — five. linkProps method is another cool feature of the framework. Some properties can depend on other ones which depend on the third ones and the third ones can totally depend on the properties of some other object. Meanwhile, you are protected from the iterative references. The method stops working if it finds dangerous dependencies.

Now let’s bind the object properties to the elements on the page. First, declare a “sandbox”. The “sandbox” is necessary for restricting the instance influence with the help of one element on the page and avoiding conflicts (for example, if there are two elements with the same class on the page). Next bind the other elements.

“bindNode” method is responsible for two way data binding. You can find out about it in the documentation.

// alternative syntax of the method allows to pass a key-element

// object as the first argument,

// it reduces the code quantity a bit

.bindNode({

sandbox: ".login-form",

userName: ":sandbox .user-name",

password: ":sandbox .password",

showPassword: ":sandbox .show-password",

rememberMe: ":sandbox .remember-me"

})

As you see, for the other elements a nonstandard “:sandbox” selector is used, which refers to the “sandbox” (to the element with “.login-form” class name). In this case it isn’t obligatory as the page contains only our form. Otherwise, if there are a few forms or other widgets on the page, it is strongly recommended to restrict the chosen elements by sandbox.

Then bind the button which is responsible for the form submission to “isValid” property. When “isValid” equals true, add “disabled” class name to the element, when it equals false — remove it. This is an example of the one-way binder, i. e. the object property value influences the state of HTML element, but not vice versa.

.bindNode("isValid", ":sandbox .submit", {

setValue: function(v) {

$(this).toggleClass("disabled", !v);

}

})

Instead of the above notation, you can use a shorter one:

.bindNode("isValid", ":sandbox .submit",

MK.binders.className("!disabled"))

See the documentation for binders object.

Bind the password field to the “showPassword” property and change the input type depending on the property value (“:bound(KEY)” is the last non-standard selector).

.bindNode("showPassword", ":bound(password)", {

getValue: null,

setValue: function(v) {

this.type = v ? "text" : "password";

}

})

“getValue: null” means that we override the standard framework behavior on binding the form elements.

Add the form submission event.

.on("submit::sandbox", function(evt) {

this.login();

evt.preventDefault();

})

“submit” is an ordinary DOM or jQuery event, “sandbox” is our form (“.login-form”). Such an event and a key must be separated by the double colon.

This is syntactic sugar of DOM events, i. e. the event can be added in any other way which includes the use of addEventListener:

this.nodes.sandbox.addEventListener("submit", function() { … });

In the handler we call “login” method, which will be declared below and we prevent the reloading of the page, cancelling the browser standard behaviour by using preventDefault.

The final touch is “login” method. For example, the method displays the resulting object on the screen if the form is valid. In the real application the function content must obviously be ajax request to the server.

login: function () {

if(this.isValid) {

alert(JSON.stringify(this));

}

return this;

}

In the end we create a class instance.

var loginForm = new LoginForm();

You can open the console again and change properties manually:

loginForm.userName = "Chuck Norris";

loginForm.password = "roundhouse_kick";

loginForm.showPassword = true;

Cool?

The live example link

3. The user list. Let’s see into collections (Matreshka.Array)

We have considered the data of a key-value type. Now it’s time to see into collections. Let’s say the task is to display the list of some people as a table.

So as not to make the example more complicated, let’s place the prepared data into data variable.

(the names and phone numbers have been obtained with the help of the random-number generator)

At the beginning, as usual, let’s create HTML layout.

<table class=”users”>

<thead>

<th>Name</th>

<th>Email</th>

<th>Phone</th>

</thead>

<tbody><!- the list of users will be here -></tbody>

</table>

Declare “Users” collection which is inherited from Matreshka.Array (MK.Array — for short).

var Users = Class({

"extends": MK.Array,

...

});

Set “itemRenderer” property which is responsible for the way the elements of the array will be rendered on the page.

itemRenderer: "#user_template",

In this case, the selector, referring to a template in HTML code, has been given as a value.

<script type="text/html" id="user_template">

<tr>

<td class="name"></td>

<td class="email"></td>

<td class="phone"></td>

</tr>

</script>

itemRenderer property can get other values, including function or HTML string.

And set the value of Model property, defining the class of elements which are contained in the collection (users of Backbone must be familiar to such syntax).

Model: User,

We will create “User” class a bit later, let’s define the constructor of the newly-created collection class first.

constructor: function(data) {

this

.bindNode("sandbox", ".users")

.bindNode("container", ":sandbox tbody")

.recreate(data);

}

While creating the instance class

“sandbox” property is bound to “.users” element creating a sandbox (class boundary effect on HTML).

container property is bound to “:sandbox tbody” element determining HTML node where the rendered array elements will be inserted into.

add the passed data to the array with the help of recreate method.

Now declare “Model”. “User” class which is inherited from the familiar Matreshka.Object.

var User = Class({

"extends": MK.Object,

constructor: function(data) { ... }

});

Set in the data passed to the constructor with the help of jset method.

this.jset(data);

Next, wait for “render” event which only fires out when the corresponding HTML element has been created but it hasn’t been inserted into the page yet. Bind the relevant properties to the corresponding HTML elements in the handler. When the property value is changed, “innerHTML” of the set element will be changed as well.

this.on("render", function() {

this

.bindNode({

name: ":sandbox .name",

email: ":sandbox .email",

phone: ":sandbox .phone"

}, {

setValue: function(v) {

this.innerHTML = v;

}

});

})

In the end, create the instance of “Users” class, having passed the data as an argument

var users = new Users(data);

That’s it. On page reloading you will see a table with the list of users.

The link to the live example

Now open the dev console and type:

users.push({

name: "Gene L. Bailey",

email: "bailey@rhyta.com",

phone: "562–657–0985"

});

As you see, a new element has been added to the table. And now call

users.reverse();

or any other array method (sort, splice, pop…). Besides its own methods, MK.Array contains all the methods of a standard JavaScript array without any exception. Then,

users[0].name = "Vasily Pupkin";

users[1].email = "mail@example.com";

As you see, you don’t have to watch changes in the collection manually, the framework catches data changes and alters DOM by itself. It’s incredibly convenient.

Remember, MK.Array supports its own set of events. You can catch any change in the collection: adding, deleting, re-sorting of elements with the help of on method.

users.on("addone", function(evt) {

console.log(evt.added.name);

}); users.push({

name: "Clint A. Barnes"

});

(it will print the name of the added user in the console)

As another example of the collection, you can consider the implementation of a simple music search engine, based on Soundcloud API.

4. TodoMVC

Now, when we have considered Matreshka capabilities using simple examples, have a look at the implementation of the famous reference application. The description to the code is here, and here is the repo.