Manipulating the view state via JavaScript can be quite overwhelming. The following table illustrates the differences between various scenarios.
The table below assumes that some view state has been set via JavaScript after a page load to a view state field.
Persisted on browser refresh (F5)
No
Yes
Yes
Persisted on WEM application event
Yes
Yes
Yes
Removed when the widget is unloaded
Yes
Depends
Depends
Removed when the browser tab is closed
Yes
Yes
No
Removed when the browser is closed
Yes
Yes
No
Removed when your computer is hit by a meteor
Yes
Yes
Yes
In the case of a widget being unloaded, there are two scenarios where the outcome depends on specific conditions. As discussed in the previous chapter, we have the option to manually clear the state of localStorage
and sessionStorage
in the unloadscript
block.
In the previous module of the view state chapter, we declared a field and created an event to toggle that field, allowing the message box to expand and collapse. Another way to change the view state is through JavaScript. Let's update our previous implementation of the message box to utilize JavaScript instead.
First, we need to add the Text
text property and the Style
dropdown property, which should include the values "info," "success," and "danger." You can refer to the previous view state chapter for guidance on how we created these properties. Next, we will create a boolean view state field named Collapsed
.
Use the following script:
What has changed is that there is no register event
because we don't have an event in this widget, nor do we need @Collapsed := @Collapsed ? false
to initialize that field to false
when it is unknown
. This works in this case because when we check with if @Collapsed then
, it does not matter whether the value is unknown
or false
.
We introduce a new function, OutputId()
. Every widget has its own Output ID, which serves as a unique identifier on the page. This is perfect for use in conjunction with the id
attribute, which should also be unique! We will discuss this function in more detail in later chapters.
We changed the onclick
attribute to execute the toggleMessageBox()
function instead of triggering an event, as we did in the previous chapter. A new feature is that this function is declared inside a scriptmodule
, which is another way to add JavaScript functionality to the widget, aside from including it via a file. We will cover this topic in more detail in later chapters.
The toggleMessageBox(element: HTMLElement)
function is straightforward. Depending on the state, we toggle between the two classes. We retrieve the Output ID from element.id
and check if it is collapsed by verifying if the element's class list contains the "collapsed" class. The Runtime.viewState(outputId: string)
function returns the view state of the widget with that Output ID. We use the set(key: string, value: any)
function, where the key
is the name of the view state field, to set a value for that field. There are two additional functions besides set()
: get(key: string)
and clear(key?: string)
. These functions are self-explanatory, but you can find more information about them in the WEM widget reference.
The styles remain unchanged and are the same as before:
Now, place this widget on a template and add a refresh button to the page. Preview the widget. Notice that when you click the widget, there is no refresh, unlike in the previous implementation. This creates a better user experience. However, there is one downside: the view state is only persisted when we perform a postback to the WEM Runtime, such as when clicking the refresh button.
Try toggling the state of the widget, and then press F5 to refresh the page in your browser. You will notice that the widget retains its previous state. Now, toggle the state of the widget again and click the refresh button we added to the page. This time, the state is saved.
In the next chapter, we will explore a more modern approach to storing view state using localStorage
and sessionStorage
. This method addresses the refresh issue but comes with its own set of challenges.
You can also utilize sessionStorage
and localStorage
, which are available in the JavaScript standard library. These solutions are purely JavaScript-based and do not rely on WEM view state internals. Let's update our previous widget slightly.
Update the script with the following:
In this update, we introduce startupscript
, which is a JavaScript code block that runs after the page has been initialized and all static HTML elements are rendered. We also have unloadscript
, which executes when a WEM event is triggered, just before the new page is rendered. We will discuss these script blocks in more detail in later chapters.
Comparing the previous code with the new version, you will notice that the initializeMessageBox()
function is used to initialize the message box, as the name suggests. Here, we set the class names and retrieve the view state using sessionStorage
through JavaScript. Additionally, we updated the code to set the click
handler here instead of using an onclick
attribute. While functionally there is no difference, this approach promotes better organization, a principle known in computer science as "separation of concerns."
Looking at the function getKeyMessageBox(outputId, nodeId)
and the line const key =
${element.id}:${nodeId}:collapsed;
, we see that the key
is created by combining the Output ID, a node ID, and an arbitrary identifier, separated by a colon. Since sessionStorage
keys are stored globally, we need to ensure that the keys for this widget are globally unique. If we had simply used "collapsed" as the key and had two identical widgets on the same page, they would overwrite each other's state. The node ID ensures that the view state of this widget is unique per interaction node. While we use a colon here, any character that is not alphanumeric can be used, as those characters are reserved for the Output ID and node ID.
Depending on the widget you are creating, you may want to clear the view state—demonstrated using clearStateMessageBox()
here—when unloading the widget.