Designing The Block View
Blocks in coServ are modeled after the MVC pattern. A block view is usually made up of a HTML and a CSS file. To be accurate, a block view is actually made of a HTML and a CSS templates. With templates, you have more power to design block views.
A HTML or CSS template means you can embed Javascript codes to a HTML or CSS file. The embedded Javascript code should be enclosed with '<%' and '%>'. Unlike many other template processors, you can have complete features of the Javascript languge rather than just a subset of it. Furthermore, coServ adds some important features to make HTML templates more powerful and manageable. We'll discuss those features in the followin sections.
Print out HTML codes
One important application of HTML template is to display different HTML codes based on contents. Below is an example:
<%
if (result.isSuccess) {
%>
<div>
show contents here...
</div>
<%
} else {
%>
<div>
show warnings...
</div>
<% } %>
The above example will examine if contents are successfully retrieved. If so, the contents will be displayed. Otherwise, warnings will be shown. Such coding patterns are widely used in almost all HTML template tools (languages). It works well except that the readability is quite poor.
If the HTML code fragments to be displayed are not very complicated, coSev has a print() function to make the whole thing much more readable. The above example can be rewritten by adopting the print() function:
<%
if (result.isSuccess)
print('<div>show contents here...</div>');
else
print('<div>show warnings...</div>');
%>
As you can see, with the print() function, your HTML templates can be much more concise and easier to read. The print() function is quite similar to the document.write() function except that the coServ print() function is executed on the server side instead of the client side.
Embedding blocks and wcomps
coServ makes HTML refactoring possible. The key idea is to break up a web page into smaller pieces call "blocks". As a result, you don't want to build large blocks as them won't do too much good in terms of making your app more managable. On the other hand, you don't want to have tens of top-level blocks in a page or your design would lose clarity. The best practice is to have a few top-level blocks in a page with each top-level block composed of smaller blocks. In this way, you "build blocks out of blocks". It also means that you are able to refactor HTML codes as you do with other programming languages. Refactoring is a huge progress in HTML coding and coServ is the first and probably still the only platform to make that possible.
The signature of embedding a block is:
<% block(id, blockPath, params); %>
The 'id' parameter gives the generated block an ID. You can use the block ID to look for the block in DOM, or to find the block controller (which will explained in the next chapter). The 'id' parameter is optional. If you don't specify one, coServ will automatically generate one for you.
The block path is a file path relative to the block view directory (whose location is '$web_root/$web_name/themes/$theme/block/views'). With the block path, coServ can locate the HTML/CSS templates for the embedded block. The 'blockPath' parameter is required.
The 'params' parameter is the actual input to a block.
Let's show an example. Assuming you have a block which will display informations about a book and its endpoint (URL) is like:
http://www.foo.com/book/info/537
where '537' is the book ID. If you want to include such a block, you can simply:
<% block('/book/info/537'); %>
Now, let's further assume the block can take an 'showPricing' input to decide if it will display the pricing info of books. The invocation URL becomes:
http://www.foo.com/book/info/537?showPricing=true
and below is how to embed such a block:
<%
var params = {
showPricing: true
};
block('/book/info', params);
%>
Polyform of the block function
The block() function is polyformed. That is, you can specify one, two or three parameters in the function call. Below shows the possible forms of the block function call:
- block(blockPath) : the block does not need inputs and you'll allow coServ to generate an ID for the block.
- block(id, blockPath) : the block does not need inputs.
- block(blockPath, params) : you'll allow coServ to generate an ID for the block.
- block(id, blockPath, params) : you've specified all the parameters.
Embedding wcomps
Embedding wcomps is similar. Below is the signature:
<% wcomps(id, wcompPath, params, options); %>
The id, wcompPath and params parameters are similar to the ones when embedding blocks. The options parameter now only supports one property: options.noWrapper.
By default, coServ will add a <div> wrapper around a wcomp so coServ can assign an ID to the wcomp and provide CSS encapsulation to it. However, this may sometimes cause layout problems if you're using some CSS or UI libraries which would not expect such an additional wrapper. To solve the problem, you can ask coServ not to add an additional wrapper by specifying the options.noWrapper property to be true.
Data binding
A block view can bind contents (data) of a block model. coServ supports one-way data binding. We believe the reverse data-binding should be explicitly done by programmers because in large scale applications two-way binding could be very confusing and error-prone.
Before composing a block view, coServ will query its block model (when it's applicable). There is a catch that querying a block model may not always be successful. To make the data-binding semantics clear and robust, it's recommended that the data model should package the result as an object with three properties:
- errCode: an integer number indicating whether the execution of data model is successful. 0 means successful. Any non-zero integer means error.
- message: a text message to supplement the error code.
- value: the actual value of the query result.
You can access the result by simply referring to their property names. For example, you can do the following in a HTML template:
<%
if (errCode) {
%>
<div>
Error: <%=message%>
</div>
<% } else { %>
<div>
<div>
Title: <%= value.title %>
</div>
<div>
Author: <%= value.author %>
</div>
</div>
<% } %>
The sample code above checks the errCode parameter to see if querying the data model is successful. If something goes wrong, the error message will be presented instead of showing the actual contents.
Context and variables
In addition to its data model, coServ also allows you to access many useful information of a block via variables.
The context variable
The context variable can be referred to as 'ctx'. It has the following properties:
- title: this shows the title of a HTML page into which the block is embedded.
- description: similar to the 'title' property but showing the page description.
- pageURI: the URI of a HTML page into which the block is embedded.
- id: the CSS ID of the block.
The blockInfo variable
The block-info variable can be referred to as 'bi'. It has the following properties:
- uri: the endpoint of a block.
- query: input to a block.
- cookies: cookies of a block request.
- http-headers: http headers of a block request.
- client: infomation about the request client. It's an object with only one property: category. The possible values of client.category are 'desktop', 'tablet' and 'mobile'.
- isGuest: the user who is making a block request is a guest user or not.