There is no dynamic code in a template file. An Asta4D template file is always a pure HTML file which can be easily maintained by front-end developers, it is very design friendly and we can reduce the workload for source refactoring by over 90%.
Declare snippet class in template file
Example 3.1. Sample of snippet declaration:
<section> <article> <div afd:render="SimpleSnippet">dummy text</div> <afd:snippet render="SimpleSnippet:setProfile"> <p id="name">name:<span>dummy name</span></p> <p id="age">age:<span>0</span></p> </afd:snippet> </article> </section>
The "afd:render" attribute in div tag declares a Java class which is in charge of the concrete rendering logic, such Java class is usually called as a snippet class. A snippet class can be declared by a "afd:snippet" tag too, as you have seen in above sample. The rendering logic is secluded to independent Java classes by snippet declaration and it is not necessary to learn any new template language for back-end developers. The back-end guys can achieve all the rendering logic easily by powerful Java language which they have been adept to, no learning costs, no magic codes, succinct Java source only.
Implement a snippet class
Example 3.2. Sample of snippet class:
public class SimpleSnippet { public Renderer render(String name) { if (StringUtils.isEmpty(name)) { name = "Asta4D"; } return Renderer.create("div", name); } public Renderer setProfile() { Renderer render = Renderer.create(); render.add("p#name span", "asta4d"); render.add("p#age span", 20); return render; } }
The Renderer class is provided by framework to declare rendering actions. Renderer uses traditional CSS selector to reference the rendering target and receive the rendering value at the same time, amazing and powerful.
If the above template file and snippet class are executed, we would get the following result:
Example 3.3. Result of snippet execution:
<section> <article> <span>Hello Asta4D</span> <p id="name">name:<span>asta4d/span></p> <p id="age">age:<span>20</span></p> </article> </section>
Asta4D introduces 4 extra tags to the html template file: afd:extension, afd:block, afd:embed, afd:snippet, which will not disturb most html editors, accordingly Asta4D is extremely friendly to front-end engineers. On the other hand, we can see that all the rendering logic is fulfiled by Java code and the back-end engineers can compose their back-end logic and rendering logic very smoothly without magic skills and extra learning costs, which means highly boost of productivity.
Be careful of "ElementUtil.parseAsSingle" which would cause cross-site issues. Basically the parseAsSingle api is not recommended for common use. See more atSection 3.3, “Immune from cross-site hole”.
In Asta4D, all the rendering logic is declared by a Renderer class which accepts various types as rendering value.
Example 3.4. Sample usage of Renderer
Renderer render = Renderer.create(); render.add("#someIdForInt", 12345); render.add("#someIdForLong", 12345L); render.add("#someIdForBool", true); render.add("#someIdForStr", "a str"); render.add("#someIdForNull", (Object) null); render.add("#someIdForClear", Clear); // NOTE: parseAsSingle is not recommended to use Element newChild = ElementUtil.parseAsSingle("<div></div>"); render.add("#someIdForElementSetter", new ChildReplacer(newChild)); render.add("#someIdForElement", ElementUtil.parseAsSingle("<div>eee</div>")); render.add("#someIdForRenderer", Renderer.create("#value", "value"));
List of data can be rendered via Renderer too:
Example 3.5. Sample of list rendering
Renderer render = Renderer.create()(); render.add("#someIdForInt", Arrays.asList(123, 456, 789)); render.add("#someIdForLong", Arrays.asList(123L, 456L, 789L)); render.add("#someIdForBool", Arrays.asList(true, true, false)); render.add("#someIdForStr", Arrays.asList("str1", "str2", "str3")); // NOTE: parseAsSingle is not recommended to use Element newChild1 = ElementUtil.parseAsSingle("<div>1</div>"); Element newChild2 = ElementUtil.parseAsSingle("<div>2</div>"); render.add("#someIdForElementSetter", Arrays.asList(new ChildReplacer(newChild1), new ChildReplacer(newChild2))); render.add("#someIdForElement", Arrays.asList(newChild1, newChild2)); render.add("#someIdForRenderer", Arrays.asList(123, 456, 789), new RowRenderer<Integer>() { @Override public Renderer convert(int rowIndex, Integer obj) { return Renderer.create("#id", "id-" + obj).add("#otherId", "otherId-" + obj); } });
DOM attribution can be done too:
Example 3.6. Sample of DOM attribution rendering
render.add("#id", "+class", "yyy"); render.add("#id", "-class", "zzz"); render.add("#id", "+class", "xxx"); render.add("#id", "value", "hg"); render.add("#id", "href", null); render.add("#X", "value", new Date(123456L));
The previous samples show us that all the operations to DOM are delegated by Renderer except create DOM from String value by "ElementUtil#parseAsSingle". As a matter of fact, all the delegated operations cause all the rendering values are escaped by force, which means that your system is, by nature, immune from cross-site problems if there is no "ElementUtil#parseAsSingle" calling in your system. You do not need to be wary of illegal characters by from the clients since all the values will be escaped as HTML compulsorily.
On the other hand, according to our practice, it is rarely that you have to call “ElementUtil.parseAsSingle” to generate the rendering content from a raw HTML string, so just take care of your usage of this exceptional api then you can say bye-bye to the cross-site holes.Testing web page is always a complex task and we usually verify rendering results by selenuim which is as a common technology. By Asta4D, testable Renderer can replace over than half of selenium tests to simpler junit tests.
According to the previous samples, all the rendering logic is holded by an instance of Renderer class, so simply, we can verify almost rendering logic by testing the returned Renderer instance from the snippet method.
Example 3.7. Sample RendererTester
RendererTester tester = RendererTester.forRenderer(render); Assert.assertEquals(tester.get("#someIdForInt"), 12345); Assert.assertEquals(tester.get("#someIdForLong"), 12345L); Assert.assertEquals(tester.get("#someIdForBool"), true); Assert.assertEquals(tester.get("#someIdForStr"), "a str"); Assert.assertEquals(tester.get("#someIdForNull"), null); Assert.assertEquals(tester.get("#someIdForClear"), Clear);
More samples can be found in source