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">
           <!-- The following span tag's body contains two method calls getRemaining and getSize that are updated automatically from change events using data binding.
                The getSize call simply detects change events when the 'todos' list is modified.  But getRemaining
                needs to run also after any change to a TodoItem's 'complete' status.  To handle that event with no additional code we have set 'refreshBindings=true'
                on this page to revalidate all bindings in a 'do later' event after any user input. -->
           <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.