Skip to content

teropa/angular-virtual-dom

Repository files navigation

angular-virtual-dom

npm version Bower version

angular-virtual-dom is an experimental Virtual DOM based AngularJS view renderer designed to be used with immutable data structures such as immutable-js and mori.

angular-virtual-dom lets you use regular AngularJS templates and expressions to bind data to the DOM, but uses Virtual DOM diffing behind the scenes.

angular-virtual-dom supports extensibility using directives - though only with directives that are Virtual DOM aware. That means angular-virtual-dom is not a drop-in substitute for the AngularJS directive compiler, and is meant to be used in limited contexts.

angular-virtual-dom works with AngularJS versions 1.2 and newer.

Usage

angular.module('myModule', ['teropa.virtualDom'])
  .controller('MyCtrl', function($timeout) {
    this.myData = Immutable.fromJS({
      cols: [
        {name: 'One', cssClass: 'one', key: 'one'},
        {name: 'Two', cssClass: 'two', key: 'two'}
      ],
      rows: [
        {one: 'A1', two: 'B1'},
        {one: 'A2', two: 'B2'}
      ]
    });

    // A new version of the immutable data structure triggers
    // DOM diffing later.
    $timeout(function() {
      this.myData = this.myData.updateIn(['rows'], function(rows) {
        return rows.push(Immutable.Map({one: 'A3', two: 'B3'}));
      });
    }.bind(this), 1000);
  });
<div ng-controller="MyCtrl as myCtrl">
  <table v-root="myCtrl.myData">
    <thead>
      <th v-repeat="col in myCtrl.myData.get('cols')"
          class="{{col.get('cssClass')}}">
        {{col.get('name')}}
      </th>
    </thead>
    <tbody>
      <tr v-repeat="row in myCtrl.myData.get('rows')"
          class="{{$even ? 'even' : 'odd'}}">
        <th v-repeat="col in myCtrl.myData.get('cols')">
          {{row.get(col.get('key'))}}
        </th>
      </tr>
    </tbody>
  </table>
</div>
  • v-root establishes a Virtual DOM tree. The table tag and all of its descendants will be rendered using virtual-dom, bypassing Angular's own DOM compiler.
  • Virtual DOM diffing and patching occurs when myCtrl.myData changes. The whole Virtual DOM tree uses a single (reference) watch, and only when it fires does the view re-render. The idea is to attach an immutable data structure on the scope, refer to it in v-root, and let Virtual DOM diffing take care of updates when new versions of the data structure are produced.
  • The expressions within the table are normal AngularJS expressions. However, they are not being watched, and are only re-evaluated when diffing is triggered by the containing v-root.
  • Directives bundled with angular-virtual-dom can be used within the Virtual DOM tree. Custom directives can also be created (see below).

Installation

With NPM / Browserify

npm install angular-virtual-dom

Require the module and include it in your AngularJS modules:

require('angular-virtual-dom')

angular.module('myModule', ['teropa.virtualDom'])

Or just:

angular.module('myModule', [
  require('angular-virtual-dom')
])

With Bower

The library is available as a Bower dependency:

bower install angular-virtual-dom --save

After installation, add one of the following to your loaded scripts:

  • angular-virtual-dom/release/angular-virtual-dom.js
  • angular-virtual-dom/release/angular-virtual-dom.min.js

Finally, include the teropa.virtualDom module in your AngularJS modules:

angular.module('myModule', ['teropa.virtualDom'])

API

v-root

Use the v-root directive in your Angular templates to establish a Virtual DOM. This will short-circuit Angular's normal DOM compilation and build the Virtual DOM template from the contained elements.

The directive accepts an expression, and changes to that expression's value cause the Virtual DOM tree to be re-rendered:

<div v-root="baseData">
  <!--
    Nested DOM structures built into a Virtual DOM
    tree
  -->
</div>

Expressions

Within a v-root, any AngularJS expressions are evaluated whenever DOM diffing occurs:

<div v-root="baseData">
  <h1 class="{{anExpression}}">
    {{anotherExpression}}
  </h1>
</div>

Typically, though not necessarily, the expressions will access data from the data structure referred to in v-root:

<div v-root="baseData">
  <h1 class="{{baseData.headerClass}}">
    {{baseData.headerText}}
  </h1>
</div>

Directives

v-if

Includes the node in the Virtual DOM only when the expression evaluates to a truthy value. Analogous with ng-if.

<div v-root="baseData">
  <h1 v-if="{{baseData.headerText}}">
    {{baseData.headerText}}
  </h1>
</div>

v-repeat

Includes a collection of nodes in the Virtual DOM, for each item in a collection. Analogous with ng-repeat.

Supports at least the following types of collections:

  • immutable-js lists, maps, stacks, ordered maps, sets, and ordered sets.
  • mori lists, seqs, vectors, maps, sets, sorted sets, and queues.
  • JavaScript arrays an objects.

Should additionally support any ES6 iterable collections.

Usage with sequential data structures:

<ul v-root="data">
  <li v-repeat="item in data">
    {{item}}
  </li>
</ul>

Usage with associative data structures:

<ul v-root="data">
  <li v-repeat="(k, v) in data">
    {{k}}: {{v}}
  </li>
</ul>

Additionally makes the special variables $index, $even, and $odd available within the template scope.

Writing Custom Directives

Note: The directive API should be considered highly unstable.

Virtual DOM directives are registered as normal AngularJS directives, but must define a linkVirtual function in the directive definition object. This should be a pure function that take a Virtual DOM node as an argument, and returns a modified Virtual DOM node or collection thereof.

The Virtual DOM nodes used by this library always hold a $scope attribute, referring to the current scope. A directive may create a new scope and attach it to the $scope attribute of the returned node.

Usage with Mutable Data Structures

While angular-virtual-dom is designed to be used with immutable data structures, it is not a hard requirement. Regular, mutable JavaScript data structures and objects work just as well.

You will, however, need to manually trigger re-renders by reassigning v-root to a new value unless your code does so naturally.

Contribution

Use Github issues for requests.

Author

Tero Parviainen (@teropa on Twitter)

Leans heavily on virtual-dom by Matt-Esch.

About

A Virtual DOM based AngularJS view renderer designed to be used with immutable data structures

Resources

License

Stars

Watchers

Forks

Packages

No packages published