> For the complete documentation index, see [llms.txt](https://docs.wem.io/platform/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.wem.io/platform/tutorials/building-widgets/in-depth/5-view-state/1-runtime.viewstate.md).

# 1. Runtime.viewState

{% hint style="info" %}
A CSP-compliant version is available for download on the [Examples](/platform/tutorials/building-widgets/examples.md) page.
{% endhint %}

In the previous module of the view state chapter on building the Message Widget, 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:

```html
<div id="<?attr OutputId() ?>" class="message <?attr @Style ?> <?attr if @Collapsed then "collapsed" else "expanded" ?>" onclick="toggleMessageBox(this)">
	<span class="box ellipses">
		<span class="glyphicon glyphicon-comment"></span> 
		...
	</span>
	<span class="box text">
		<span class="glyphicon glyphicon-heart-empty"></span>
		<?= @Text ?>
	</span>
</div>
<? scriptmodule "message-box" ?>

	function toggleMessageBox(element) {
		element.classList.toggle("collapsed");
		element.classList.toggle("expanded");
	
		const outputId = element.id;
		const collapsed = element.classList.contains("collapsed");
		
		Runtime.viewState(outputId).set("Collapsed", collapsed);
	}
	
<? end ?>
```

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:

```less
.message {
	display: flex;

	> .box {
		border: 1px solid transparent;
		border-radius: 4px;
		cursor: pointer;
	}
	
	&.danger > .box {
		background: lighten(@brand-danger, 25%);
		border-color: lighten(@brand-danger, 10%);
		color: darken(@brand-danger, 23%);
	}

	&.info > .box {
		background: lighten(@brand-info, 25%);
		border-color: lighten(@brand-info, 10%);
		color: darken(@brand-info, 23%);
	}

	&.success > .box {
		background: lighten(@brand-success, 25%);
		border-color: lighten(@brand-success, 10%);
		color: darken(@brand-success, 23%);
	}

	&.collapsed > .box {
		padding: 5px;

		&.text {
			display: none;
		}
	}

	&.expanded > .box {
		padding: 15px;

		&.ellipses {
			display: none;
		}
	}
}
```

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.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.wem.io/platform/tutorials/building-widgets/in-depth/5-view-state/1-runtime.viewstate.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
