# 4.2 preview - simplified view model API in JS Modules

**URL:** <https://forum.dotvvm.com/t/4-2-preview-simplified-view-model-api-in-js-modules/65>\
**Category:** Announcements\
**Created:** [September 27, 2023, 2:30pm UTC](https://forum.dotvvm.com/t/4-2-preview-simplified-view-model-api-in-js-modules/65 "2023-09-27T14:30:18Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![exyi](https://forum.dotvvm.com/user_avatar/forum.dotvvm.com/exyi/32/9_2.png) [@exyi](https://forum.dotvvm.com/u/exyi)\
**Post date:** [September 27, 2023, 2:30pm UTC](https://forum.dotvvm.com/t/4-2-preview-simplified-view-model-api-in-js-modules/65/1 "2023-09-27T14:30:18Z")

</div>

Continuing the informal series about news in DotVVM 4.2, let me show how we have improved the JavaScript API for modifying the view model, specifically in [JS View Modules](https://www.dotvvm.com/docs/4.0/pages/concepts/client-side-development/js-directive/overview).

From the very first DotVVM version, we had the `dotvvm.viewModels.root.viewModel` “API” - the view model is simply available to anyone as an object wrapped in Knockout observables. Modifying a single property is easy, just remember to unwrap each observable by calling it - `dotvvm.viewModels.root.viewModel.Collection()[5]().MyProperty("new-value")`. However, it gets quite cumbersome to unwrap everything in a more complex logic. Moreover knockout observables are rather expensive to create, so in DotVVM 3.0 we internally switched to manage the state using a plain immutable object. Knockout observables are obviously still available, but only created lazily, avoiding unnecessary allocations.

With this rewrite, we introduced the `dotvvm.state` property and `dotvvm.patchState` function for accessing and updating the immutable view model object. The `patchState` function accepts a partial object, updating only the specified properties, leaving the rest unchanged. For example, `patchState({ User: { Name: "new-name" } })` updates the User.Name property.

The `state`/`patchState` API is also available on each knockout observable, enabling us to use the state API in knockout bindings and binding handlers. Knockout observables also have a `setState` function, which simply replaces the entire object. This function was missing from the global `dotvvm` object, because, well, we forgot to add it.

In 4.2 we fix this disparity, with the same API available on `dotvvm`, knockout observables and JS module context (`DotvvmModuleContext`):

- `state` - returns the current value of the view model
- `setState(newValue)` - replaces the current value
- `patchState(newValues)` - replaces only the specified properties, for example `patchState({ Title: "new title" })`
- `updateState(currentValue => computeNewValue(currentValue))` - applies the specified function onto the current view model

### JS Modules

In previous versions, we noticed that it’s often useful to directly modify the client-side view model inside View Modules (instead of calling a [named command](https://www.dotvvm.com/docs/4.0/controls/builtin/NamedCommand)). If the module is loaded into a page, we can simply use the global `dotvvm.state`/`dotvvm.patchState`, however, it was quite cumbersome in markup controls. In 4.2, the API is unified as `context.state`/`context.setState`/… In markup controls, it works on the current viewmodel (the type declared using `@viewModel` directive). The same API available for properties in `context.properties.MyProperty.state`.

A simple usage might look as follows. In the example we download new data from a REST endpoint and place it into a view model using the `context.updateState` function.

```javascript
export default context => {
    return {
        async myNamedCommand() {
            const newData = await (await fetch("/api/newData")).json()
            context.updateState(vm => ({...vm, Data: newData }))
        }
    }
}

```

> We also changed that modules are initialized after dotvvm is fully initialized, making it safe to access `state`, `setState`, … in the constructor

### Lenses

In simple cases using the updating the immutable objects in not very complicated, one just has to get used to the object spread syntax: `{...oldVM, MyProperty: newValue }`. However, when the object gets nested heavily, the code gets rather annoying to write. You might use the knockout observable API for this, but mixing the two is sometimes problematic.

In functional programming, all objects are immutable – there is nothing like simply assigning the value to a nested object, you have to go through the trouble of updating each object on the path to it. There has to be a better way, since this code quite repetitive and Haskell programmers are known for other things than writing repetitive code 😁

The “Haskell” way is called lenses or more generally optics. There is number of implementations in Javascript and you can use it quite easily with dotvvm. I won’t go into details about lenses and optics here, there is enough of it on the web. Some libraries are powerful, general optics are much more than a way to do immutable assignments, but even I’d say this comes at quite steep cost of complexity. However, there are reasonably sober implementations, for example [shades](https://github.com/jamesmcnamara/shades) - a quick example how to use it with `updateState`.

```auto
const lens = shades.mod("Questions", "Items", questionIndex, "Votes")
dotvvm.updateState(lens(votes => votes + 1))

```

The `lens` is essentially a function which upgrades a modification function (`votes => votes + 1`) from working on a number to working on an object containing properties `Questions.Items[index].Votes`. `dotvvm.updateState` expects the function to accept the entire view model and return the new one, so when we upgrade the `number => number` using the lens, the types will match.

Just to peak into the more powerful constructs, you can also simply update all elements of the collection 😉

```auto
const lens = shades.mod("Questions", "Items", shades.all(), "Votes")

```

or just elements matching a condition:

```auto
const lens = shades.mod("Questions", "Items", shades.matching(q => q.Title.startsWith("A")), "Votes")

```
