User's Guide
A complete example
<html> <head> <title>${title}</title> </head> <body> <p>The following items are in the list:</p> <ul><%for element in list: output "<li>${element}</li>"%></ul> <p>I hope that you would like Brail</p> </body> </html>
The output of this program (assuming list is (1,2,3) and title is "Demo" ) would be:
<html> <head> <title>Demo</title> </head> <body> <p>The following items are in the list:</p> <ul><li>1</li><li>2</li><li>3</li></ul> </body> </html>
And the rendered HTML will look like this:
---- The following items are in the list: * 1 * 2 * 3 -----
Code Separators
Brail supports two code separators <% %> and <?brail ?>, I find that <% %> is usually easier to type, but <?brail ?> allows you to have valid XML in the views, which is important for some use cases. Anything outside a <?brail ?> or <% %> is sent to the output. ${user.Id} can be used for string interpolation.
The code separator types cannot be mixed. Only one type of separators must be used per file.
Output methods
Since most of the time you will want to slice and dice text to serve the client, you need some special tools to aid you in doing this. Output methods* are methods that are decorated by [Html] / [Raw] / [MarkDown] attributes. An output method return value is transformed according to the specified attribute that has been set on it, for instance, consider the [Html] attribute:
<% [Html] def HtmlMethod(): return "Some text that will be <html> encoded" end %> ${HtmlMethod()}
The output of the above script would be:
Some text that will be <html> encoded
The output of a method with [Raw] attribute is the same as it would've without it (it's supplied as a NullObject for the common case) but the output of the MarkDown attribute is pretty interesting. Here is the code:
<% [MarkDown] def MarkDownOutput(): return "[Ayende Rahien](http://www.ayende.com/), __Rahien__." end %> ${MarkDownOutput()}
And here is the output:
<p><a href="http://www.ayende.com/">Ayende Rahien</a>, <strong>Rahien</strong>.</p>
Markdown is very interesting and I suggest you read about its usage.
Using variables
A controller can send the view variables, and the Boo script can reference them very easily:
My name is ${name}
<ul>
<%
for element in list:
output "<li>${element}</li>"
end
%>
</ul>
Brail has all the normal control statements of Boo, which allows for very easy way to handle such things as:
<% output AdminMenu(user) if user.IsAdministrator %>
This will send the menu to the user only if he is administrator.
One thing to note about this is that we are taking the variable name and trying to find a matching variable in the property bag that the controller has passed. If the variable does not exist, this will cause an error, so pay attention to that. You can test that a variable exists by calling the IsDefined() method.
<% if IsDefined("error"): output error end %>
Or, using the much clearer syntax of "?variable" name:
<% output ?error %>
The syntax of "?variable" name will return an IgnoreNull proxy, which can be safely used for null propagation, like this:
<% # will output an empty string, and not throw a null reference exception output ?error.Notes.Count %>
This feature can make it easier to work with optional parameters, and possible null properties. Do note that it will work only if you get the parameter from the property bag using the "?variableName" syntax. You can also use this using string interpolation, like this:
Simple string interpolation: ${?error}
And a more complex example: ${?error.Notes.Count}
In both cases, if the error variable does not exists, nothing will be written to the output.
Using sub views
There are many reasons that you may want to use a sub view in your views and there are several ways to do that in Brail. The first one is to simply use the common functionality. This gives a good solution in most cases (see below for a more detailed discussion of common scripts).
The other ways is to use a true sub view, in Brail, you do that using the OutputSubView() method:
Some html <?brail OutputSubView("/home/menu")?> <br/>some more html
You need to pay attention to two things here:
The rules for finding the sub view are as followed:
- If the sub view start with a '/' : then the sub view is found using the same algorithm you use for RenderView()
- If the sub view doesn't start with a '/' : the sub view is searched starting from the ''current script'' directory.
A sub view inherit all the properties from its parent view, so you have access to anything that you want there.
You can also call a sub view with parameters, like you would call a function, you do it like this:
<?brail OutputSubView("/home/menu", { "var": value, "second_var": another_value } ) ?>
Pay attention to the brackets, what we have here is a dictionary that is passed to the /home/menu view. From the sub view itself, you can just access the variables normally. This variables, however, are not inherited from views to sub views.
Including files
Occasionally a need will arise to include a file "as-is" in the output, this may be a script file, or a common html code, and the point is not to interpret it, but simply send it to the user. In order to do that, you simply need to do this:
${System.IO.File.OpenText("some-file.js").ReadToEnd()}
Of course, this is quite a bit to write, so you can just put an import at the top of the file and then call the method without the namespace:
<% import System.IO %> ${File.OpenText("some-file.js").ReadToEnd()}
Principle of Least Surprise
On general, since NVelocity is the older view engine for now, I have tried to copy as much behavior as possible from NVelocityViewEngine. If you've a question about how Brail works, you can usually rely on the NVelocity behavior. If you find something different, that is probably a bug, so tell us about it.
Common Functionality
In many cases, you'll have common functionality that you'll want to share among all views. Just drop the file in the CommonScripts directory - (most often, this means that you will drop your common functionality to Views\CommonScripts) - and they will be accessible to any script under the site.
The language used to write the common scripts is the white space agnostic deriative of Boo, and not the normal one. This is done so you wouldn't have white spacing sensitivity in one place and not in the other.
The common scripts are normal Boo scripts and get none of the special treatment that the view scripts gets. An error in compiling one of the common scripts would cause the entire application to stop.
Here is an example of a script that sits on the CommonScripts and how to access it from a view:
Views\CommonScripts\SayHello.boo - The common script
def SayHello(name as string): return "Hello, ${name}" end
Views\Home\HelloFromCommon.boo - The view using the common functionality
<% output SayHello("Ayende") %>
The output from the script:
Hello, Ayende
Symbols and dictionaries
Quite often, you need to pass a string to a method, and it can get quite cumbersome to understand when you have several such parameters. Brail support the notion of symbols, which allows to use an identifier when you need to pass a string. A symbol is recognized by a preceding '@' character, so you can use this syntax:
<% output SayHello( @Ayende ) %>
And it will work exactly as if you called SayHello( "Ayende" ). The difference is more noticable when you take into account methods that take components or dictionary parameters, such as this example:
<% component Grid, {@source: users, @columns: [@Id, @Name] } %>
Using a symbol allows a much nicer syntax than using the string alternative:
<% component Grid, {"source: users, "columns": ["Id", "Name"] } %>
Layouts
Using layouts is very easy, it is just a normal script that outputs ChildOutput property somewhere, here is an example:
Header
${ChildOutput}
Footer