LogoLogo
What's NewDeveloper CommunitySupportStatus
  • 🚀Get Started
    • What is JourneyApps Platform?
    • Tutorial: Build your First App
      • 1. Introduction
      • 2. Create a new App
      • 3. OXIDE IDE Overview
      • 4. Hello World app
      • 5. The Data Model
      • 6. View Components
      • 7. Queries and Data Sync
      • 8. Simple Navigation
      • 9. View Stack
      • 10. Input Validation
      • 11. View Parameters
      • 12. Data Manipulation
      • 13. Responsive Apps
      • 14. Styling
      • 15. Lists
      • 16. GPS Capturing
      • 17. Relationships
      • 18. Multiple User Roles
      • 19. Deployment and Users
      • 20. Version Control
      • 21. CSV and APIs
      • 22. Conclusion
    • JourneyApps Platform Fundamentals
      • Creating a New App
        • Git-enabled Apps
      • What are Views?
      • What is the Data Model?
      • JourneyApps Syntax Basics
      • Access the Database (DB)
        • Manipulate DB Objects
        • Query DB Objects
      • View Navigation
        • Deep Linking
      • CloudCode Overview
      • OXIDE (Online IDE)
  • 💻Build your App
    • JourneyApps Syntax
      • Syntax Basics
      • Access the DB
      • View Navigation
      • Async & Await
      • TypeScript Apps (Beta)
        • runtime-build package
        • TypeScript App Troubleshooting
      • What's New in V4
        • Updating to the V4 API
    • Configure your Data Model
      • What is the data model?
      • Reference: model
        • field
        • belongs-to
        • has-many
        • index
      • Data Rules
        • Data Buckets
        • Sync Rules - Limit data synced to devices
        • Data ACLs - Limit access to data
        • Real-world example for Data Rules
        • ❔FAQs
        • Migrate to Data Rules
      • App Indexes
      • Webhooks
    • UI Components
      • All UI Components
        • actionSheet
        • Attachments
        • button
        • button-group
        • capture-coordinates
          • marker
          • marker-query
        • capture-file
        • capture-photo
        • capture-signature
        • card
          • accent
          • action
        • columns
          • column
        • component
        • context-menu
          • divider
          • item
        • CSV
        • date-input
        • datetime-input
        • dialog
          • body
        • display-3d-model
          • 📖display-3d-model Guides
            • Guide 1: Initialize and layout a 3D model in a view
            • Guide 2: Control playback position
            • Guide 3: Troubleshooting controls
        • display-coordinates
        • display-file
        • display-image
        • display-photo
        • display-signature
        • heading
        • html
          • HTML Advanced Topics
          • ❔HTML FAQs
          • 📖Guide: HTML & JourneyApps iFrame Client
        • icons
        • info
        • info-table
          • row
        • journey.photos (capture multiple photos)
        • JourneyPrinter (print PDFs)
        • grid
          • cell
          • 📖grid Examples
        • list
          • list-item
            • accent
            • asset
            • pills
              • pill
            • action
        • multiple-choice-checklist
        • navigation (Navigation drawer)
          • general-section
            • item
          • section
            • item
              • item
          • ❔navigation FAQs
        • notification
        • object-dropdown
        • object-list
          • action
        • object-repeat
        • object-table
          • action
          • column
            • action
            • edit-boolean
            • edit-date
            • edit-datetime
            • edit-integer
            • edit-number
            • edit-select
            • edit-text
            • edit-time
            • edit-typeahead
              • action
            • header-action
          • column-group
          • empty-action
          • 📖object-table Guides
            • Actions
            • Cell callouts
            • Column groups
            • Columns
            • Controlled object-table
            • Controls
            • Copy & paste data
            • Edit cells
            • Filters
            • Frozen columns
            • Fullscreen object-table
            • Mode
            • State
            • Styles
        • optionList
        • PhotonSync (transfer data offline)
        • power-bi
          • 📖Guide: PowerBI Embedding
        • scan-barcode
        • shortcut
        • sidebar
        • single-choice-dropdown
        • single-choice-radio
        • template
        • text-input
        • time-input
        • toggle
        • view
      • JS/TS Events
      • Show / Hide UI Components
      • View Templates
      • XML Fields (Attributes)
        • align-content
        • align-controls
        • align-label
        • bind
        • clear-button-visibility
        • control-order
        • disabled
        • error-message
        • icon-position
        • id
        • hide-if
        • modifier-text
        • label
        • label-case
        • label-color
        • on-change
        • on-press
        • placeholder
        • required
        • show-if
    • JS / TS APIs
      • Attachment
      • Bluetooth (Beta)
      • Broadcast
      • component
      • CSV
      • DB
      • HardwareBarcode
      • journey
        • journey.config
        • journey.container
        • journey.device
        • journey.diagnostics
        • journey.dialog
        • journey.files
        • journey.hardware
        • journey.photos
        • journey.runtime
        • journey.sensors
        • journey.viewStack
      • JourneyPrinter
      • KeyboardBarcode
      • LocalDB
      • NFC
      • OnlineDB
      • PhotonSync
      • SerialPort
      • ShortcutManager
      • TCPSocket
      • user
    • Extend your App with Custom Code
      • App packages
        • App packages overview
        • PDF report package
        • TypeScript library & unit tests
        • Manage External Dependencies
      • Custom HTML
    • Style & Customize your App
      • Style & configure UI components
        • Overview
        • Understand extendable themes
        • Use themes on a view
        • Theme specific components on a view
        • Examples
        • Debugging
        • ❔FAQs
      • Change your App Font
      • Custom Branding
        • Custom Container Features
        • Special Requirements for iOS Containers
    • Integrate your App
      • Backend integrations with CloudCode
      • Barcode Scanning
        • Barcode Scanning using Keyboard Emulation
        • Hardware Barcode Scanning
        • scan-barcode
      • Bluetooth Low Energy (BLE)
      • Broadcast API
      • HTTP requests (Fetch API)
      • JourneyApps Print (Android)
      • Maps and navigation
      • NFC
      • Opening external links/apps
      • Serial Port
      • TCP Sockets
    • Design Intuitive Apps
      • UX Guidelines
      • Write Effective Copy
  • 📱App Features
    • RealWear® Voice Control
      • Automatic Voice Commands
        • Automatic Voice Commands - Advanced
      • Manual Voice Commands
    • App, Runtime and Container Updates
    • Batch Operations (App)
    • Call JS/TS Functions from XML
    • Capture GPS Locations
    • Push Notifications
    • Translations
    • XML Format Strings
    • Webhooks (External)
  • 🌐CloudCode
    • CloudCode Overview
    • Trigger a CloudCode Task
      • Trigger CC with a Schedule
      • Trigger CC via a Webhook
      • Trigger CC from an App
      • Trigger CC from Another Task
      • Trigger CC via HTTP
    • Attachments in CloudCode
    • Timezones
    • Advanced CloudCode Topics
      • Access Multiple DBs in CloudCode Tasks
      • Batch API (CloudCode)
      • CloudCode Dependencies
      • Configure HTTPS in CloudCode
      • Deployment environment variables
      • Local CloudCode Development
      • PDF Reports using CloudCode
      • Shared CloudCode Tasks
      • Translations in CloudCode
  • 📥Backend API
    • Introduction
    • API Reference
      • Retrieve All Objects
      • Query Objects
      • Sort Results
      • Limit and Skip
      • Count Objects
      • Create a New Object
      • Retrieve a Single Object
      • Update a Single Object
      • Delete a Single Object
      • Batch Operations (v4 API)
      • Oplog API
      • Retrieve the App Data Model
      • Manage App Users and Sessions
      • Field Representation
      • Error Responses
    • API Limits
    • Update to the V4 API
  • ⚙️Technical
    • Data Synchronization Priority
    • Device Diagnostics
    • JSON1 Query Engine
    • Improve App Performance
    • Security Measures
    • Supported Platforms
      • Web Container
      • Windows Installer
    • Domain Whitelist
  • 🖥️OXIDE
    • Get started with OXIDE
      • OXIDE Overview
      • Components of OXIDE
    • Configure Testing Deployments
    • Edit and Manage Files
      • How to Navigate to a Function
      • Manage External Dependencies
    • Create and Manage App Containers
    • Debugging & Troubleshooting
      • Common Troubleshooting Pointers
      • App Diagnostics Reports
      • Build Logs
    • OXIDE Workspaces
      • OXIDE Trees
  • ❕Deprecated Features
    • Deprecated Features and Components
Powered by GitBook
On this page
  • Overview
  • Limitations
  • Usage & Syntax
  • 1) Define a view template
  • 2) Reference the template in a view
  • Use Cases
  • Functions
  • App Modules (TypeScript)
  • Nested templates
  • Complete Example
  • Architecture
  1. Build your App
  2. UI Components

View Templates

PreviousShow / Hide UI ComponentsNextXML Fields (Attributes)

Last updated 5 months ago

Version compatibility

View templates were introduced in version 4.90.0 of the JourneyApps Runtime.

In TypeScript apps, the minimum runtime-build version required is 2.4.7. See the docs on how to configure this.

Overview

View templates allow developers to define view XML as a standalone template, and reference it across views. What this means for developers:

  • Less code duplication across views.

  • Easier to maintain complex views, by breaking up the view XML into smaller snippets in templates.

  • Can introduce a better separation of concerns - a set of view components can be grouped into distinct templates.

View templates are supported in both JavaScript and TypeScript apps.

Limitations

  • Limited validation and auto-complete in OXIDE.

Usage & Syntax

Using view templates involves two things:

  1. Defining the template -> This is done using template definitions (template.xml files) for your app.

  2. Referencing it in a view -> This is done using the template UI component on a view.

Let's dive into these:

1) Define a view template

When working with view templates we recommend opening (and docking) the View Templates panel in OXIDE:

Here you can create new templates, view and select existing templates.

You can create multiple template files (e.g. demo.template.xml), and each can contain multiple template definitions (<template-def />). A template file is simply a way to group similar template definitions.

A template file:

<?xml version="1.0" encoding="UTF-8"?>
<templates>
    <template-def name="dialog">

    </template-def>
</templates>

Inside template-def, you can define UI components in the similar to how you’d define them in views. You can pass parameters from your views into the template definition, including objects and functions.

<!-- buttons.template.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<templates>
    <template-def name="save-back-buttons">
        <param name="user" type="user" />
        <param name="onBack" type="function" />
        <param name="onSave" type="function" />
        
        <info>{user.name}</info>
        <button-group>
            <button label="Back" icon="fa-arrow-circle-left" on-press="$:onBack" style="outline" validate="false" />
            <button label="Save" icon="fa-check-circle" on-press="$:onSave" validate="true" />
        </button-group>
    </template-def>

    <template-def name="">
        ...
    </template-def>
</templates>

2) Reference the template in a view

In your view XML, reference the view template using the template UI component and pass the parameters you defined:

<!-- main.view.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<view title="buttons">
    <var name="user" type="user" />

    <!-- Provide the name of the template definition in the name="" attribute. -->
    <!-- Pass parameters from your view to the template using the parameters you defined in the template definition. -->
    <template name="save-back-buttons" user="user" onBack="$:goBack()" onSave="$:save()" />
</view>
// main.ts
async function init() {
    view.user = await DB.user.first();
}
function goBack() {
    notification.success("Back was pressed");
}
function save() {
    notification.success("Save was pressed");
}

Use Cases

Functions

The below example illustrates how a function can be called in a view template and pass parameters to the view.

Template definition:

<!-- dialog.template.xml -->
<!-- Dialog to select an item from an object table -->

<template-def name="select-item-template">
    <param name="items" type="array:line_item" />
    <param name="selectItem" type="function">
        <!-- Define function arguments and type -->
        <arg name="selectedItem" type="line_item"/>
    </param>
        
    <dialog id="select-item-dialog" title="Select an item" auto-hide="true">
        <body>
            <object-table query="items" label="Items" empty-message="Your items will appear here">
                <column heading="Name">{name}</column>
                <column heading="Code">{product_code}</column>
                <action on-press="$:selectItem($object)" />
            </object-table>
        </body>
    </dialog>        
</template-def>

View XML:

<!-- main.view.xml -->
<!-- Reference the template -->
<template name="select-item-template" 
    items="items" 
    selectItem="$:selectItem(selectedItem)" />

View TS:

// main.ts
function selectItem(selectedItem) {
    view.selected_item = selectedItem;
    component.dialog({ id: 'select-item-dialog' }).hide(); 
}

App Modules (TypeScript)

App Modules can be referenced from template definitions directly.

<!-- buttons.template.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<templates>
    <template-def name="save-back-buttons">
        <import module="*" as="shared" from="~/lib/mylib"/> 
        
        <param name="user" type="user" />
        
        <info>{user.name}</info>
        <button-group>
            <button label="Back" icon="fa-arrow-circle-left" on-press="$:shared.onBack()" style="outline" validate="false" />
            <button label="Save" icon="fa-check-circle" on-press="$:shared.onSave()" validate="true" />
        </button-group>
    </template-def>
</templates>

Warning

Your app will fail to deploy with a duplicate identifier error if you use the same identifier for an App Module in a view and in a template that is referenced in the view. Ensure that you do not import Modules that are already imported in the underlying view.

Nested templates

You can nest templates by referencing one in a template definition as follows:

<!-- nested-demo.template.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<templates>
  <template-def name="job-dialog">
        <param name="job" type="job" />
        <param name="editMode" type="boolean" />
        <param name="mainButtonLabel" type="text" />
        <param name="onSave" type="function" />
        <param name="onCancel" type="function" />
        <dialog id="job-details-dialog" auto-hide="true">
            <body>
                <toggle bind="editMode" label="Edit" required="false" />
                <columns>
                    <column hide-if="editMode">
                        <heading>Edit job</heading>
                        <template name="edit-job" />
                    </column>
                    <column show-if="editMode">
                        <heading>Job details</heading>
                        <template name="job-details" job="job" />
                    </column>
                </columns>
            </body>
            <button-group>
                <button label="Cancel" show-if="editMode" on-press="$:onCancel" style="outline" />
                <button label="{mainButtonLabel}" on-press="$:onSave" />
            </button-group>
        </dialog>
    </template-def>
    <template-def name="job-details">
        <param name="job" type="job" />
        <info-table>
            <row label="Job number" value="#{job.number}" />
            <row label="Description" value="{job.description}" />
            <row label="Completed" value="{job.completed}" />
        </info-table>
    </template-def>
    <template-def name="edit-job">
        <param name="job" type="job" />
        
        <info>Job number: #{job.number}</info>
        <text-input bind="job.description" label="Description" required="false" />
        <toggle label="Completed" bind="job.description" mode="checkbox" required="false" />
  </template-def>
</templates>

Complete Example

The below shows a composable dialog as a view template and demonstrates:

  • Passing objects or functions as parameters to the template.

  • Using a function expression (inline function)

  • Using an app module in a view template.


<!-- dialog.template.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<templates>
    <template-def name="create-edit-item-template">
        <import module="*" as="shared" from="~/lib/index"/> 

        <param name="lineItem" type="line_item" />
        <param name="onCancel" type="function" />
        <param name="onSave" type="function" />
        
        <!-- Dialog that will be used in multiple views -->
        <dialog id="create-edit-item" title="{$:lineItem ? 'Edit' : 'Create'} Item" auto-hide="false"> 
            <body>
                <text-input label="Name" bind="lineItem.name" required="true" />
                <text-input label="Product Code" bind="lineItem.product_code" required="true" />
                <heading />
                <button show-if="lineItem" label="Delete" icon="{$:shared.ICONS.delete}" on-press="$:shared.dialogHelper.delete('add-edit-item')" validate="false" color="negative" style="outline"  /> 
            </body>
            <button-group>
                <button label="Cancel" icon="{$:shared.ICONS.close}" on-press="$:onCancel" validate="false" style="outline" />
                <button label="Save" icon="{$:shared.ICONS.done}" on-press="$:onSave" validate="true" />
            </button-group>
        </dialog>      
    </template-def>
</templates>

The corresponding view XML and TS:

<!-- main.view.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<view title="Dialog Example">
    <var name="line_item" type="line_item" />

    <button label="Show dialog" on-press="$:shared.dialogHelper.open('create-edit-item')" validate="false" />
    <template name="create-edit-item-template" lineItem="line_item" onCancel="$:cancel('create-edit-item')" onSave="$:save('create-edit-item')" /> 
    
    <!-- For reference, here's how the dialog 
    was defined prior to the template -->
    <dialog id="reference" title="{$:view.line_item ? 'Edit' : 'Add'} Item" auto-hide="false">
        <body>
            <text-input label="Name" bind="line_item.name" required="true" />
            <text-input label="Product Code" bind="line_item.product_code" required="true" />
            <heading />
            <button show-if="line_item" label="Delete" icon="{$:shared.ICONS.delete}" on-press="$:shared.dialogHelper.delete()" validate="false" color="negative" style="outline"  />
        </body>
        <button-group>
            <button label="Cancel" icon="{$:shared.ICONS.close}" on-press="$:cancel()" validate="false" style="outline" />
            <button label="Save" icon="{$:shared.ICONS.done}" on-press="$:save()" validate="true" />
        </button-group>
    </dialog>
        
</view>
// main.ts

async function init() {    
    view.line_item = await DB.line_item.first();
}

function cancel(dialogId:string) {
    notification.success("Cancel was pressed");
    shared.dialogHelper.close(dialogId);
}
async function save(dialogId:string) {
    await view.line_item.save();
    notification.success("Save was pressed");
    shared.dialogHelper.close(dialogId);
}

Architecture

It is important to note that a view template, unlike other UI components, does not evaluate expressions, but instead replaces templated components and attributes with the provided values.

Let us use the following example of a template with function param:

<!-- example.view.xml: -->
<view title="Example">  
  <template name="test" sayHello="$:alert(msg, theUser)" />
</view>

Attribute sayHello is $:alert(msg, theUser) - which is a function call.

// example.ts
function alert(msg: string, theUser: DB.user, source?: string) {
    notification.info(msg + ' ' + theUser.name);
}
<!-- example.template.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<templates>
   <template-def name="example">
     <param name="sayHello" type="function">       
        <arg name="msg" type="text" />
        <arg name="theUser" type="user" />
     </param>
   
     <button label="Greet" on-press="$:sayHello('Hello', user)" />
   </template-def>
</templates>

The template-def declares a function parameter with two arguments, msg and theUser.

When this template-def is compiled for the particular view (this happens during a deploy, not at runtime), the expression is broken up into parts and the view's template component is replaced with the components inside the template-def. Function definitions are replaced as follows:

This results in the button’s attribute on-press="$:alert('Hello', user)"

Also note that one could still pass view specific arguments, for example <template name="test" sayHello="$:alert(msg, theUser, 'main')"/> and only the two matching arg values will be replaced, resulting in on-press="$:alert('Hello', user, 'main')"

💻
here