📜 ⬆️ ⬇️

JohnSmith - a simple and lightweight JavaScript framework for building UI



To date, there are a lot of JavaScript libraries to create rich-client applications. In addition to the well-known Knockout, Angular.JS and Ember there are a great many other frameworks, and each has its own peculiarity - someone promotes minimalism, and someone - ideological purity and compliance with the MVC philosophy. With all this diversity, more and more new libraries appear regularly. From the last that was mentioned on Habré - Warp9 and Matreshka.js . In this regard, I want to talk about my own crafts, meet, JohnSmith - a simple and lightweight JavaScript framework for building UI.



')
First of all, I would like to say that JohnSmith was not written for the sake of some academic interest and not to eliminate that fatal flaw. Quite the contrary, JohnSmith was born in a real project, then migrated from project to project, gradually improving and changing its shape. And now it has materialized as a full-fledged open-source library.


Example



To demonstrate the capabilities of JohnSmith, we will write the simplest application with the following functionality:

There is an input field in which the user writes his name. As soon as the name is entered, we show the message: Hello,% username% .

Who wants to immediately see the result: here is a ready User Greeter .



View Model



Let's start with the creation of the View Model, and first of all, write the "class":

var GreeterViewModel = function(){ } 


The View Model usually “exposes” objects to the outside world, changes of which can be tracked from the outside. In JohnSmith, these objects are called bindable. Add a field to store the user name:

 var GreeterViewModel = function(){ this.userName = js.bindableValue(); }; 


This field ( userName ) will be used for bidirectional binding in the View. Add another field that will form the message text. This field depends on userName , so we describe it as dependentValue :

  var GreeterViewModel = function(){ this.userName = js.bindableValue(); this.greetMessage = js.dependentValue( this.userName, function(userNameValue){ if (userNameValue) { return "Hello, " + userNameValue + "!"; } return "Please, enter your name"; }); }; 


js.dependentValue is similar to computed in knockout, except that we manually specify dependencies in JohnSmith, since there's no auto-tracking magic behind the scenes.

Model View is ready, now we will describe View.



View



Let's start by creating a class:

 var GreeterView = function(){ } 


A view is a collection of markup and the logic of the connection between this markup and the outside world. The markup is described in the template field, and the logic is described in the init method:

 var GreeterView = function(){ this.template = "...  ..."; this.init = function(){ //    } }; 


In our test example, the markup is pretty simple, so we write it directly in the template field:

 var GreeterView = function(){ this.template = "<p>Enter your name: <input type='text'/></p>" + "<p class='message'></p>"; this.init = function(){ //     }; }; 


Now go to the init method. First, JohnSmith implies that each View works with a specific View Model, so we add the viewModel parameter:

 var GreeterView = function(){ this.template = "<p>Enter your name: <input type='text'/></p>" + "<p class='message'></p>"; this.init = function(viewModel){ // <--- //     }; }; 


Next, our task is to associate the properties of the View Model with the markup that our View "draws". JohnSmith provides the syntax for configuring this link directly in js code. For our case, it will look like this:

 var GreeterView = function(){ this.template = "<p>Enter your name: <input type='text'/></p>" + "<p class='message'></p>"; this.init = function(viewModel){ this.bind(viewModel.userName).to("input"); // <--- this.bind(viewModel.greetMessage).to(".message"); // <--- }; }; 


Now everything is ready and we only need to draw our view (it is assumed that there is an element on the page with id = 'greeter'):

 js.renderView(GreeterView, new GreeterViewModel()).to("#greeter"); 


So, on this our mini-application is finished, the result can be seen here . This example demonstrates the basic philosophy of the framework, but in order to learn more about the capabilities of JohnSmith, let's clarify some details.



Binding



The basis of binding in JohnSmith is observable objects (as in knockout). These objects are created using one of the following methods:



Directly binding object A and listener B is configured by the code of the form:

 js.bind(A).to(B); 


For example:

 var firstName = js.bindableValue(); //   js.bind(firstName).to(".firstName"); //   jQuery- firstName.setValue("John"); //    


Inside the view, the binding code changes slightly:

 //    init   this.bind(viewModel.firstName).to(".firstName"); 


And in this case, the search by .firstName selector will work only inside the markup of this View, and not in the whole document. This ensures complete independence of the form from the external environment.

The js.bind(A).to(B) syntax allows you to combine the “declarative” style with the imperative one and use the jQuery-style in those cases where it is necessary:

 //      : js.bind(firstName).to(".firstName"); js.bind(firstName).to( function(newValue, oldValue){ // <--      //     jQuery  , // , / -    //   newValue/oldValue,  ,    .. }); 


If you pass a normal (non-observable) value as a bindable object, then a one-time synchronization with the interface will occur. This allows you to consistently process both observable and “regular” View Model fields:

 var ViewModel = function(){ this.firstName = "John"; // static value this.lastName = js.bindableValue(); // observable value }; //... // somewhere in the View: this.bind(viewModel.firstName).to(".firstName"); // will sync only once this.bind(viewModel.lastName).to(".lastName"); // will sync on every change 


For rendering complex objects, a child View can be used:

 var ViewModel = function(){ this.myAddress = js.bindableValue(); this.initState = function(){ this.myAddress.setValue({ country: 'Russia', city: js.bindableValue(); }); }; }; // ... this.bind(viewModel.myAddress).to(".address", AddressView); // ... 




Views Composition



The view in JohnSmith is the atomic unit for building the interface. Each species is completely independent and provides reusability. The interface of the entire application is made up of individual Species, by building a "tree" ( composite pattern ). That is, there is one main view, it has child views, each of the children has its own child views, etc. Composition is achieved in several ways:



As a small demonstration of the composite view, the file tree is:


Conclusion



As a conclusion, we denote the features of JohnSmith:



repository on github
That's all, thank you for your attention, waiting for constructive criticism!

Source: https://habr.com/ru/post/203890/


All Articles