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.