Patterns for Jitsu
Jitsu represents a departure from traditional web programming. This document takes a moment to describes some of the ways to take advantage of Jitsu's programming model.
1. Use controls to avoid duplicated markup.
Guideline: Don't repeat markup unless you have to.
In many web apps you often see markup copied and pasted multiple times into a document, with little variation for each version.
A markup control:
- takes little more effort to write
- once written it will save you time maintaining,
- it will also often load faster and lead to a better user experience.
Avoid redundant markup in Jitsu. Create a custom markup control instead (see Extending Controls in Markup for examples.
2. Keep controls private to a page
Guideline: The server should not directly address controls on the page.
Consider this fragement of an application:
<script> function processMessageFromSever(json) { var msg = eval(json); var name = msg.statusName; var msg = msg.statusText; var statusControl = document.apps[0].getControl(name); control.setValue(ID_text, msg); } </script> <!-- Create a label that to show status messages --> <j:Label controlId="mainStatus"/> <br/> <!-- Rest of the app here -->
Here, when a message from the server is received, the script extracts the name of a control from the message and then sets a property directly on that control.
Though this approach is short and simple, it should be avoided. Here's why:
The server must have explicit hard-coded knowledge of the controls on the page. If you change how the page is presented (e.g. lets say you use a style rather than a control to present the data), you must now modify the server code too.
The aporach breaks the Jitsu cruncher See The Cruncher). When you run a Jitsu app through the cruncher, control names and control property names become shortened, so the server's hard-coded control name will be wrong.
The Jitsu cruncher preserves names in all of your data model. So in general the pattern here should be:
In this case, you could define a small dataset:
<j:define targetNamespace="urn:myapp"> <j:type name="ProgressStatus"> <j:property name="message" /> </j:type> </j:define> <body> <j:App> <!-- create a "progress" dataset --> <j:data> <j:DataSet id="progress" xmlns:myapp="urn:myapp"> <myapp:ProgressStatus id="mainStatus"/> </j:DataSet> </j:data> <j:script> function processMessageFromSever(json) { var msg = eval(json); var name = msg.statusName; var msg = msg.statusText; var dataSet = document.apps[0].getDataSet(ID_progress); dataSet.getValue(name).setValue(ID_message, msg); } </j:script> <!-- Create a label that binds to the progress status --> <j:Label text="bind(data.progress.mainStatus.message)"/> <br/> <!-- Rest of the app here --> </j:App> </body>
Although the code is a little longer, now the backend addresses a dataset rather than a control on the page. This pattern helps ensure your backend and frontend remain decoupled, and also works with the cruncher.
Beware of setValue on Controls
In traditional GUI programming, you see code like this (C# WinForms) code:
private void Initialize() { this.button = new System.Windows.Forms.Button(); this.button.Text = "Next"; this.button.Click += new System.EventHandler(this.button_Click); // create other components } private void button_Click(object sender, System.EventArgs e) { if (textBox1.Text == "") textBox1.Text = textBox2.Text + " " + textBox3.Text; panel1.Visible = false; panel2.Visible = true; }
The pattern is (1) create controls, (2) register event handlers, (3) when events occur, use property setters and getters and other widget methods to update the UI state.
Its very easy to write this kind of code, but it is also poor style in Jitsu. Here's why:
- As your UI grows, the event handling code gets more and more complex, since you have to maintain all the interactions that changes have across the user interface.
- It mixes up business logic and UI logic - in the example above, the first line of the click method makes one text box show the value of two other text boxes, concatenated by a space. Presumably this is a requirement from the backend, but the code here makes it hard to see what is going on.
Instead of using setValue on controls, write methods that modify the page's data model. Then bind controls to that data model. Here's the guideline: