Web framework topics:

The TodoList example

The TodoList example is built in two layers, the model and UI.

Here, the model layer contains the logic and for both the domain model and the view model in one:

file: example/todo/model/TodoList.sc
// The classic TodoList example.  Unlike most examples, there's no 'view model' or coreui layer.
class TodoList {
   @Component
   class TodoItem {
      String text;
      boolean complete;

      complete =: updateRemaining();

      // Use ManualGetSet so we do not send unnecessary change events from the constructor
      @sc.obj.ManualGetSet
      TodoItem(String t, boolean c) {
          text = t;
          complete = c;
      }
   }
   ArrayList<TodoItem> todos; /* = {
      new TodoItem("Run todo sample", true),
      new TodoItem("Check me and see it stay in sync", false),
      new TodoItem("Add a new entry and press 'remove completed'", false),
   } */

   String todoText = "";

   // No need to synchronize these two because we sync todos from which they are computed
   @sc.obj.Sync(syncMode=sc.obj.SyncMode.Disabled)
   int remaining, numTodos;

   todos =: updateRemaining();

   void addTodoEntry() {
      todos.add(new TodoItem(todoText, false));
      todoText = "";
   }

   void updateRemaining() {
      int count = 0;
      if (todos != null) {
         for (TodoItem todo: todos) {
            if (!todo.complete)
               count++;
         }
      }
      remaining = count;
      numTodos = todos.size();
   }

   int getSize(List<TodoItem> list) {
      return list == null ? 0 : list.size();
   }

   void removeComplete() {
      for (int i = 0; i < todos.size(); i++) {
         TodoItem todo = todos.get(i);
         if (todo.complete) {
            todos.remove(i);

            DynUtil.dispose(todo);
            i--;
         }
      }
   }
}
Here is the UI layer which modifies the model layer:
file: example/todo/jsui/TodoList.schtml
<!-- This file, TodoList.schtml modifies TodoList.sc in the previous model layer -->
<html extends="EditablePage">
   <head>
      <link rel="stylesheet" type="text/css" href="todoStyle.css" />
   </head>
  <body>
     <div class="appFrame" id="appFrame">
        <h2>Todo List</h2>
        <div id="todoControl">
           <span><%= remaining %> of <%= numTodos %> to do</span>

           <!-- When the tag is clicked, clickEvent fires and calls removeComplete() -->
           [ <a href="#" clickEvent="=: removeComplete()">remove completed</a> ]
           <ul>
              <!-- Repeat li once for each element in todos, setting the var todo -->
              <li repeat=":= todos" repeatVarName="todo">
                 <!-- Set checked to the value of todo.complete and vice-versa -->
                 <input type="checkbox" checked=":=: todo.complete"/>
                 <!-- set the class to be complete-true or complete-false -->
                 <span class=':= "complete-" + todo.complete'><%= todo.text %></span>
              </li>
           </ul>
           <form submitEvent="=: addTodoEntry()">
              <input type="text" value=":=: todoText" size="45" placeholder="enter todo entry here"/>
              <input type="submit" value="Add" disabled=':= todoText.length() == 0'/>
           </form>
        </div>
     </div>
   </body>
</html>

The template page can access fields defined in the model because both layers are merged into one type.


Next topic: