# Views, rendering, and the reactive data model
Source of truth: `src/js/elements/View.js` (`MMO.elements.View`, read in full) and `src/js/core/Template.js` (`MMO.core.Template`, its base class, read in full). `MMO.core.Map` (`src/js/core/Map.js`) is the underlying reactive data container — read that file directly for its exact API before relying on details not covered here.
## The three layers
```
MMO.core.Template – imperative DOM builder: open()/attr()/text()/html()/close(), no data binding
↑ extends
MMO.elements.View – + binds a subtree to a MMO.core.Map path: get/set/watch/loop/test, sub-views
↑ used by
CustomElement/CustomShadowElement – wires a View to a Web Component's lifecycle (see custom-elements.md)
```
A `.jhs` file's compiled `render($view)` method is just a long chain of calls on a `View` (`$view` = `this.view` for a custom element) — everything in [jhs-syntax.md](https://mcp.mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fjhs-syntax.md) is sugar over this API. You can call the same methods yourself from a `<script>` method when the declarative markup isn't enough.
## Rendering a standalone `<view>` from JS
```js
MMO.domReady(() => {
MMO.elements.View.RenderView('my-view', document.getElementById('container'), {name: 'John'}, /*basepath*/'', /*ref*/null, /*scope*/null);
});
```
`RenderView(name, node, map, basepath, ref, scope)`:
- `name` — the view's `id` as declared in the `.jhs` file.
- `node` — DOM element to render into (children are inserted, existing content is not cleared for you).
- `map` — a plain object/array, or an existing `MMO.core.Map`/`MMO.elements.Entity` — becomes the view's data root.
- `basepath` — JSON path within `map` this view is scoped to (usually `''`).
- `ref` — insert after this sibling instead of at the front.
- `scope` — sets `$this` inside `on*`/`{=...}` expressions (defaults to the view itself).
From inside another view/element's markup, embed instead of calling JS: `<view src="my-view" path="user"></view>` — see [jhs-syntax.md](https://mcp.mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fjhs-syntax.md#view--define-or-embed-a-reusable-view).
## Paths
Every data-accessing method (`get`, `set`, `has`, `watch`, `delete`, `push`, `pop`) takes a **path**: a string like `'a/b/c'`, an integer, or an array of segments.
- Relative (no leading `/`): resolved against the view's own `basepath` — `'name'` inside a view scoped to `user` reads `user/name` in the root map.
- Absolute (leading `/`): resolved from the **root view**'s content, ignoring this view's own scoping — `'/currentUser/id'`.
- `..` segments walk up (relative-parent notation), per `walkTo()`'s docblock.
- A path ending in the literal segment `$$` or `$#` targets the *key* / *numeric index* of that map entry instead of its value — same special values usable inside `{path/$$}` in markup.
## Core `View` API (all in `src/js/elements/View.js`)
| Method | Does |
|---|---|
| `get(path)` / `has(path)` | Read a value / check existence. `get` returns `undefined` (not throw) if the path doesn't resolve. |
| `set(path, value)` | Write; returns `true` only if the value actually changed (use this to guard side effects, as `mmo-radio.jhs`'s `set value()` does before dispatching a `change` event). |
| `getText(path)` / `getHTML(path)` | Text/HTML string representation of a path's value (delegates to the `Map`'s own `getText`/`getHTML`). `bind-text` renders through `getText`; `{path}` in text content and `bind-html` both render through `getHTML`, whose strings go into `innerHTML` unescaped — see [jhs-syntax.md](https://mcp.mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fjhs-syntax.md#expressions-path-and-js). |
| `delete(path)` | Remove; if `path` is `''`/root, removes the view itself from the DOM too. |
| `push(path, value)` / `pop(path)` | Array-style append/remove-last on the Map at `path`. |
| `merge(value)` | Shallow-merges `value` into the view's own content (`this.content.merge(value)`). |
| `watch(path, listener, onPathChange?)` | Subscribe to changes at `path`. `listener(value, map, key, special)` fires immediately on subscribe and again on every change. `onPathChange(newPathString)` fires if the *path itself* shifts (e.g. an ancestor key was renamed) — most manual `<script>` code just needs the first two params. |
| `unwatch(path, bind)` | Undo a `watch()`, using the binding object it returned. |
| `createFunction(name, fn, onChange, ...pathArgs)` | The primitive behind `{=...}`: re-runs `fn` whenever any of `pathArgs` changes, calling `onChange(newValue)` only when the computed value actually differs. Useful if you need `{=...}`-style reactivity from a `<script>` method instead of markup. |
| `loop(path, renderFn, insertFn?, deleteFn?)` / `loopFn(...)` | Programmatic equivalent of `<for>`. |
| `test(...tests)` | Programmatic equivalent of `<if>/<elseif>/<else>` — see the JSDoc above `test()` in `View.js` for the exact tuple shape. |
| `createView(path, node, nodeRef?, root?)` | Low-level: create a detached sub-view (used internally by `loop`/`test`/`$view`; you rarely call this directly). |
| `hide()` / `show()` / `detach()` / `reattach()` / `remove()` | Visibility/lifecycle for the view and all its sub-views. `remove()` also disconnects all Map watches — always prefer it over ripping DOM nodes out manually. |
### Events emitted by a `View`
`path`, `index`, `remove`, `clear`, `show`, `hide`, `view` (async, bubbles to ancestors when a sub-view is created) — subscribe with the `EventEmitter` mixin's `.on(name, fn, scope)` (see `src/js/core/EventEmitter.js`).
## `Entity`
`MMO.elements.Entity` (`src/js/elements/Entity.js`) is a `Map`-like wrapper specifically for records coming from the framework's API (schema-aware, ids, embedded/parent relationships — `View`'s constructor special-cases `map instanceof Entity && map.isEmbedded()` to resolve the *real* root and basepath through the entity's parent chain). Pass an `Entity` (or plain data) as `RenderView`'s `map` argument when rendering something schema-backed; read `Entity.js` directly for its full API before depending on specifics beyond this.
## `Template` (base class) — for imperative DOM building outside any data binding
If you ever need to build DOM without a reactive `Map` behind it (rare in `.jhs` files, more common in hand-written `.js`), `new MMO.core.Template(node)` gives you the same `.open(tag)/.attr(k,v)/.text(s)/.html(s)/.close(tag?)` chain `View` extends, plus `add()`, `insertBefore/After`, `detach/reattach`, and `setAttribute(node, name, value)` — which is also where the `onxxx` → `addEventListener` and `set-xxx` → property-set conventions are implemented (`Template.setAttribute`), so those two attribute forms work identically whether you're in `.jhs` markup or hand-rolled JS.