webfirmframework for Java Experts

URL rewriting/routing in wffweb-12

The advantage of URL rewriting/routing in this framework is that it changes only the needful part of the UI (so the server sends only the data required for that portion), it will not download/reload the entire page. We can get the same performance advantage of a single page application with url rewriting/routing feature.


URIStateSwitch.whenURI or AbstractHtml.whenURI

AbstractHtml is the implementation class of URIStateSwitch interface so whenURI methods can be called on any tag (but on NoTag only consumer based overloading methods are allowed.).

whenURI method mainly has four arguments. They are, whenURI(predicate, successSupplier/successConsumer, failSupplier/failConsumer, index) . The argument of predicate.test is the changed URI. The argument of consumer is an TagEvent object which contains info like the changed URI, the sourceTag etc... There are many overloading methods available with different combinations of these arguments for ease of use.

The whenURI method can be called multiple times on a tag, the failSupplier/failConsumer is valid only on the last whenURI call.

Now, let's talk about how it works. There are client side and server side methods to change URI of the app, at server side browserPage.setURI can be called and at client side wffAsync.setURI can be called, their various usage is explained in the upcoming section.

The main logic is when there is a URI change the predicate will be invoked and if the predicate.test method returns true , the successSupplier/successConsumer will be invoked otherwise the failSupplier/failConsumer will be invoked unless there is further predicates to test, i.e. we can call whenURI method to add multiple actions. Here, the role of the supplier is to provide innerHtml/child tags for the corresponding tag that means the existing children of the tag will be replaced with the provided tags (returned by the supplier). The role of consumer is to do any operation at the time of URI change and the current children of the tag will not be altered unlike supplier. Checkout this sample code here.

Let's see an example with only successSupplier
						AbstractHtml div = new Div(null);

div.whenURI(uriEvent -> uriEvent.uriAfter().startsWith("/ui/user"),
() -> {return new AbstractHtml[]{ new H1(null).give(TagContent::text, "h1")};});

					
When setURI method is called either from server side or client side, the predicate will invoke and if the predicate test returns true then existing children of div will be replaced with H1 otherwise existing children will be removed as there is no failConsumer/failSupplier passed.

Let's see an example with both successSupplier and failSupplier
						AbstractHtml div = new Div(null);

div.whenURI(uriEvent -> uriEvent.uriAfter().startsWith("/ui/user"),
() -> {return new AbstractHtml[]{ new H1(null).give(TagContent::text, "h1")};},
() -> {return new AbstractHtml[]{ new H2(null).give(TagContent::text, "h2")};});

					
When setURI method is called either from server side or client side, the predicate will invoke and if the predicate test returns true then existing children of div will be replaced with H1 otherwise with H2 .

Let's see an example with multiple whenURI methods
						AbstractHtml div = new Div(null);
 
div.whenURI(uriEvent -> uriEvent.uriAfter().startsWith("/ui/user"),
() -> {return new AbstractHtml[]{ new H1(null).give(TagContent::text, "h1")};});
 
div.whenURI(uriEvent -> uriEvent.uriAfter().startsWith("/ui/admin"),
() -> {return new AbstractHtml[]{ new H2(null).give(TagContent::text, "h2")};},
() -> {return new AbstractHtml[]{ new H3(null).give(TagContent::text, "h3")};});

					
When setURI method is called either from server side or client side, the predicate of the first whenURI action will invoke and if the predicate test returns true then the existing children of div will be replaced with H1 if that predicate test returns false it will invoke the predicate of the second whenURI action if the test returns true then existing children of div will be replaced with H2 if test returns false then existing children of div will be replaced with H3 .
In short:
If browserPage.setURI("/ui/user") is called the div will contain h1 tag.
If browserPage.setURI("/ui/admin") is called the div will contain h2 tag.
If browserPage.setURI("/ui/other") is called the div will contain h3 tag.
In the above example there is no failSupplier in the first whenURI method because the failSupplier/failConsumer is valid only on the last whenURI call.

removeURIChangeActions()

This method can be called on a tag to remove all whenURI actions.

removeURIChangeAction(int index)

When the whenURI is called multiple times those actions will be indexed in the tag. This method can be called on a tag to remove whenURI action at a particular index.

browserPage.setURI(String)

This method can be called at server side to change the URI of the app.

browserPage.setURI(String, boolean)

This method can be called at server side to replace the current URI with the given one in the browser page history only if true is passed as the second argument. If false is passed as second argument then it changes the current uri with the given one and adds to the browser history, i.e. browser page navigates to the given uri, it is similar to calling browserPage.setURI("/ui/user/items") .

browserPage.getURI()

This method can be called at server side to get the current URI of the app.

browserPage.beforeURIChange(URIEvent uriEvent)

This method can be overridden in the extended class of BrowserPage to mask the uriBefore value in the URIEvent object. Use case: This will be useful to improve security if third party components are used in the application. If the uriBefore contains some sensitive path param values and we don't want to let any third party components to read such details then override this method and return an URIEventMask with new uriBefore value.


Client side features

wffAsync.setURI

This function can be called at client side to change the URI of the app. There are four arguments for this function. The first argument is the uri to change, the second and third arguments are functions which will be invoked on uri change and after uri change respectively. The fourth argument is a boolean argument, if true is passed then the current uri in the browser page history will be replaced with the given uri (i.e. first argument). The second, third and fourth arguments are optional.

Checkout the following example

var uri = '/ui/user/items/view';

// the event object contains uriBefore and uriAfter properties
var preFunction = function(event) {
    console.log('preFunction');
};

// the event object contains uriBefore, uriAfter and origin properties
var postFunction = function(event) {
    console.log('postFunction');
};

// true to replace existing uri in the browser page history with the given uri passed
var replace = false;

// preFunction, postFunction and replace arguments are optional
wffAsync.setURI(uri, preFunction, postFunction, replace);


preFunction will be invoked if there is a uri change, i.e. at time of calling if the current uri is same as the uri value passed in wffAsync.setURI function then this function will not invoke. This will be useful to show a progress icon only if there is a change in the URI. postFunction will be invoked only after the UI changes applied by the server, this function may be used to stop the progress icon. If you want to handle progress icon globally, you can use wffGlobalListeners . The fourth argument is false here i.e. not to replace the current uri in the browser history instead navigate the page to the given uri.

wffGlobalListeners

wffGlobalListeners, onSetURI and afterSetURI are legal names used by the framework. It is not mandatory to have wffGlobalListeners in your js file. You can add the following code in the js file to listen to URI change events.


const wffGlobalListeners = new function() {

// the event object contains uriBefore, uriAfter, origin and initiator properties
    this.onSetURI = function(event) {
        console.log('wffGlobalListeners > onSetURI', event);
        loadingIcon.hidden = false;
    };

// the event object contains uriBefore, uriAfter, origin and initiator properties
    this.afterSetURI = function(event) {
        console.log('wffGlobalListeners > afterSetURI', event);
        loadingIcon.hidden = true;
    };

};

wffGlobalListeners , onSetURI and afterSetURI are optional. event.uriBefore is the uri before the uri change, event.uriAfter is the uri after uri change and event.origin will contain either client or server . If the setURI is called by client side JavaScript code (like wffAsync.setURI ) then the event.origin will contain value as client and if the setURI is called by server side code (like browserPage.setURI ) then the event.origin will contain value as server .
onSetURI will be invoked only when there is a URI change at the time of calling wffAsync.setURI function or browserPage.setURI method. Its role is similar to preFunction in wffAsync.setURI function.
afterSetURI is invoked only after URI set and the UI changes applied by the server. Its role is similar to postFunction in wffAsync.setURI function but in addition to that if browserPage.setURI is called at server side this function will get invoked.

The initiator can have value as serverCode , clientCode or browser . If the value is serverCode it means that the URI change is initiated by server side Java code. If the value is clientCode it means that the URI change is initiated by client side JavaScript code. If the value is browser it means that the URI change is initiated by some browser actions such as browser's back/forward button (history buttons) click.
Note: If the value of initiator is browser then the uriBefore property may not available.