# View Navigation

## Definitions

The following terms are used throughout this page and are defined as:

### Action

A navigational function that will navigate the user from the current view to another.

Actions include:

* `link`
* `dismiss` (and `discard` - a special case)
* `replace`
* `clear`

Navigational actions are namespaced to `navigate.<action>()`.

{% hint style="info" %}
**Version compatibility**

* The `navigate` namespace and its primary navigation actions were introduced in version **4.50** of the JourneyApps Container.
* `navigate.getDeeplink` was introduced in version **4.89.0** of the JourneyApps Runtime. See more details about deep linking to a view [here](https://docs.journeyapps.com/reference/get-started/journeyapps-fundamentals/view-navigation/deep-linking).
  {% endhint %}

### Navigate

Transitioning from one view to another, modifying the view stack. *Forward* navigation adds a view to the view stack. *Backward* navigation navigates back to a view that is already on the view stack, while removing all views in between.

### Top level view

A view that is not dependent on other views, i.e. it can be navigated to from anywhere in the app, and is typically used as the starting point of a workflow.

### View stack

A virtual representation of the navigation history, with the current view at the top of the stack.

Accessed in JS using `journey.viewStack`. Please refer to the [viewStack reference](https://docs.journeyapps.com/reference/build/js-ts-apis/journey/viewstack) for more details.

### View path

A unique path representing a view (which you specify when you create a view) used to navigate to the particular view.

Accessed from JS using `view.path`.

{% hint style="info" %}
The **view path** is especially useful for functions in SharedJS or if you are logging usage of the app to an analytics service.
{% endhint %}

### View parameters

Some views may expect data to be passed when navigating to them. These can be objects or other data types and are are specified in a view’s XML. Any view variable or JS variable may be passed as parameters (referred to as `params`).

For example, the view `new_user` requires two parameters (`group` and `name`) to passed to it:

```xml
<view title="New User">
   <param name="group" type="group"/>
   <param name="name" type="text"/>
   ...
</view>
```

Using object parameters simplify apps where there are multiple steps that work with the same object. For example, if the `group` object above is used or modified in multiple successive steps as part of a linear flow, you should specify it as a parameter for those views.

An example of a valid link to reach this view would be `navigate.link('new_user', view.group, view.user_name)`, refer to [Linking to a view](#linking-to-a-view) for more detail.

### View parameter attributes

#### **`required`**

{% hint style="info" %}
**Version compatibility**

The `required` attribute was introduced in version **4.89.0** of the JourneyApps Runtime.
{% endhint %}

* **Optional**
* **Type:** `boolean`
* **Default**: `true`

<pre class="language-xml" data-title="main.view.xml"><code class="lang-xml"><strong>&#x3C;param name="job" type="job" required="false"/>
</strong><strong>
</strong></code></pre>

Specify whether a view parameter requires a value or not. If this attribute is set to `false`, the parameter does not require a value when navigating to the view (in-app or when using deep links). **Important**: this is only the case when there are no subsequent parameters in the link. For this reason, it is recommended to list required parameters first in a view, and optional parameters last.

**Example**

Given the following view:

{% code title="job\_details" %}

```xml
<param name="job" type="job" required="true" />
<param name="site" type="site" required="true" />
<param name="current_date" type="date" required="false" />
```

{% endcode %}

The following links are valid:

{% code title="main.ts" %}

```typescript
navigate.link('job_details', job, site);
navigate.link('job_details', job, site, new Date);
```

{% endcode %}

If the order of the view parameters were as follows:

{% code title="job\_details" %}

```xml
<param name="job" type="job" required="true" />
<param name="current_date" type="date" required="false" />
<param name="site" type="site" required="true" />
```

{% endcode %}

Links need to include `null` for empty optional view parameters:

{% code title="main.ts" %}

```typescript
navigate.link('job_details', job, null, site);
navigate.link('job_details', job, new Date, site);
```

{% endcode %}

#### **`transform-value`**

{% hint style="info" %}
**Version compatibility**

The `transform-value` attribute was introduced in version **4.89.0** of the JourneyApps Runtime.
{% endhint %}

This attribute takes a function and passes an `$event` object.&#x20;

The `$event` object has the following structure:

```typescript
"$event": {
    value: DB.object | string | null,
    param: {
        name: string // -> 'current_job'
        type: string // -> 'job'
        required: boolean // -> true
    }
}
```

Example:

{% code title="main.view\.xml" %}

```xml
<param name="job" type="job" transform-value="$:loadJob($event)" />
<param name="compeletion_date" type="date" transform-value="$:getDate($event)" />
```

{% endcode %}

{% code title="main.js" %}

```javascript
function loadJob(event) {
    // event.value => ''
    var job = DB.job.first(event.value);    
    return job;
}

function getDate(event) {
    // event.value => '1681822674806' | '1991/01/01' | null
    return new Date(event.value);
}
```

{% endcode %}

* This function takes some input and outputs a value based on that input. It is not like any other JS/TS function; for example, `navigate` does not work within `transform-value`. Its sole purpose is to return a value to a view parameter.
* This function is useful for setting a parameter where the event object needs to be transformed or retrieved from the DB from an ID. It is also useful to provide a fallback value (e.g. when a parameter is `null`).
* The function always runs before `init()` . This ensures that anything that depends on the view parameter in `init()` can seamlessly execute.
* It is executed every time before the view is added onto viewStack including:

  * In-app navigation using `navigate()`&#x20;
  * [Deep linking](https://docs.journeyapps.com/reference/get-started/journeyapps-fundamentals/view-navigation/deep-linking)

  **Note**: The function does not execute when `resume` the flow happens (since in this case the view is already on the viewStack).
* When an exception gets thrown in this function, the view cannot successfully load and the user will be taken to the main view.

## Example use case

Here is an example use case to aid in explaining view navigation.

![](https://2865107717-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9TCHLR67eLHBOjPvHhud%2Fuploads%2Fgit-blob-e3aba3997fd2df34fd1634058de0ab7bf6fe61dc%2Fview-navigation-graph.png?alt=media)

The above diagram represents the navigational flow of a fictitious app.

It consists of a Login page, from which the user navigates to individual landing views, according to some criteria (typically based on a `role`).

From a landing page, the user can navigate to any of the top level views and back, in any order. This is done using the `replace` action, only replacing the current view and not adding to the view stack. See [Replacing a view](#replacing-a-view).

When the user starts a linear flow from Top level 1, `link` is used. Navigating between steps adds to the view stack and allows the user to navigate forward and backward. (See [Linking to a view](#linking-to-a-view) and [Dismissing a view](#dismissing-a-view)).

When the user performs non-linear tasks, `link` from Top level 2 to Task 1 is used. Thereafter `replace` is used, allowing the user to jump between different tasks in the process. See [Replacing a view](#replacing-a-view).

The diagram also illustrates how `clear` is used, allowing the user to ‘log out’ and navigate to the login screen, clearing the views on the stack. See [Clear and navigate](#clear-and-navigate).

## Linking to a view

The `link` action performs forward navigation (→) and is typically used with linear workflows.

For example: A → B → C will result in a view stack of `[A, B, C]` with C being the active view.

### Syntax

`navigate.link(path, param1, param2, ...)` — link to the specified view path and pass `params`.

| Option               | Required   | Details                                               |
| -------------------- | ---------- | ----------------------------------------------------- |
| `path`               | *Required* | String that represents a view path                    |
| `params1, param2, …` | *Optional* | Objects or other data passed to the view navigated to |

### Examples:

Using `navigate.link` in xml:

```xml
<view title="Main Menu">
    <button label="New User" on-press="$:navigate.link('new_user')" />
</view>
```

Notice the use of `$:`, this will evaluate the function in the view context.

Using `navigate.link` with `params` in JS

```javascript
function editUser(currentUser){
    navigate.link('edit_user', currentUser);
}
```

### link()

{% hint style="warning" %}
**Deprecation warning**

The `link()` function might be deprecated in the near future.
{% endhint %}

`link.view_path()` — link to the specified view path `link.view_path.with.slash(params)` — link to a view path containing `/` (the slashes are replaced with a `.`)

Using `link` in JS:

```javascript
function createUser() {
    // Include other logic here
    link.new_user();
}
```

## Dismissing a view

The `dismiss` action performs backward navigation (←).

Calling `dismiss` on a view (e.g. on a button press) will save and close that view and navigate to the previous view on the stack. Given a view stack of `[A, B, C]` `dismiss` ← will result in a view stack of `[A, B]`.

To close the view without saving variables and view state, chain `discard`.

Closing multiple views, for instance to return to a top level view, use the `dismiss` action specifying a view path, `navigate.dismiss('view_path')`.

### Syntax

`navigate.dismiss()` — dismiss the active view&#x20;

`navigate.dismiss(path)` — dismiss to the specified view path

`navigate.discard().dismiss(path)` — dismiss to the specified view path without saving

| Option | Required   | Details                                                                                                                            |
| ------ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `path` | *Optional* | String that represents a view path. If no `path` is provided, the active view will be dismissed to the previous view on the stack. |

Note: `dismiss` and `discard` does not take view parameters.

### Examples:

Using `navigate.dismiss` to a view path in xml:

```xml
<button on-press="$:navigate.dismiss('main')" label="Main Menu" />
```

This will automatically save all objects defined in the view.

Using `navigate.discard().dismiss` in xml:

```xml
<button on-press="$:navigate.discard().dismiss('main')" label="Main Menu" />
```

### dismiss()

{% hint style="warning" %}
**Deprecation warning**

The `dismiss()` function might be deprecated in the near future.
{% endhint %}

`dismiss()`— dismiss the active view&#x20;

`discard()` — dismiss the active view without saving&#x20;

`discard.view_path()` — dismiss to the specified view path without saving

**Examples**:

Using `dismiss()` in xml:

```xml
<button on-press="dismiss()" label="Save" />
```

This will automatically save all objects defined in the view.

Using `discard()` in xml:

```xml
<button on-press="discard()" label="Cancel" />
```

## Replacing a view

Instead of adding a view to the view stack, the `replace` action (denoted by ⇌) replaces the current view and navigates to a new view.

For example: A → B ⇌ C will result in `[A, C]` on the view stack. A → B ⇌ C ⇌ D ⇌ B will result in `[A, B]` on the view stack.

The `replace` view action is typically used for non-linear workflows, but is also a powerful tool to build apps that consists of various top level views.

{% hint style="info" %}
`replace` calls the target view’s `init()` function, instead of the `resume()` callback when navigating to it. You can read more about the resume callback in a section below.
{% endhint %}

### Syntax

`navigate.replace(path, param1, param2, ...)` — replace with the specified view path passing `params`.

`navigate.discard().replace(path, param1, param2, ...)` — discard unsaved changes and replace with the specified view path, passing `params`.

| Option              | Required   | Details                                               |
| ------------------- | ---------- | ----------------------------------------------------- |
| `path`              | *Required* | String that represents a view path                    |
| `param1, param2, …` | *Optional* | Objects or other data passed to the view navigated to |

### Examples:

Using `navigate.replace` in xml:

```xml
<button on-press="$:navigate.replace('tasks/task_1')" label="Complete Task" />
```

Using `navigate.replace` in JS:

```javascript
function toMyDashboard() {
    // Other logic here
    navigate.replace('dashboards/tech');
}
```

Using `navigate.replace` with `params`:

```xml
<button on-press="$:navigate.replace('tasks/task_2', view.results)" label="Complete Task" />
```

The `replace` action reduces the need to manage the view stack manually and makes for clearer navigation within your app.

## Clear and navigate

The `clear` navigation action (denoted by ⇒) will remove all views currently on the view stack and navigate to the target view.

For example:

* Normal linking A → B → C → D will have `[A, B, C, D]` on the view stack Performing ⇒ E will result in a view stack of `[E]`
* Also when ⇒’ing to a view already on the stack, e.g. A → B → C → D ⇒ B will result in `[B]`

The `clear` action is typically used when navigating away from a point in the app, where the navigation history is not needed anymore, for instance when a user logs out.

### Syntax

`navigate.clear(path, param1, param2, ...)` — clear and navigate to the specified view path, passing `params`.

`navigate.discard().clear('view_path', param1, param2, ...)` — discard unsaved changes, clear and navigate to the specified view path, passing `params`.

| Option              | Required   | Details                                               |
| ------------------- | ---------- | ----------------------------------------------------- |
| `path`              | *Required* | String that represents a view path                    |
| `param1, param2, …` | *Optional* | Objects or other data passed to the view navigated to |

### Examples:

Using `navigate.clear` in xml:

```xml
<list>
    <list-item on-press="$:navigate.clear('login')" header="Log out" icon="ion-home"/>
</list>
```

The `clear` action will save view state by default, but can be chained to `discard()` navigate without saving. Using `navigate.clear` in JS, without saving view state:

```javascript
function cancelTasks(){
    // other logic here
    navigate.discard().clear('tech_landing');
}
```

Using `navigate.clear` with `params` in xml:

```xml
<button on-press="$:navigate.clear('dashboard', query_range_str, query_range_end)" label="Dashboard" />
```

## Deep link to a view

Please see the following section for deep linking to a view when opening a container:

{% content-ref url="view-navigation/deep-linking" %}
[deep-linking](https://docs.journeyapps.com/reference/get-started/journeyapps-fundamentals/view-navigation/deep-linking)
{% endcontent-ref %}

## *resume* Callback

A `resume` function may be defined on a view, which is called whenever the user returns to the view from a different view, either via a dismiss function or via the back button.

The resume function receives a single "from" parameter, with the following properties:

* `path`: the path of view that the user returned from
* `dismissed`: true if the `dismiss()` function or a dismiss link was used to return
* `back`: true if the user used the back button to return

### An example:

```javascript
function resume(from) {
    if(from.dismissed && from.path == 'user/new') {
        dialog("New User", "A new user has been created");
    }
}
```

Resume functions may also chain further links. However, it is recommended to use a dismiss link instead.

## Overriding the built-in back button

By default, the built-in back button has the same behavior as the `discard()` function.

This can be changed by specifying an [`on-back`](https://docs.journeyapps.com/reference/build/ui-components/all-ui-components/view#on-back) handler on the view. If the function triggers navigation or returns `false`, the default back behavior is suppressed. Otherwise, the default `discard()` is performed after the handler has completed.

### Examples

```xml
<!-- Example 1: Save data when back is pressed. -->
<view title="Example" on-back="$:navigate.dismiss()">
    <!-- ... -->
</view>
```

```xml
<!-- Example 2: Confirm when going back. -->
<view title="Example" on-back="$:confirmBack()">
    <!-- ... -->
</view>
```

```javascript
function confirmBack() {
    var confirmed = confirmDialog('Back', 'You will lose unsaved changes. Are you sure?', 'Yes', 'No').

    return confirmed;
}
```
