StrataCode web framework: schtml, javaScript, sync

Why another web framework? Here are the advantages.

To run a simple example, download and install StrataCode if you haven't already. If you have the examples bundle, you can run:

scc -v -vs example/simpleSync/main

This builds and runs a "hello world" web application using the framework layers: js.schtml, jetty.schtml, and js.sync. The -v option enables basic verbose logging and -vs enables verbose sync so you can see the client/server communication.

There are three types of application layers - UI, model, and server. The UI and model layers run on both the client and the server. The server layer runs only on the server.

Model layer

file: example/simpleSync/model/HelloWorld.sc
object HelloWorld {
   String message = "hello";
   // Note: if you assign a value here, even empty string, by default the sync framework will not send the 'initial value'
   // to the client javascript code (it will still be in the initial HTML).  There are several workarounds -
   // 1) set @Sync(initDefault) on this property (see HelloWorld2).  2) If the reply is done in a "doLater" that
   // should also work - if the setReply method is called after addSyncInst in the code it (TBD) record the
   // initial change.
   String reply;

   // When message or reply change print them using 'reverse only' binding
   message =: System.out.println("--- Message: " + message);
   reply =: System.out.println("--- Reply: " + reply);
}

Here reverse bindings are used to print to System.out when either the message or reply properties changes. Since this runs on both client and server, you'll see these messages in both the server's standard output as well as the javascript console.

UI layer

file: example/simpleSync/ui/HelloWorld.schtml
<!-- A simple HTML page template which binds the input tag to the message property and displays 
     the reply property as the body of a div tag. -->
<html>
<body>
   <form id="helloForm" onSubmit="return false;">
      <input id="messageInput" type="text" value=":=: message" size="45"/>
      <div><%= reply %></div>
   </form>
</body>
</html>

The input tag's value attribute uses a bi-directional data binding expression to bind the input tag's value to HelloWorld.message in the model. Setting either one will set the other. In schtml files, an html attribute can refer to a Java expressions with or without data binding by prefixing the attribute value with the "=" operator, or one of the [data binding operators](dataBinding.html): ":=", ":=:", or "=:".

When you use just the = operator, the property in the tag object for that tag is initialized with this value like you are initializing a field in an instance.

The <%= directive works like JSP. It evaluates the expression and outputs the result. When used in a declarative context, the expression is part of a data binding expression to compute the body of that tag and listen for changes. When the reply property changes, the body content property is updated which invalidates the tag's body. If necessary that schedules a refresh of the tag object's DOM element. Your application benefits from fast, incremental updates using events when you use this approach. The generated code is readable and debuggable and you have all of the tracing options available with data binding.

Server-only layer

file: example/simpleSync/server/HelloWorld.sc
// This runs on the server - it could be a database query or secure logic you do not want
// to expose on the client.
HelloWorld {
   reply := "Server says: " + message;
}

For the server's version of the HelloWorld class there's a forward binding which recomputes the value of 'reply' when the message property changes. The change to reply will be sync'd back to the client so you'll see that message printed in the JS console as well as the server's console.

UnitConverter example

Here's the unitConverter's user interface implemented in HTML:

file: example/unitConverter/html/core/UnitConverter.schtml
<%@ @URL(pattern="/uc") %>
<html>
<head>
	<title>UnitConverter on StrataCode</title>
	<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
   <div id="appFrame" class="appFrame">
      <form id="unitConverterForm">
         <select id="converterChoice" 
               optionDataSource=":= converters" 
               selectedIndex=":=: currentConverterIndex"/>
         <br>
         <span id="unit1Label" class="unitLabel">
              <%= currentConverter.unit1 %>
          </span>
         <input id="unit1Field" class="unitField" autoComplete="off"
                value=":=: numberConverter.numberToString(currentConverter.value1)"/>
         <br>
         <span id="unit2Label" class="unitLabel">
             <%= currentConverter.unit2 %>
          </span>
         <input id="unit2Field" class="unitField" autoComplete="off"
                value=":=: numberConverter.numberToString(currentConverter.value2)"
           />
      </form>

      <div id="feedback"><%= numberConverter.error %></div>
   </div>

</body>
</html>

If you've seen the swing, android, wicket, or gwt versions of the unit converter you'll notice that it uses the same base layers. This is the advantage of portable domain and view models. All we're doing here is binding the user interface to existing view model properties.

Although the template is written in schtml, it is compatible with html for preview or use by other tools. Expressions will show up as formulae you can edit in the external tool.

This same template can either run on the client-only, server-only, or using a mix based on how the framework chooses to run the system on a tag-by-tag basis. Data binding keeps everything in sync across the client/server boundary. StrataCode is converted to Java which is converted to Javascript.

Run templates and tags on the client, server, or both

The StrataCode web framework supports the ability to take one declarative code model and run it in various ways. You can choose to run templates, or individual tags within templates, on the client, server, or both.

Default runtime for templates

Some templates or tags have to run on the server. The code might use the database, or libraries that don't exist in the browser. Put these templates in a layer that extends a server specific layer like jetty.schtml, or any jpa layer.

Other templates may only run in the client, e.g. a standalone javascript application or one that talks to a server that's not built from these layers. Have these client-only layers extends js.schtml or any layer that only runs in the JS runtime.

If a layer extends layers that depend on more than one runtime (e.g. js.schtml and jetty.schtml), it will run in both runtimes, unless you override the defaults in the layer definition file. Some layers are free floating - and do not depend on any runtime. You can add those layers on the command line and they are included in all runtimes.

Layers that need schtml, but have neither client or server dependencies can extend html.schtml so that they can run in either. Before you run your application, you will need to add either js.schtml or jetty.schtml to see anything. With just html.schtml, you will generate a tag object not rendered by any framework but available as an API. For a layer which you want to run in more than one configuration, extend html.schtml and then add js.schtml, jetty.schtml later. You could create separate layers for clientOnly, clientServer, or serverOnly which simply extend the combination you want. Or add them as additional layers in the run configuration or on the scc command line.

For layers that extend only jetty.schtml, schtml template pages run in a server-only mode where the tag class is generated for only the server runtime. But any dynamic elements or logic are detected in the page, and force the inclusion of a small javascript client that implements a subset of the sync protocol to manage basic dynamic html functionality using a server refresh for each application event.

Html and javascript only applications

Sometimes, it's nice to be able to build a simple "serverless" website that contains only Javascript and html - no server at all, especially when prototyping UIs. StrataCode lets you build these applications, but later add a server if necessary with minimal refactoring.

When the framework layers in your stack include only js.schtml, and no server dependences like jetty.schtml, StrataCode builds a website from only html and javascript files. You can open those files on the file system in your browser or publish them to a plain file based web server. In this configuration, StrataCode generates .java files from the schtml and sc files in your project, then converts those files to .js which is loaded by the browser.

After you build your project, the layer's build directory contains a directory called web - the top-level directory for your site for any static files. In this configuration, all schtml templates are global since there's no server code to run. At compile time they generate a .html file which is used to bootstrap the JS application. Your browser opens automatically to the index.html file unless you run StrataCode with the -nw (no window) option or modify the defaults in a layer definition file you include.

StrataCode generates a default index.html which contains links to the top-level URLs defined in your project. If you have schtml files with an html tag which is not abstract, it will be in this list automatically. As soon as you add an index.html or index.schtml file to your project, it overrides the default one. Like any other path in the source directory, a subsequent layer can simply override it by adding one to replace it.

Some schtml files are typically used as HTML fragments. Those pages should not start with the html tag but instead might define a div tag, span or something else. If you add an @URL annotation to any Servlet or schtml page, you can access it from a pre-defined URL instead of using the paths of the pages directly.

Client/server applications

When you include the jetty.schtml layer, StrataCode generates a class called PageInit to initializes all of the top level URLs and registers them with the PageDispatcher servlet. This will include those types marked with @URL or which have an <html> tag as their root but this is configurable. This servlet maps the page requests from URLs to code which uses your server-side template object to generate an HTML response.

URLs and mapping to template pages

If you do not specify an @URL annotation in your schtml file, by default it's URL will be https://server:port/myDir/myFile.html for a file in a layer with the path myDir/myFile.schtml. Unlike generated .java and .class files, the web directory files do not include the directories corresponding to names in the layer's package. This allows you to keep code and templates next to one another in the layer directory but have them deployed to different places - code in the package tree, web files in a separate web-tree. It's convenient to allow overlap between these two trees and so that's the default. Directories in the layer directory become sub-directories under 'web' and also sub-packages in the package-tree for the generate java files. When you do need to specify a different web-root for a given layer, that's configurable in the layer definition. The default provides a nice URL naming scheme for finding, modifying and replacing template pages using layers.

You can also control what URLs map to which pages via the @URL annotation. This annotation lets you associate a pattern string which matches components of the URL, such as path-name entries to properties values.

When the path name of the incoming request matches the @URL pattern, an instance of that page's tag object is created. If the pattern references any properties of the page object, those properties are then set. Then the page object is used to generate the content for the response.

Top level page properties can also use the annotation @QueryParam to bind them to query parameters automatically. You can make query parameters required to reject page requests unless they are present.

See the simpleBlog example to see use of @QueryParam and URL based parameters using patterns.

Synchronization

When your layers extend both jetty.schtml and js.schtml, you can also enable the JS to Java StrataCode synchronization support by extending js.sync. When synchronization is enabled, you can use the @Sync annotation or the apis to register a synchronizable set of properties for any given type. It allows basic RPC based serialization, preserving object graphs, usable JSON, or other pluggable protocols. You can also use it with data binding to track changes and serialize just the changed parts of an instance back and forth across the wire.

To synchronize overlapping parts of a layer or type set @Sync(syncMode=SyncMode.Automatic) in the layer definition file or for one or more types in your layer. With that mode, @Sync is enabled by default for any properties that appear in both the client and server runtimes.

Rather than writing marshalling and unmarshalling code using explicit client/server APIs, most of your application can be moved seamlessly between the client and server, or both based on how you want the data to flow.

You can augment the automatic synchronization by annotation driven synchronization. Here you explicitly mark properties and types with @Sync annotation, using attributes to disable synchronization, specify one-way synchronization and to control how and when the object is fetched and updated.

Using StrataCode synchronization provides you with high-end application behavior from your domain model in a pure form. You get quick initial page loads by rendering pages on the server and providing the browser an HTML that represents the initial page view. Then you start loading the javascript version of the page. Once it's loaded, changes made locally are refreshed in the browser. Changes to objects made in the browser are also sync'd back to the server. That way, you can refresh the page at any point without losing important information.

Although it's based on sticky-session load balancing, if the session is lost, the client can restore the server's session automatically. So important information is not lost when a server fails.

For more information see details on the sync framework.

Mixing Html and Java

For a more complex sample here is the domain model for a TodoList application:

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--;
         }
      }
   }
}
In the example.todo.jsui layer, which extends the model layer, the template page is merged with the model because their type/file names are the same. The sub layer can access or override the base layer just as if itwere extending it.
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>

One step build/run

The scc command does it all. Even with client/server applications, it will do all code generation and compilation necessary, start processes, open browser windows, and implement restart, so you have full life-cycle control over the application. This will make it easy to deliver SC as both a SaaS and OnPremise solution. Let you support client-only, server-only, and client/server mixed solutions.

The layer architecture will let you start or attach to a server, start any necessary processes and run any necessary main methods based on the current layer set. Any SC process can change any object or model and sync with the other federated processes, allowing them to update both runtime views and development time views "in sync".

Automatic refresh of declarative tags/templates

StrataCode tags and templates generate code in two different ways depending on the configuration and the content of your template. When your tags only use declarative constructs, i.e. properties and data binding expressions they support incremental evaluation. As properties fire, only the affected chunks of text are updated - e.g an attribute is replaced, or part of an expression evaluated. These template fragments are converted into data binding expressions similar to:

chunk1 := variable1 + "constant1" + variable2 + "constant2";
chunk1 =: invalidateStartTag(); // or invalidateBody if the expression is in the body

As the data values change, only the affected portions of the template are rendered. This provides efficient caching on the server and incremental refresh for interactivity on the client.

Procedural tags

When you use any non-declarative constructs in your tag, that tag cannot use this mode for its content. It falls back to generating an output method which appends to a StringBuilder using the values in the expression. When you use this mode, these regions of template must be refreshed manually using the tag API.

To make your templates declarative, here are a few tips:

Dynamic css using the .sccss format

What's the best way to manage css for your website? Because css is an ever growing, potential maze of twisty passages that create unmaintainable designs, it's a rule of thumb to limit the features you use, and properly structure it for manageability. Lean towards using classes, and ids as selectors and other patterns that permit traceability and localization of changes. Css does not really have variables or expressions and so many newer frameworks (i.e. the similarly named scss) generate css at build time because otherwise you face a lot of duplication of logic and limit the ability to customize style sheets.

StrataCode supports the use of the sccss extension to generate CSS files automatically from sc templates. They are normal templates which produce css files when evaulated which can be at build time or at runtime when the URL is requested. Like schtml, sccss templates can use data binding to evaluate rules at build time, or on the fly.

It may not be something you always want to use, but StrataCode supports dynamic css ismorphically - so you can request a dynamic css file on the server, update it incrementally on the client, refresh to get the latest version on the server as you'd expect in an isomorphic application.

To summarize the advantages of sccss:

Here's an example:

      <%!
         String borderColor = "#777";
         // With the SC template language, you can break back into a template expression for any string using >%
         // The template language is really Java where strings are turned inside-out
         String dropShadow = %>
            border-style: solid;
            border-width: 1px;
            border-color: <%= borderColor %>;
            box-shadow: 5px 5px 2px #aaaaaa;
         <%;
      %>

      #navMenu li ul {
         <%= dropShadow %>
      }

      #navMenu li ul ul {
         <%= dropShadow %>
      }

      .toggleButtonSelected {
         border-color: <%= borderColorSelected %>; 
      }

Relative URLs

Many web frameworks require absolute URLs in all references but this eliminates an important tool in a web developer's toolbox: management of locality of references and more easily tracing references. With StrataCode, links that are absolute are not changed. But if you use a relative path name - i.e. not starting with http:// or / - your links will be rewritten using a function so they work even if your page is included from some other top-level URL or even if the top-level url is different, or even changes due to a navigation change that does not refresh the page. When you are not using 'page based' URLs, you should put a / in front of the URL to indicate it's not relative.

TODO: We should provide warning/errors for broken links and IDE support for navigation along href and other link attributes. This should be optional because in most frameworks even relative URLs may refer to file types that are not managed by this set of layers and so are not resolveable. Maybe we enable it or disable it for certain URL patterns.

Images, static html etc.

For management of static files - resources for a desktop app, or web files in your web root, layers let you easily merge a collections of files to produce a single name-space with the ability to override any file. While this is a bit more complex than a typical doc root, paths and references are traceable. Apps that do not conflict in their namespace can be easily merged or separated.

Layers provide path management features to let you organize directories, and install different types of files with different path prefixes. Each layer defines a simple set of a files in a standbox that can be deployed as needed. Framework layers and metadata ensure the files are deployed for the appropriate URL and leaving content developers a clean view of the assets under their control. Rather than a monolithic document root, your source assets are small containers organized for specific roles, assembled with the ability to distribute responsibility as needed. For multi-variant testing, workflow, review and staging the layered structure is a perfect fit.

You can think of StrataCode as adding static typing for files in your document root, with the ability to override any file just by defining a new file in a subsequent layer with the same name.

Because files replace each other based on the resulting output file suffix, you can replace a static html file with a dynamic schtml file or vice versa.

Reusable tag macros

It's very useful to have tags/attribute combinations in HTML are reused throughout your design. You need a way to parameterize the requirement so you can reimplement it in different ways in one place. This can be a tricky problem but important to get right in a framework. Java's type system gets us most of the way there, but StrataCode adds the last bit for template macros.

Tags which have an id or those which are not possible to include as static content inside of the parent tag are converted into a StrataCode object which role is to render the tag. If the tag has an id, that becomes the class name and it's defined as an inner class of it's parent tag object. A tag can use the extends attribute to specify a 'base class' for that tag.

With schtml, you can create tag macros which take typed parameters, either in the same file or in a separate file. When you define a macro in the same file, use the abstract="true" attribute so that tag for the macro is not inserted into the output of the template. In the generated code, an abstract="true" tag generates a class not an object like other tags.

When you have an schtml file with a single tag in it, that tag's id must match the name of the file. You do not have to specify it explicitly like you do in Java. If you define a file called "MyMacro.schtml" with a single div tag with no id, in a file called "MyMacroInstance" you can define a div tag that has the attribute extends="MyMacro". When one tag extends another, by default, both the attributes and body of the extended tag are rendered. Any attributes set in the tag replace those in the extended tag. If you'd like to access and modify the super tag's features, you can use Java's super operator. The extended tag's body is also initially the body used for the output. Any new tags you provide are merged into the body of the extended tag. Tags with the same id inside of the body are themselves merged and so on. In many cases the sub tag's outputStart and outputBody will call the super tags's method and then append to the output. But when you reorder children or replace, or do other changes to the children tags or attributes the sub tag's start/body methods start from scratch because the change is not incremental.

TODO: currently tag objects defined without an id that are being merged into a downstream layer will by default be merged rather than appended. This seems counter-intuitive but we need to merge singleton tags like html, body, and head. Maybe we should by default append div, span, etc. and merging only the singleton tags?

The tags you extend should have the same tag name. So a 'div' tag should always extend another div tag. The role the tag plays seems to be indicated by a preferred tag in all cases and if a div inherits from an input tag, they have different behavior so it doesn't work. We could probably support a div extending a span or vice versa because the behavior part is the same.

Inside of your tag, you can define properties using the <%! operator. You can set these properties using attributes from the extending tag. The transformation process converts each attribute setting into a property assignment, so they override any previous values those properties might have had. This also means static-type checking is performed so you have edit or compile time errors, find-usages, navigate references etc.

Be aware that you do need to use: attributeName="= expression" unless the attribute is a String value. The 'expression' part is Java expression and so converted and type-checked properly. When you leave that out, it's a string and only works with String properties (TODO: maybe we should support some basic type conversion here?) If you think the expression will change at runtime, use attributeName=":= value" so the value is updated on the fly. SC takes care of adding the necessary getX and setX methods along with the sendEvent call to trigger the update.

Here's an example that demonstrates reusable tag macros:

file: test/js/simplePerson/PersonPage.schtml
<html>
<body>
<%! 
// Template declarations - add an inner class and two fields
class Person {
   String name;
   int credits;
   Person(String n, int c) {
      name = n;
      credits = c;
   }
}

Person buyer = new Person("Franklin", 250), seller = new Person("Mario", 5800); 
%>

<h2>People</h2>
Here are the people:

<!-- Define PersonTemplate an 'abstract' template meaning it is not drawn itself.  
     It has a "data" field of type Person we can populate through attributes. -->
<div abstract="true" id="PersonTemplate">
  <%! Person data; %>
  <h3><%= data.name %></h3>
  <p id="creditsParagraph">Credits: <span><%= data.credits %></span></p>
</div>

<!-- Render the PersonTemplate tags once binding data to buyer and then again for seller -->
<div extends="PersonTemplate" data="= buyer"/>
<div extends="PersonTemplate" data="= seller"/>
 
</body>
</html>

Client/server tag object API

StrataCode provides a single tag API that is supported both in Java on the server and in Javascript in the browser. This api exposes a subset of the features of the Javascript document object model API (aka the DOM). The Java implementation is used on the server side and to convert code from Java to JS for the JS version. The JS implementation of the API that's used is natively written in JS in the file js/tags.js file that's part of the js.core layer.

This API provides bindable properties you can use in your templates to change the HTML, or react to events such as click, mouse move, change, or resize. It exposes wrappers to some Javascript variables, such as innerWidth, and innerHeight which you can use in binding expressions. You can make an API call to refresh a tag if it's not able to auto-refresh using data binding.

Tag classes expose all of the standard DOM events so that you can do reverse-only bindings on them: e.g. in your schtml, for any tag, you can add the attribute: clickEvent="=: myMethod()". This is a nice syntax to avoid the need for function closures or interfaces.

The tag classes are nested matching their hierarchy in the source file. The class or object name uses the value of the id attribute when it's supplied and that value ultimately becomes the id of the DOM element. The base tag classes live in the sc.lang.html package:

TODO: Should these have a 'Tag' suffix in the name? I think DivTag, ATag would look a little better than just Div and A as class names.

These base tag classes are used as the default value for the 'extends' attribute. It is used as the extends class for the class or object generated for the tag. It's possible to substitute your own class for the default for a given tag name using the tagPackageList. This property specifies a search path of packages to search in order for the HtmlPage, Div, etc. classes. It's usually set by framework layers to point to a local package of templates or classes to use for each class. For example, the html.core layer calls:

  system.addTagPackageDirectory("sc.html.tag", this, 1);

to register the sc.html.tag package. The layer html.schtml extends html.core and defines a default HtmlPage.schtml template with a package name of sc.html.tag. So when that layer is included, StrataCode uses these classes as the default value for the 'extends' attribute for the <html> tag.

This class serves as a good example for how framework features are implemented and how you can adjust behavior (see HtmlPage).

Tag bindable properties

StrataCode tag objects support special properties to control the tag's behavior in the page. In your template, you can set them to constant values, expressions, or data binding expressions.

Tag visibility

Set the 'visible' attribute to false to remove this tag from the parent's body. When set back to true, the parent's body is re-rendered to include the tag again. When the tag is invisible, it's useful to specify a substitute in some cases. To support this, you can add a child tag with the special id="alt". When the parent is invisible, the alt tag is put in it's place and vice versa. At runtime you sometimes need to refer to the tag's id in code. For the alt tag the id is: "<parentId>alt" (e.g. fooalt if the parent tag has id="foo").

You can alternatively control the tag visibility using the style attribute as in normal HTML, but using a binding to make the tag appear and disappear. If you have a boolean property called 'vis' you could use style=':= vis ? "display:none" : ""'

TODO: Currently when your tag is not visible, the bindings still run which means you have to do more work to be sure they behave even when maybe they are not applicable. Should fix by activating/de-activating the bindings, or even better conditionally creating/destroying the object. A 'switch' option might be nice so you can have one instance which morphs into different implementations based on binding expressions.

Detecting mouse over

The hovered property is set to true when your mouse is over an element. You can bind to this property to adjust styles, trigger code etc for dynamic mouse over behavior.

Dynamic styles

Set the style attribute using expressions or data binding for dynamic styles in your tag object. For example:

style=':= "background-color: " + (colorIsRed ? "red" : "blue")'

Changing the tag's class

Like other bindable attributes, set class to the value of a binding expression.

class=':= hovered ? "highlightClass" : ""'

DOM Events

There are properties in the tag object for each of the DOM events are fired on any element. You can use reverse-only bindings so that when an event is fired, your method is called, an assignment expression evaluated, or expression evaluated. At that time, you can use the Event object exposed by this property to get the timestamp, or other info from the event in your code.

Here is the list of event properties that are supported:

clickEvent, dblClickEvent, mouseDownEvent mouseMoveEvent, mouseOverEvent, mouseOutEvent, mouseUpEvent, keyDownEvent, keyPressEvent, keyUPEvent, submitEvent, changeEvent, focusEvent, blurEvent.

For example:

<div id="clickMe" clickEvent='=: clicked = clicked + 1'>
   Clicked on <%= clicked %> times.
</div>

Client implementation of DOM events

When your tag object is running in javascript, your binding will be called directly from the DOM event handler. Tags are invalidated immediately when a property used in their content changes. Tags are refreshed in a "do later" that runs after all pending callbacks have been delivered.

So in the clickMe example above, only the body of the clickMe tag is re-evaluated when the button is clicked and that happens right away without calling the server.

Server tag implementation of DOM events

When you are using a server tag, either a server-only template page or a tag using exec="server", you can still register for these events but your method is called on the server using a serialized version of the event copied over from the client. To implement this, when your initial page is rendered, metadata about which tags and events is sent to the client along with the initial HTML. The small JS file stags.js reads this data and adds the necessary event listeners so it can send over 'sync' messages to set the property.

So in the clickMe example above, in the server tag implementation the clickEvent is sent to the server as part of a 'sync' operation in response to the button being clicked. The server increments the clicked value, fires the binding, invalidating the body of the clickMe tag. Once all events have been delivered, the page is refreshed. Because clickMe is a serverTag, the refresh of the body fires a change event for the innerHTML property. All server tags are synchronized for the startTag and innerHTML properties, as well as any tag specific properties we might need to re-apply back on the client, like an input tag's value. In this way the sync sytem records the changes to these two properties and returns them to the browser at the end of the sync. Back in the browser, it applies these changes and the content updates. By using the same sync system, you can mix and match client and server models of operation in the same page.

Repeating Tags

The repeat attribute lets you create repeating tags in the document, one for each element in the List or array provided by the repeat attribute's value. You can create your own dynamic list or grid that updates incrementally when individual cells or rows are modified. When change events are detected, the tag object's syncRepeatTags method runs to performs incremental updates to the browser's model of the tag objects (i.e. it inserts, removes, moves or appends to the DOM elements to bring them in sync with the tag objects). The same thing happens on the server where it refreshes the HTML of the repeatTags, also incrementally.

When you set the repeat attribute on a tag, you can also set the repeatVarName attribute to define a new property whose type is the type of the list or array element. If the List property is defined with a type parameter, the repeatVarName will have the type of the type parameter (defaulting to java.lang.Object).

If you don't set repeatVarName, you can still access the element value using the built-in property 'repeatVar' which is always typed as an Object.

Implementation of repeat tags

For those times when you need to look at the generated code, or use the Java API created by the tag object in your code, or customize the process for creating a tag object from a list element, it's helpful to understand how repeat tags are implemented.

Each tag using repeat generates two classes: an outer "repeat wrapper" class and an inner class used for each repeated tag. The class name of the repeat wrapper is <idVal>_Repeat. The class name of the repeated tag is just <idVal>

For example:

<div id="foo" repeat="bar"> <%! int fum; %> </div>

would generate:

 object foo_Repeat implements IRepeatWrapper {
    ...
    object foo extends Div {
       int fum; 
       ...
    }
 }

The tag with the repeat element itself is used as the definition of each repeated element. So if you use repeat with an li tag, you'll end up with a list of li tags in the document. If the list is empty, nothing is added to the document.
NOTE on naming of generated classes. By default, the repeatWrapper outer class just renders the repeated tags and so is not visible when the page is rendered, so it's not common to need to customize it. But it is visible in the inner object hierarchy generated. So if you have a tag with id="foo" that uses repeat, to refer to the tag class "foo", you'll need to use "fooRepeat.foo". The inner object "fooRepeat" is the repeatWrapper class.

More control over the repeated tag

Sometimes you'd like to render different chunks of HTML for different elements of the list. It can be awkward to try and make one tag object render itself differently and still retain a nice declarative style for incremental updates. A better pattern is to use the repeatWrapper attribute and specify your own implementation of the IRepeatWrapper interface.

It has a createElement method which you implement that returns the tag object to use for that element.

You have the option of reusing an old tag object which is passed in when we are replacing the repeat variable for a given tag object. If the class has not changed, it might be a lot faster to just set the value than recreate a new tag object and render it from scratch.

TODO: should we just look at the 'extends' or 'implements' of the repeat tag and if it implements IRepeatWrapper, we can use the definition of the tag as the repeatWrapper and avoid one extra class. We currently don't seem to use the inner tag class which gets generated in almost all of the places we use repeatWrapper.

Select tag properties

These special tag properties are supported:

Input tag properties

Img tag properties

Option tag properties

TODO: We are still filling in missing pieces in this API. Let us know if you need anything added and we'll add it.

All tags: structural attributes

These are attributes you can set on any tag which control how that tag is converted to Java.

See the tag objects doc for more details on merging and exec.

Document, Location, Window

These are Java classes which emulate parts of the corresponding JS apis. You can use them from Java code that's converted to JS, or some properties like window.location.pathname are synchronized to the client. So when you set them in server code, they redirect the browser or change the pages URL.

ome of the features exposed in the API are only available on Java code that runs in the browser. For example, the Window.window.innerWidth and innerHeight properties are only set on the client. But on the client, they are bindable so you can write rules that rely on window size, but only after the initial page has finished loading.

javadoc: Document, Location, Window.

TODO: to make innerWidth/Height accurate on the first page load is tough because we don't have that info so it's too late for the initial HTML.

Imports

Just like JSP, you can add imports for the page and annotations on the top-level class object using the <%@ annotation as the first thing in the file.

<%@ import myClass; %>

Annotations

To annotate the class corresponding to the page itself, you use:

<%@ @AnnotationName(annotationValues=...) %>

Live in-browser programming

StrataCode as a framework supports changing the code while the application is running. Just as these changes can sometimes be applied to the server code at runtime without a restart, they can be converted to Javascript code and run on the client. When the changes are more complex, the complete javascript on the server is regenerated and the client page is told to refresh. If the server tag types are dynamic, they too can be easily updated and refreshed. If you use compiled tag types, the server may need to restart because of changes to compiled code.

Implementation details

Read about the implementation of java to javascript and tag objects.