webfirmframework for Java Experts
wffweb best practices
Build UI with minimal tags
When building heavy UI, it's important to follow this rule to keep only necessary tags. It reduces the load of a browser, why should the browser take care about unnecessary tags!!
Use minimal tag manipulation methods
If there are continuous invocation of tag manipulation methods then try to convert it to invoking minimal tag manipulation methods. Check out the below examples
Eg 1 :-
List<AbstractHtml> children = div1.getChildren();
for (AbstractHtml child : children) {
div1.removeChild(child);
}
//should be as follows instead of the above
div1.removeAllChildren();
Eg 2 :-
div1.removeAllChildren();
div1.appendChild(div2);
//should be as follows instead of the above
div1.addInnerHtml(div2);
Eg 3 :-
div1.removeAllChildren();
div1.appendChild(div2);
div1.appendChild(div3);
//should be as follows instead of the above
div1.addInnerHtmls(div2, div3);
Use BrowserPage#holdPush & BrowserPage#unholdPush wherever possible
Surround with
BrowserPage#holdPush
and
BrowserPage#unholdPush
methods for multiple tag manipulation statements. When a tag
manipulation method (eg:
div.removeAllChildren()
) is executed the framework sends its UI update to the client as a
push so if there are multiple statements then there will be
multiple push. When all tag manipulation methods are surrounded
with
BrowserPage#holdPush
and
BrowserPage#unholdPush
then all those UI updates will be sent in a single push so that the
end user will not see the UI changes are taking place one by one in
a slow network. Also ensure that the
BrowserPage#unholdPush()
is called inside finally block. See the sample code given below
Eg :-
try {
browserPage.holdPush();
div1.addAttributes(new Style("background:green"));
div2.addAttributes(new Id("div2Id"));
div3.addAttributes(new Name("div3"));
div1.addInnerHtmls(div2, div3);
} finally {
//pushes UI updates for the above four methods in a single push
browserPage.unholdPush();
}
But there are also cases where these methods are not suitable, for
example you are adding up to 1000 rows (or more than that) in a
table. In such case if you are writing that code between
BrowserPage#holdPush
and
BrowserPage#unholdPush
then the end user will have to wait a long time to see the result
because the framework sends all changes in a single push and the
browser displays it only after receiving all these UI updates.
Here, the end user is expecting a lazy loading. In such scenarios
it's better to avoid using these
BrowserPage#holdPush
and
BrowserPage#unholdPush
methods.
The usage of
BrowserPage#holdPush
and
BrowserPage#unholdPush
methods depends upon the requirements.
There will be a major improvement for holdPush/unholdPush implementation in wffweb-3.0.1 or later versions.
The improvement is with when holdPush/unholdPush
method is called by multiple threads from the same browserPage
instance,
if the holdPush
is called three times the unholdPUsh
needs to be called three times to unhold the push
(in previous versions one unholdPush call will unhold the push).
Do not return anonymous class
When you want to reuse a set of tags, you have to keep a separate template class for it.
First let us look at a bad practice:-
public class UserDetails {
public Div getUserDetailsDiv(final String fullName) {
Div details = new Div(null) {{
new Span(this) {{
new NoTag(this, "Name : " + fullName);
}};
}};
return details;
}
}
public static void main(String[] args) {
UserDetails userDetails = new UserDetails();
System.out.println(
userDetails.getUserDetailsDiv("Hitomi").toHtmlString());
}
Use the below code instead which does the similar job
public class UserDetailsTemplate extends Div {
public UserDetailsTemplate(String fullName) {
super(null);
develop(fullName);
}
private void develop(final String fullName) {
//before 3.x.x
// new Span(this) {{
// new NoTag(this, "Name : " + fullName);
// }};
// as of 3.x.x
new Span(this).give(span -> {
new NoTag(span, "Name : " + fullName);
});
}
}
public static void main(String[] args) {
UserDetailsTemplate userDetails = new UserDetailsTemplate("Hitomi");
System.out.println(userDetails.toHtmlString());
}
But if you really want a method to return a div, then you have to change the coding style like this
with the latest version of 3.x.x:public class UserDetails {
public Div getUserDetailsDiv(final String fullName) {
Div details = new Div(null).give(div -> {
new Span(div).give(span -> {
new NoTag(span, "Name : " + fullName);
});
});
return details;
}
}
public static void main(String[] args) {
UserDetails userDetails = new UserDetails();
System.out.println(
userDetails.getUserDetailsDiv("Hitomi").toHtmlString());
}
OR
public class UserDetails {
public Div getUserDetailsDiv(final String fullName) {
Div details = new Div(null);
Span span = new Span(details);
new NoTag(span, "Name : " + fullName);
return details;
}
}
public static void main(String[] args) {
UserDetails userDetails = new UserDetails();
System.out.println(
userDetails.getUserDetailsDiv("Hitomi").toHtmlString());
}
Use Executor Framework & wffweb-3.0.1 or later when using multiple threads
There are use cases where we need to load different parts of ui in an asynchronous manner. Java Executor Framework will be very useful to safely handle multiple threads for it. It's available as part of core java. wffweb-3.0.1 or later is recommended to use if ui portions are loaded in multiple threads. There some major thread safety improvements done in wffweb-3.0.1 & later and previous versions are not 100% thread safe.
You may use Kotlin to develop UI Templates to reduce coding effort
Using Kotlin to develop UI templates will be better productive, maintainable and readable. Kotlin is designed with Java Interoperability in mind, that is you can call/use Kotlin code in Java and vice versa.