Paiza Engineering Blog

Engineering blog of browser-based web development environment PaizaCloud Cloud IDE ( https://paiza.cloud/ ), online compiler and editor Paiza.IO( https://paiza.IO/ )

Data binding code in 9 JavaScript frameworks

f:id:paiza:20150320120255p:plain

f:id:paiza:20140712194904j:plain (by Yoshioka Tsuneo, @ at https://paiza.IO/)

(Japanese article is available here.)

The JavaScript frameworks comes every now and then. React.js, Ractive.js, Aurelia.js, and forthcoming Angular2 !

Each framework have their own features, but what's the difference of these frameworks ? From where, should I start?

I just tried to write sample code in 9 major framework, and share the experience.

  • Backbone.js
  • Ember.js
  • Knockout.js
  • AngularJS(1.x)
  • React.js
  • Ractive.js
  • Vue.js
  • Aurelia.js
  • AngularJS2.0(alpha)

These frameworks typically have some of features below.

  • MVC/MVVM model
  • Data bindings between HTML and JavaScript
  • HTML template
  • URL routing
  • RESTful API
  • Custom directive
  • DI(Dependency Injection)

A framework may or may not contains these features, but one common and the most important feature is data binding between HTML representation and JavaScript code.

So, let's focus on the data binding and go through all 9 frameworks by writing code.

Sample code

The sample web app have two input form, first name and last name, and one output area for full name. Just after changing first name or last name, full name is updated immediately.

f:id:paiza:20150310154810p:plain

This is quite simple program, but we can see important framework behavior like:

  • How to represent variables, controller, mode, in both HTML and JavaScript.
  • How to transfer input value in HTML to JavaScript variable.
  • How to observe JavaScript variable and transfer it to HTML.

Framework comparison

So, let's go through 9 frameworks. We go through roughly in chronological order.

0. No framework(jQuery or Vanilla JS)

http://jquery.com/

HTML:

First Name: <input id="firstName"/><br>
Last Name: <input id="lastName"/><br>
Full Name: <span id="fullName"></span><br>

JavaScript:

function updateFullName(){
    var fullName = $("#firstName").val() + " " + $("#lastName").val();
    $("#fullName").text(fullName);
}
$("#firstName, #lastName").bind("change keyup", function(){
    updateFullName();
});
$("#firstName").val("Taro");
$("#lastName").val("Yamada");
updateFullName();

(Sample: http://jsfiddle.net/yoshiokatsuneo/4va165n5/ )

Before trying frameworks, let's write code without framework.

jQuery is powerful enough to handle the task with simple code. But, the code have problems. At first, data is represented not as JavaScript variable but as string in jQuery selector, like "#firstName" or "#lastName" preventing refactoring. jQuery object is just coupled with DOM and act like global variables.

The worst problem is what data exists only on HTML(DOM), and is not structured.

1. Backbone.js

HTML:

<div id="person">
    First Name: <input id="firstName" value=""><br>
    Last Name: <input id="lastName" value=""><br>
    Full Name: <span id="fullName"></span>
</div>

JavaScript:

Person = Backbone.Model.extend({});
PersonView = Backbone.View.extend({
    el: '#person',
    events: {
        'change': 'change',
    },
    initialize: function(){
        this.listenTo(this.model, 'change', this.render);
        this.render();
    },    
    change: function(){
        var firstName = $('#firstName').val();
        var lastName = $('#lastName').val();
        this.model.set({firstName: firstName, lastName: lastName});
    },
    render: function(){
        this.$('#firstName').val(this.model.get('firstName'));
        this.$('#lastName').val(this.model.get('lastName'));
        var fullName = this.model.get('firstName')
                        + ' ' + this.model.get('lastName');
        this.$('#fullName').text(fullName);
    },
});
person = new Person({lastName: "Yamada", firstName: "Taro"});
personView = new PersonView({model: person});

(Sample: http://jsfiddle.net/yoshiokatsuneo/5u9czbwe/ )

Backbone.js itself is a simple framework, having Model class and View class for MVC model

Backbone.js introduced structure with MVC model, and put data model instead of just in HTML DOM. These modularization helps to work for larger application.

HTML behavior is handled by View class that may be nested. Each view have their own model, but model and HTML is completely separated. HTML and View class and communicated through jQuery's val()/text() functions or event monitoring. View class and Model class are communicated through model's set/get function or event monitoring(listenTo()).

There is no fancy feature like data binding and we need to write code explicitly for each behavior. But, this makes each module independent, and makes Backbone.js large application friendly.

2. Ember.js

index.html:

<script type="text/x-handlebars" data-template-name="index">
First Name:{{input type="text" value=firstName}}<br/>
Last Name:{{input type="text" value=lastName}}<br/>
Full Name: {{model.fullName}}<br/>
</script>

JavaScript:

App = Ember.Application.create();
App.Person = Ember.Object.extend({
  firstName: null,
  lastName: null,

  fullName: function() {
    return this.get('firstName') + ' ' + this.get('lastName');
  }.property('firstName', 'lastName')
});

var person = App.Person.create({
  firstName: "Taro",
  lastName:  "Yamada"
});

App.Router.map(function () {
});

App.IndexRoute = Ember.Route.extend({
    model:function () {
        return person;
    }
});

(Sample: http://jsfiddle.net/yoshiokatsuneo/gLkq1sd5/ )

Unlike BackboneJS where we need to write data and JavaScript relation explicitly, Ember.js introduced data binding. Data on HTML and JavaScript is automatically updated each other.

Ember.js introduced "{{}}" syntax where we can write variables to bind to JavaScript model. Input data("firstName" or "lastName") automatically update model. For about dependency, we explicitly specify the dependency using "property" function to calculate new value when depended variable is updated.

By the data biding, we no more need to write event handling explicitly.

3. Knockout.js

HTML:

<p>First name:<input data-bind="value: firstName" /></p>
<p>Last name:<input data-bind="value: lastName" /></p>
<p>Full name:<span data-bind="text: fullName"></span></p>

JavaScript:

function AppViewModel() {
    this.firstName = ko.observable("Taro");
    this.lastName = ko.observable("Yamada");

    this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();    
    }, this);
}

// Activates knockout.js
ko.applyBindings(new AppViewModel());

(Sample: http://jsfiddle.net/yoshiokatsuneo/3q880ohq/ )

As Ember.js, Knockout.js have data binding between HTML and JavaScript. Ember.js specify dependency explicitly but Knockout.js specify variables to be observed using "ko.observable".

On the code, data written like "data-bind=“value: firstName” in HTML is tied to variable in ViewModel. On ViewMode, variable changing is detected by specifying variable as "observable()". When variables change, data specified as "ko.computed" is updated automatically, and reflected to HTML representation.

4. AngularJS(1.x)

HTML:

<div ng-app ng-controller="PersonController">
First Name: <input type=text ng-model="firstName"> <br>
Last Name: <input type=text ng-model="lastName"><br>
Full Name: {{getFullName()}}
</div>

JavaScript:

function PersonController($scope) {
  $scope.firstName = "Taro";
  $scope.lastName  = "Yamada";
  $scope.getFullName = function() {
    return $scope.firstName + " " + $scope.lastName;
   };
}

(Sample: http://jsfiddle.net/yoshiokatsuneo/pqku2r33/ )

AngularJS is full-stack framework with two-way binding, routing, RESTful API, DI.

Ember.js or Knockout.js introduced data binding, but we need to specify dependency explicitly. Whereas, AngularJS automatically re-calculated depended data without writing dependency, and it makes AngularJS code much simpler.

On HTML, we can use "Angular expression" like model="DATA" or "{{}}". Those data or expression is tied to variable or method in controllers.

We can just use normal variable representing data in controller, without using syntax to specify observation. Whenever data changes, AngularJS detect the change and re-calculate depended data or function or HTML expression.

5. React.js

JavaScript:

var MyApp = React.createClass({
  getInitialState: function(){
      return {
          firstName: this.props.firstName,
          lastName:  this.props.lastName,
      }
  },
  handleChange: function(){
      var firstName = this.refs.firstName.getDOMNode().value;
      var lastName = this.refs.lastName.getDOMNode().value;
      this.setState({
          firstName: firstName,
          lastName: lastName,
              });
  },
  render: function() {
    var fullName = this.state.firstName + this.state.lastName;
    return (
        <div>
        First name: <input ref="firstName" onChange={this.handleChange} value={this.state.firstName}/><br/>
        Last name: <input ref="lastName" onChange={this.handleChange} value={this.state.lastName}/><br/>
        Full name: {fullName}
        </div>);
  }
});

React.render(<MyApp firstName="Taro" lastName="Yamada" />, document.body);

(Sample: http://jsfiddle.net/yoshiokatsuneo/k5d6jhhb/ )

React.js is a framework specialized for data binding, it also simplify data binding utilizing VirtualDOM. I

AngularJS have two-way binding. But, we need to update JavaScript models as the same way we intended to update HTML DOM. To add one item in HTML DOM, just specifying the final model is not enough but we need to add one item in the model.

React.js automatically detect how the DOM is updated without explicitly specifying how to update, and only the difference is applied to DOM. So, we no more need to manage how to change model.

On code, React.js embed HTML representation on JavaScript code using JSX syntax.

Embedded HTML is not directly updated to DOM on HTML, but stored as VirtualDOM. When we need to change state, we use setState() function. Then React.js detect the difference on VirtualDOM and update state difference to HTML. React.js have smaller performance impact because React.js does not directly manipulate DOM in HTML but manipulate VirtualDOM and only update the changing.

6. Ractive.js

HTML:

<script type="text/reactive" id="tpl">
First Name:<input type="text" value="{{firstName}}"/><br/>
Last Name:<input type="text" value="{{lastName}}"/><br/>
Full Name: {{fullName()}}<br/>
</script>
<div id='container'></div>

JavaScript:

var ractive = new Ractive({
  el: 'container',
  template: '#tpl',
  data: {
    firstName: 'Taro',
    lastName: 'Yamada',
    fullName: function () {
        return this.get( 'firstName' ) + ' ' + this.get( 'lastName' );
    }
  },
});

(Sample: http://jsfiddle.net/yoshiokatsuneo/d8dkppdb/ )

Ractive.js introduced simple data binding on HTML. Ractive.js specialized for data bindings like React.js. But, while React.js focuses on JavaScript, Ractive.js focues on HTML and minimize JavaScript code.

On HTML, we use "{{}}" to specify data binding, and the data is automatically tied to model in JavaScript. That makes JavaScript code simple.

7. Vue.js

HTML:

<div id="person">
    First Name: <input v-model="firstName"><br/>
    Last Name: <input v-model="lastName"><br/>
    Full Name: {{fullName}}<br/>
</div>

JavaScript:

var demo = new Vue({
    el: '#person',
    data: {
        firstName: 'Taro',
        lastName: 'Yamada',
    },
    computed: {
        fullName: {
            get: function(){
                return this.firstName + ' ' + this.lastName;
            }
        }
    },
})

(Sample: http://jsfiddle.net/yoshiokatsuneo/3gdzaw94/ )

Vue.js implements two-way data binding, as simple as possible. HTML is even more natural than Ractive.js.

On HTML, data written in "{{}}" or v-model is automatically tied to model in JavaScript.

It is quite simple, so good when we design HTML, prototype, or write small application.

8. Aurelia.js

app.html:

<template>
  <section>
    <form role="form">
      First Name: <input type="text" value.bind="firstName"><br/>
      Last Name: <input type="text" value.bind="lastName"><br/>
      Full name: ${fullName}
    </form>
  </section>
</template>

app.js:

export class Welcome{
  constructor(){
    this.firstName = 'Taro';
    this.lastName = 'Yamada';
  }
  get fullName(){
    return `${this.firstName} ${this.lastName}`;
  }
}

(Template project: http://aurelia.io/get-started.html)

Aurelia.js is a full-stack futuristic framework with ECMAScript 6/7.

Aurelia.js implements full-stack features introduced in AngularJS like two-way data binding, routing RESTful API, DI. At the same time, it is simple and better performance like Ractive.js/Vue.js utilizing ES6/ES7's feature like module or Object.observe().

It is one of the latest framework looking ES6-era, and works just now using polyfil.

HTML template is just ES6 template. We embed data using "${}" or "value.bind".

Controller is written as ES6 class, variables or get/put property in "this" is two-way bound to data in HTML.

9. AngularJS2.0(alpha)

app.html:

First Name: <input type=text [value]="firstName" #first (keyup)="firstNameChanged($event, first)"><br/>
Last Name:  <input type=text [value]="lastName"  #last  (keyup)="lastNameChanged($event, last)"><br/>
Full Name:  {{fullName}}

app.js:

import {Component, Template, bootstrap} from 'angular2/angular2';

// Annotation section
@Component({
  selector: 'my-app'
})
@Template({
  url: 'app.html'
})
// Component controller
class MyAppComponent {
  constructor() {
    this.firstName = 'Taro';
    this.lastName = 'Yamada';
    this.updateFullname();
  }
  changed($event, el){
    console.log("changes", this.name, el.value);
    this.name = el.value;
  }
  updateFullname(){
    this.fullName = this.firstName + " " + this.lastName;
  }
  firstNameChanged($event, first){
    this.firstName = first.value;
    this.updateFullname();
  }
  lastNameChanged($event, last){
    this.lastName = last.value;
    this.updateFullname();
  }
}
bootstrap(MyAppComponent);

(Template project: https://angular.io/docs/js/latest/quickstart.html)

AngularJS2.0(or Angular2, alpha version) is a framework announced special development site http://angular.io in AngularJS conference ng-conf.

It will take like one year to actual release, but I checked it out because AngularJS2.0 is quite attracted.

AngularJS2.0 is based on ES6 as Aurelia.JS. AngularJS2.0 uses AtScriptTypeScript that add static type checking or annotation to JavaScript. That ease syntax error detection or IDE integration to improve productivity.

AngularJS2.0 trashed two-way data binding but need to specify behavior explicitly. It makes behavior more clear and improve performance by removing digest loop.

On JavaScript code, we use @Component or @Template to specify element or template tied to the component. we create controller related to element id to specify behavior.

ng-xxx syntax is removed but introduced syntax like "[expression]" for JavaScript to HTML data transfer, "(event)" for HTML to JavaScript event propagation, "#element" to refer elements. There is no more scope, but variable on this can be just written in HTML.

Summary

I introduced, practically all, 9 major framework.

Major target of those framework would be, Backbone.js for large application with customized framework, Ember.js/Knockout.js for performance sensitive and more productive projects, AngularJS for normal projects, React.js for simple JavaScript code, Ractive.js/Vue.js for HTML/design centric projects, Aurelia.js for the future, AngularJS2.0(alpha) for framework research, as of now.

Each framework have their own philosophy or design that helps to develop web application even if we don't use the application even if we don't use the framework. So, let's have fun with JavaScript frameworks !


paiza.IO is online coding environment where you can just write and run code instantly. Just try it out !