Tutorial: TodoMVC with Realtime Appstax data

Introduction

TodoMVC is a JavaScript front-end MV* framework comparison that makes it easier to choose the tool that best fits your development needs.

In this tutorial we will use the React and Angular/Firebase examples from TodoMVC as a starting point for showing how to integrate our powerful Realtime Model to connect a JavaScript app with live data from Appstax collections.

The finished apps are available at todo.appstax.io, and the complete code can be viewed in our github repository.

TodoMVC

How the Realtime Model works

The Realtime Model creates a "live" object that keeps itself updated with the latest changes in your application data. Using this object as the model in a MVC framework will make your JavaScript app automatically respond in real time to data changes on the server.

// Configuring the model like this ...
var model = appstax.model();
model.watch("todos");

// ... makes an array property with live data:
model.todos;

Behind the scenes the model.todos property is initially created empty, then asynchroneously loaded with objects from the todos DataStorage collection, and continuously kept in sync using Appstax Realtime Channels over a WebSocket connection.

For more info on Realtime Model usage, read the Realtime Model chapter in the JavaScript SDK Guide.

Part I - Angular

Set up the local developent environment

Clone the TodoMVC repository and copy the examples/firebase-angular directory somewhere you can work on it.

Start by running the app on a local web server to allow XHR and WebSocket connections from your browser. If you have the Appstax Command-line Tool installed, you can run appstax serve to serve the current directory on http://localhost:9000. Another way is to run python -m SimpleHTTPServer 9000 if you have python installed.

Add the appstax.js dependency

Add <script src="//cdn.jsdelivr.net/appstax/latest/appstax.min.js"></script> before js/app.js in index.html.

Initialize appstax in app.js

In js/app.js, add this line somewhere before the todomvc module to initialize appstax with the app key for the todo app.

appstax.init("N0FyemRZZ0M4TmJnQQ==");

Angular controller

The main controller of the TodoMVC is in the js/controllers/todoCtrl.js file. Apply the following changes to that file.

Configure the model

We start by putting our Realtime Model on the controller $scope:

// Configure Appstax Realtime Model
$scope.model = appstax.model();
$scope.model.watch('todos');

This replaces the firebase initialization, so you should delete these lines:

var url = 'https://todomvc-angular.firebaseio.com/todos';
var fireRef = new Firebase(url);
$scope.todos = $firebaseArray(fireRef);

Angular digest

You can continue by deleting the $scope.$watch('todos'... block and replace it with this:

// Update calculated scope properties and start angular digest
$scope.model.on('change', function () {
  var todos = $scope.model.todos;
  $scope.totalCount = todos.length;
  $scope.remainingCount = todos.has('completed', false).length;
  $scope.completedCount = todos.length - $scope.remainingCount;
  $scope.allChecked = $scope.remainingCount === 0;
  $scope.$apply();
});

Adding new todos

In the $scope.addTodo function we replace the call to $scope.todos.$add with this:

// Create a new todo object and save it
var todo = appstax.object('todos');
todo.title = newTodo;
todo.completed = false;
todo.save();

Editing existing todos

In the $scope.doneEditing function we replace $scope.todos.$save(todo); with:

todo.save();

Deleting todos

In $scope.removeTodo, replace $scope.todos.$remove(todo); with:

todo.remove();

Clear completed, mark all

In $scope.clearCompletedTodos and $scope.markAll, replace the two calls to $scope.todos.forEach with

$scope.model.todos.forEach

In $scope.markAll, replace $scope.todos.$save(todo); with

todo.save();

Angular template

Reference the model object

In the <li> element with ng-repeat="(id, todo) in todos | todoFilter", replace todos with model.todos:

<li ng-repeat="(id, todo) in model.todos | todoFilter" ... >

Save object when toggling checkbox

In the toggle checkbox element, replace todos.$save(todo) with todo.save():

<input class="toggle" type="checkbox" ng-model="todo.completed" ng-change="todo.save()">

Summary of angular integration

In the angular controller we set up the model to watch a collection:

// Configure model
$scope.model = appstax.model();
$scope.model.watch('todos');

// Start angular digest when model changes
$scope.model.on('change', function() {
  $scope.$apply();
});

This model can then be referenced in the template:

<li ng-repeat="(id, todo) in model.todos | todoFilter" ... >

When user interaction requires creating, updating or removing todos, we do so by using the Appstax object methods inside $scope functions:

// Create new todo
var todo = appstax.object('todos');
todo.title = newTodo;
todo.completed = false;
todo.save();

// Update existing todo
todo.title = newTitle;
todo.save();

// Remove a todo
todo.remove();

Part II - React

Set up the local developent environment

Clone the TodoMVC repository and copy the examples/react directory somewhere you can work on it.

Start by running the app on a local web server to allow XHR and WebSocket connections from your browser. If you have the Appstax Command-line Tool installed, you can run appstax serve to serve the current directory on http://localhost:9000. Another way is to run python -m SimpleHTTPServer 9000 if you have python installed.

Add the appstax.js dependency

Add <script src="//cdn.jsdelivr.net/appstax/latest/appstax.min.js"></script> before js/app.js in index.html.

React TodoApp Component

The React TodoMVC example uses JSX syntax for most of its code. You can read more about JSX here.

The TodoApp component can be found in the js/app.jsx file. The following changes apply to that file:

Initialize appstax

Add this line somewhere before the (function () { closure start to initialize appstax with the app key for the todo app:

appstax.init("N0FyemRZZ0M4TmJnQQ==");

Configure the model

The TodoMVC/React example has a custom TodoModel object that we will replace with an Appstax Realtime Model. Near the bottom of the file, replace the line var model = new app.TodoModel('react-todos') with this code:

var model = appstax.model();
model.watch('todos');

To make React re-render when our model changes, replace the line model.subscribe(render) with this:

model.on('change', render);

Adding new todos

New todos are created in the handleNewTodoKeyDown function. Replace this line this.props.model.addTodo(val) with the following to create and save a new Appstax object:

var todo = appstax.object('todos');
todo.title = val;
todo.completed = false;
todo.save();

Toggling todos

In the toggleAll function, replace this.props.model.toggleAll(checked) with this:

// Set all todos to checked or unchecked
this.props.model.todos.forEach(function(todo) {
  todo.completed = checked;
  todo.save();
});

The toggle function changes the state of a single todo. Replace this.props.model.toggle(todoToToggle) with these lines:

// Swap checked state and save
todoToToggle.completed = !todoToToggle.completed;
todoToToggle.save();

Deleting todos

In the destroy function, the call to this.props.model.destroy(todo) should be replaced with:

todo.remove();

Updating existing todos

The save function calls this.props.model.save(todoToSave, text) to update a todo. Replace it with

todoToSave.title = text;
todoToSave.save();

Clear completed todos

In clearCompleted, replace the call to this.props.model.clearCompleted() with this loop:

this.props.model.todos.forEach(function(todo) {
  if(todo.completed) {
    todo.remove();
  }
});

React TodoItem Component

In the js/todoItem.js file, remove the shouldComponentUpdate function.

The shouldComponentUpdate function in the React example is an optional check based on TodoModel creating new JavaScript objects every time a todo is updated. Since the Realtime Model updates objects in-place instead, this check does not work as intended and should be removed. It is quite possible to make similar performance improvements with appstax objects, but that's beyond the scope of this tutorial.

Summary of React integration

Before creating an instance of the main TodoApp component, we set up the model to a collection:

// Configure model
var model = appstax.model();
model.watch('todos');

// Re-render when model changes
model.on('change', render);

We then pass the model as a property when rendering the root of the app.

function render() {
  React.render(
    <TodoApp model={model}/>,
    document.getElementsByClassName('todoapp')[0]
  );
}

The TodoApp component renders TodoItems components for each entry in the model.todos array, passing the Appstax objects as a property.

model.todos.map(function (todo) {
  return (
    <TodoItem todo={todo} />
  );
});

When we need to create, update or remove todos, we do so by using the Appstax object methods inside event handlers:

// Create new todo
var todo = appstax.object('todos');
todo.title = newTodo;
todo.completed = false;
todo.save();

// Update existing todo
todo.title = newTitle;
todo.save();

// Remove a todo
todo.remove();