TodoList Example

This example is built in three layers: the model, the user interface, and the data.

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;

      TodoItem(String t, boolean c) {
          text = t;
          complete = c;
      }
   }
   ArrayList<TodoItem> todos; /* = {
      new TodoItem("Run layerCake 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 = "";

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

   int getRemaining(List<TodoItem> todoList) {
      int count = 0;
      if (todoList == null) {
         System.out.println("*** no list");
         return 0;
      }
      for (TodoItem todo: todoList) {
         if (!todo.complete)
            count++;
      }
      return count;
   }

   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 user interface, in the example.todo.jsui layer. That layer extends the model layer so this file modifies the Todo type above. That happens because these files have the same name and the layers use the same package. You have access to the properties in the model layer, just like the "code behind" pattern.
file: example/todo/jsui/TodoList.schtml
<!-- TodoList.schtml modifies the TodoList class in the previous model layer.
     Setting 'refreshBindings' here because the 'getRemaining' method needs to be
     refreshed after any changes to the page are made. -->
<html refreshBindings="true">
   <head>
      <link rel="stylesheet" type="text/css" href="todoStyle.css" />
   </head>
  <body>
     <div class="appFrame" id="appFrame">
        <h2>Todo List</h2>
        <div id="todoControl">
           <!-- This span tag's body depends on two method calls: getRemaining and getSize that are updated
                automatically from change events using data binding. Both calls automatically receives change
                events when 'todos' changes. But getRemaining also needs to run when TodoItem.complete changes.
                We could listen for that change on each item and send a change for todos but instead just set
                'refreshBindings' to true for this page. After any change, all bindings are validated and
                refreshed if inputs have changed. -->
           <span><%= getRemaining(todos) %> of <%= getSize(todos) %> to do</span>
           <!-- The clickEvent property is updated when the anchor tag is clicked on.  When that happens the
                 binding calls: 'removeComplete()' -->
           [ <a href="#" clickEvent="=: removeComplete()">remove completed</a> ]
           <ul>
              <!-- Repeat the li tag once for each element in the todos list.
                As the list changes, tags are added/removed as needed -->
              <li repeat=":= todos" repeatVarName="todo">
                 <!-- Set checked to the value of todo.complete and vice-versa -->
                 <input type="checkbox" checked=":=: todo.complete"/>
                 <!-- Add a class to this span tag when it is done -->
                 <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=':= TextUtil.length(todoText) == 0'/>
           </form>
        </div>
     </div>
   </body>
</html>
and a separate layer to store the data:
file: example/todo/data/TodoList.sc
// Specifies the data to initialize the todo list.  
TodoList {
   todos = {
      new TodoItem("Run StrataCode 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)
   };
}
We can run this layer in two different modes. If we compile it with just the js.schtml layer, it runs in the browser only (as you see above). If we compile it with both the js.schtml and jetty.schtml layers, it runs in a synchronized way. As each model entry is changed, its value is sent to the server and saved. This configuration is configured to just save the data in the user's session.