Offline remote data handling

Dealing with offline remote data is fundamental to ensuring data synchronization and consistency between the mobile app and the remote data source, allowing users to continue using the app and performing actions without interruption. Queue operations provide the functionality needed when the device regains network connectivity and manages a sequence of elements in a specific order. The commands in the queue can be manipulated to reduce the number of calls to the remote data store.

What happens to data when you are offline?

  1. Data Capture Offline:

    • When the mobile app is offline, any actions that require remote data interaction, such as submitting a form, uploading a file, or syncing data, are queued.

    • Each action is captured and stored in a queue in the local data provider on the device.

  2. Network Monitoring:

    • The app monitors the network status. It triggers the dequeuing process when it detects that the device has regained connectivity.

  3. Dequeue and Sync:

    • The app starts processing the commands in the queue. It dequeues each command and attempts to send it to the remote server.

    • If the operation is successful, the app removes the command from the queue.

How to configure the queue

In the execute-entity, execute-entities , and submit-form actions, the queueOperation property is configured to determine how the record must be handled in the queue when the device is offline. There are two configuration options:

Property
description

replace

All queued commands for the specified record and method type are replaced with the current action. For example, a record is created and then updated a few times. Using replace for the update method results in only two commands in the queue for the record: create and update. If replace is not used, there will be a command for every action: create, update, update, update. Replace is recommended for backend systems with rate limits. The queueOperation: replace requires an id (must be in lowercase). This can either be configured in the: Parameter of the execute-entity action with an id configured in the parameters of the function file. data property in the execute-entity action.

add

All commands are added to the queue. When the queueOperation property is omitted, the default is add.

actions:
  - children:
      - type: action.execute-entity
        options:
          title: Update Customer
          provider: DATA_PROVIDER_REST
          entity: customers
          method: update
          # Use replace to ensure you only have one update on the queue related
          # to a record. Not adding the replace will not break the solution but
          # will help to avoid chattiness and scenarios where backends have 
          # rate limits.
          queueOperation: replace
          function: rest-update-customer
          # Parameters to update in the remote server.
          parameters:
            # id is a required property for the replace queue
            id: [email protected]
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)
          # Data records to update in the local SQLite table. 
          data: 
            id: [email protected]
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)

Examples of configuring the required id property when using queueOperation: replace.

# This action is configured with a replace queue operation and the id is 
# specified, in the data property. The id comes from an input.
actions:
  - children:
      - type: action.execute-entity
        options:
          title: Update Customer
          provider: DATA_PROVIDER_REST
          entity: customers
          method: update
          function: rest-update-customer
          # Replace current update on the queue.
          queueOperation: replace
          # id is required for the replace operation.
          parameters:
            id: [email protected] 
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            customerType: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)
          # id is required for the replace operation.
          data:
            id: [email protected]  
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            customerType: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)

How to clear the queue

For scenarios where operations on a record must be treated as draft and all queued commands must be removed without impacting the local record, use the clear-queue action, and specify the id of the record and a title for the action. See clear all commands in the queue example.

clear-queue-action
actions:
  - children:
      - type: action.clear-queue
        options:
          title: Remove record from Queue
          id: [email protected]

Queue handling for delete methods

When using the replace property with a delete method, all commands on the queue for the specified record are removed. The delete method will still delete the local entity record as expected, for example while offline a record is created with a tempId, then updated, and then deleted with a queueOperations: replace, the commands for that record are removed from the queue and local entity will also be deleted. This avoids the need for the full cycle of calls to be sent to the backend (create, update, delete) if the end result is that the record is deleted.

If the record to be delete has a valid Id then the queueOperations: add is used to add the record to the queue, when the device is back online the queue is processed and the record is deleted using the function.

If you want to cater for both tempId and a valid Id records when offline in one queueOperation configuration use the following expression =$isTempId(@ctx.current.item.id) ? replace:add

execute-entity-delete
actions:
  - children:
      - type: action.execute-entity
        options:
          title: Delete Record
          provider: DATA_PROVIDER_REST
          entity: customers
          method: delete
          # For delete use an expression to evaluate if there is a valid or
          # tempId. If the record has a tempId it will remove all operations 
          # related to the record from the queue. If it is a valid Id the record
          # will use the add and place it on the queue, which will delete the 
          # record from the remote data store using the function.
          queueOperation: =$isTempId(@ctx.current.item.id) ? replace:add
          function: rest-delete-customer
          parameters:
            custId: =$number(@ctx.current.item.id)
          data:
            id: [email protected]

Handling TempIds

All tempIds for a record are replaced in all other queued commands if a valid id is returned. If you use a record's id in another record while offline and a valid id is returned back when the device is back online, Jigx updates the tempId used in all the other records that used it with the valid id. This makes for smoother integration with backend systems as the ids will match up. See working with REST ids for more information on returning the id.

Examples and code snippets

Execute-entity with queueOperation (replace)

In this example, when the device is offline and a customer record is created and then updated multiple times, only one create and one update command is queued. When the device is back online the queue is cleared. The remote data store returns an id that we can use to map back to the record locally in the outputTransform of the function (rest-create-customer). queueOperation is not required for the create of the customer because once the device comes online, the record will be created, and the id from the remote data store will be returned and any records with the same tempId will be updated with the returning id and will update the correct record. The queueOperation: replace is rather used in the update-customer jig.

title: New Customer
type: jig.default

header:
  type: component.jig-header
  options:
    height: small
    children:
      type: component.image
      options:
        source:
          uri: https://www.dropbox.com/scl/fi/ha9zh6wnixblrbubrfg3e/business-5475661_640.jpg?rlkey=anemjh5c9qsspvzt5ri0i9hva&raw=1

onFocus:
  type: action.reset-state
  options:
    state: [email protected]

datasources:
  region:
    type: datasource.sqlite
    options:
      provider: DATA_PROVIDER_LOCAL
      entities:
        - entity: us-states
      query: |
        SELECT 
          uss.id AS id, 
          json_extract(uss.data, '$.state') AS state, 
          json_extract(uss.data, '$.abbreviation') AS abbreviation,
          json_extract(uss.data, '$.stateCapital') AS stateCapital,
          json_extract(uss.data, '$.region') AS region,
          json_extract(uss.data, '$.flag') AS flag
        FROM 
          [us-states] AS uss
        WHERE  
          json_extract(uss.data, '$.abbreviation') = @selectedState

      queryParameters:
        selectedState: [email protected]

  customerType:
    type: datasource.static
    options:
      data:
        - id: 1
          type: New
          value: new
        - id: 2
          type: Gold
          value: Gold
        - id: 3
          type: Silver
          value: Silver
children:
  - type: component.form
    instanceId: customerForm
    options:
      isDiscardChangesAlertEnabled: false
      children:
        - type: component.text-field
          instanceId: companyName
          options:
            label: Company Name
        - type: component.field-row
          options:
            children:
              - type: component.text-field
                instanceId: firstName
                options:
                  label: First Name
              - type: component.text-field
                instanceId: lastName
                options:
                  label: Last Name
        - type: component.text-field
          instanceId: jobTitle
          options:
            label: Job Title
        - type: component.text-field
          instanceId: email
          options:
            label: Email
        - type: component.text-field
          instanceId: phone1
          options:
            label: Mobile
        - type: component.text-field
          instanceId: web
          options:
            label: Web
        - type: component.text-field
          instanceId: address
          options:
            label: Street
        - type: component.text-field
          instanceId: city
          options:
            label: City
        - type: component.field-row
          options:
            children:
              - type: component.dropdown
                instanceId: usState
                options:
                  label: State
                  data: [email protected]
                  item:
                    type: component.dropdown-item
                    options:
                      title: [email protected]
                      value: [email protected]
                      leftElement:
                        element: avatar
                        text: [email protected]
                        uri: [email protected]
              - type: component.text-field
                instanceId: zip
                options:
                  label: ZIP
        - type: component.field-row
          options:
            children:
              - type: component.text-field
                instanceId: region
                options:
                  label: Region
                  value: [email protected]
              - type: component.dropdown
                instanceId: customerType
                options:
                  label: Customer Type
                  data: [email protected]
                  item:
                    type: component.dropdown-item
                    options:
                      title: [email protected]
                      value: [email protected]

actions:
  - children:
      - type: action.execute-entity
        options:
          title: Create Customer
          provider: DATA_PROVIDER_REST
          entity: customers
          method: create
          # In this scenario, the backend system returns an Id that we can use
          # to map back to the record locally in the Output transform of the 
          # function (rest-create-customer). You don’t need to use
          # queueOperation in this scenario. Once the device goes online,
          # the record will be created, and the id from the backend will come
          # back. Any records with the same tempId will be updated with
          # the returning id and will update the correct record.
          function: rest-create-customer
          parameters:
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            customerType: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)
            jobTitle: [email protected]
            phone1: [email protected]
            phone2: [email protected]
            region: [email protected]
            state: [email protected]
            web: =$lowercase(@ctx.components.web.state.value)
            zip: [email protected]
         data:
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            customerType: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)
            jobTitle: [email protected]
            phone1: [email protected]
            phone2: [email protected]
            region: [email protected]
            state: [email protected]
            web: =$lowercase(@ctx.components.web.state.value)
            zip: [email protected]   

Execute-entity with queueOperation (add)

In this example, when the device is offline and a customer record is updated multiple times , all the update commands are queued. When the device is back online the queue is cleared.

title: Update Customer
type: jig.default

header:
  type: component.jig-header
  options:
    height: small
    children:
      type: component.image
      options:
        source:
          uri: https://www.dropbox.com/scl/fi/ha9zh6wnixblrbubrfg3e/business-5475661_640.jpg?rlkey=anemjh5c9qsspvzt5ri0i9hva&raw=1

datasources:
  region:
    type: datasource.static
    options:
      data:
        - id: 1
          region: US Central
        - id: 2
          region: US East
        - id: 3
          region: US West
  customerType:
    type: datasource.static
    options:
      data:
        - id: 1
          type: New
          value:
        - id: 2
          type: Gold
          value: Gold
        - id: 3
          type: Silver
          value: Silver
  customers:
    type: datasource.sqlite
    options:
      provider: DATA_PROVIDER_LOCAL
      entities:
        - entity: customers
      query: |
        SELECT 
          cus.id AS id, 
          json_extract(cus.data, '$.firstName') AS firstName, 
          json_extract(cus.data, '$.lastName') AS lastName,
          json_extract(cus.data, '$.companyName') AS companyName,
          json_extract(cus.data, '$.address') AS address,
          json_extract(cus.data, '$.city') AS city,
          json_extract(cus.data, '$.state') AS state,
          json_extract(cus.data, '$.zip') AS zip,
          json_extract(cus.data, '$.phone1') AS phone1,
          json_extract(cus.data, '$.phone2') AS phone2,
          json_extract(cus.data, '$.email') AS email,
          json_extract(cus.data, '$.web') AS web,
          json_extract(cus.data, '$.customerType') AS customerType,
          json_extract(cus.data, '$.jobTitle') AS jobTitle,
          json_extract(cus.data, '$.region') AS region
        FROM 
          [customers] AS cus
        WHERE id = @custId
      queryParameters:
        custId: [email protected]
      isDocument: true

children:
  - type: component.form
    instanceId: customer
    options:
      isDiscardChangesAlertEnabled: false
      children:
        - type: component.text-field
          instanceId: companyName
          options:
            label: Company Name
            initialValue: [email protected]
        - type: component.field-row
          options:
            children:
              - type: component.text-field
                instanceId: firstName
                options:
                  label: First Name
                  initialValue: [email protected]
              - type: component.text-field
                instanceId: lastName
                options:
                  label: Last Name
                  initialValue: [email protected]
        - type: component.text-field
          instanceId: jobTitle
          options:
            label: Job Title
            initialValue: [email protected]
        - type: component.text-field
          instanceId: email
          options:
            label: Email
            initialValue: [email protected]
        - type: component.text-field
          instanceId: phone1
          options:
            label: Mobile
            initialValue: [email protected]
        - type: component.text-field
          instanceId: web
          options:
            label: Web
            initialValue: [email protected]
        - type: component.text-field
          instanceId: address
          options:
            label: Street
            initialValue: [email protected]
        - type: component.text-field
          instanceId: city
          options:
            label: City
            initialValue: [email protected]
        - type: component.field-row
          options:
            children:
              - type: component.text-field
                instanceId: state
                options:
                  label: State
                  initialValue: [email protected]
              - type: component.text-field
                instanceId: zip
                options:
                  label: ZIP
                  initialValue: [email protected]
        - type: component.field-row
          options:
            children:
              - type: component.dropdown
                instanceId: region
                options:
                  label: Region
                  data: [email protected]
                  initialValue: [email protected]
                  item:
                    type: component.dropdown-item
                    options:
                      title: [email protected]
                      value: [email protected]
              - type: component.dropdown
                instanceId: customerType
                options:
                  label: Customer Type
                  data: [email protected]
                  initialValue: [email protected]
                  item:
                    type: component.dropdown-item
                    options:
                      title: [email protected]
                      value: [email protected]

actions:
  - children:
      - type: action.execute-entity
        options:
          title: Update Customer
          provider: DATA_PROVIDER_REST
          entity: customers
          method: update
          # Use add to queue all the updates related to a record.
          queueOperation: add
          function: rest-update-customer
          parameters:
            id: [email protected]
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            customerType: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)
            jobTitle: [email protected]
            phone1: [email protected]
            phone2: [email protected]
            region: [email protected]
            state: [email protected]
            web: =$lowercase(@ctx.components.web.state.value)
            zip: [email protected]
          data:
            id: [email protected]
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            customerType: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)
            jobTitle: [email protected]
            phone1: [email protected]
            phone2: [email protected]
            region: [email protected]
            state: [email protected]
            web: =$lowercase(@ctx.components.web.state.value)
            zip: [email protected]    

Execute-entity (delete) with queueOperation (replace)

In this example, when the device is offline and a customer record is updated multiple times and then deleted, all the the commands for the record are removed from the queue and local entity is deleted. When the device is back online the queue is cleared.

title: Customers
type: jig.list
icon: list

header:
  type: component.jig-header
  options:
    height: small
    children:
      type: component.image
      options:
        source:
          uri: https://www.dropbox.com/scl/fi/ha9zh6wnixblrbubrfg3e/business-5475661_640.jpg?rlkey=anemjh5c9qsspvzt5ri0i9hva&raw=1

onRefresh:
  type: action.execute-action
  options:
    action: load-customers

datasources:
  customers:
    type: datasource.sqlite
    options:
      provider: DATA_PROVIDER_LOCAL
      entities:
        - entity: customers
      query: |
          SELECT
          cus.id AS id,
          json_extract(cus.data, '$.firstName') AS firstName,
          json_extract(cus.data, '$.lastName') AS lastName,
          json_extract(cus.data, '$.companyName') AS companyName,
          json_extract(cus.data, '$.address') AS address,
          json_extract(cus.data, '$.city') AS city,
          json_extract(cus.data, '$.state') AS state,
          json_extract(cus.data, '$.zip') AS zip,
          json_extract(cus.data, '$.phone1') AS phone1,
          json_extract(cus.data, '$.phone2') AS phone2,
          json_extract(cus.data, '$.email') AS email,
          json_extract(cus.data, '$.web') AS web,
          json_extract(cus.data, '$.customerType') AS customerType,
          json_extract(cus.data, '$.jobTitle') AS jobTitle,
          json_extract(cus.data, '$.logo') AS logo
        FROM
          [customers] AS cus
        -- ORDER BY
        --  json_extract(cus.data, '$.companyName')

data: [email protected]
item:
  type: component.list-item
  options:
    title: [email protected] & ' (' & @ctx.current.item.id & ')'
    subtitle: [email protected] & ' ' & @ctx.current.item.lastName
    leftElement:
      element: avatar
      text: [email protected]
      uri: [email protected]
    label:
      title: |
        =$uppercase((@ctx.current.item.customerType = 'Silver' ? @ctx.current.item.customerType:@ctx.current.item.customerType = 'Gold' ? @ctx.current.item.customerType:''))
      color:
        - when: [email protected] = 'Gold'
          color: color3
        - when: [email protected] = 'Silver'
          color: color14
    onPress:
      type: action.go-to
      options:
        linkTo: update-customer
        parameters:
          customer: [email protected]
    swipeable:
      left:
        - label: DELETE
          icon: delete-2
          color: negative
          onPress:
            type: action.confirm
            options:
              isConfirmedAutomatically: false
              onConfirmed:
                type: action.execute-entity
                options:
                  provider: DATA_PROVIDER_REST
                  entity: customers
                  method: delete
                  # For delete use replace, If the record has tempId it will 
                  # remove all opperations related to the record from the queue
                  queueOperation: replace
                  function: rest-delete-customer
                  parameters:
                    custId: =$number(@ctx.current.item.id)
                  data:
                    id: [email protected]
              modal:
                title: Are you sure?
                description: |
                  =('Press Confirm to permanently delete ' & @ctx.current.item.companyName)

Execute-entity with queueOperations when no id is returned

In this example, the remote data store does not return an id, and we need to sync the data before we get the correct backend id for the record. We need to be careful not to create and update the same record on the queue because the backend cannot associate the records after the sync. To accomodate for this in the update-customer jig we configure two execute-entity actions.

  • The first action checks to see if a record has a tempId by using the following expression when: =$isTempId(@ctx.jig.inputs.customer.id). If the record on the queue has a tempId, we replace it using the create method with a new item that will be placed on the queue.

  • The second action checks to see if the record has a valid Id rather than a tempId by using the following expression when: =$not($isTempId(@ctx.jig.inputs.customer.id)). If the record on the queue has a valid id, we replace it using the update method with an item that will be placed on the queue.

title: New Customer
type: jig.default

header:
  type: component.jig-header
  options:
    height: small
    children:
      type: component.image
      options:
        source:
          uri: https://www.dropbox.com/scl/fi/ha9zh6wnixblrbubrfg3e/business-5475661_640.jpg?rlkey=anemjh5c9qsspvzt5ri0i9hva&raw=1

onFocus:
  type: action.reset-state
  options:
    state: [email protected]

datasources:
  region:
    type: datasource.sqlite
    options:
      provider: DATA_PROVIDER_LOCAL
      entities:
        - entity: us-states
      query: |
        SELECT 
          uss.id AS id, 
          json_extract(uss.data, '$.state') AS state, 
          json_extract(uss.data, '$.abbreviation') AS abbreviation,
          json_extract(uss.data, '$.stateCapital') AS stateCapital,
          json_extract(uss.data, '$.region') AS region,
          json_extract(uss.data, '$.flag') AS flag
        FROM 
          [us-states] AS uss
        WHERE  
          json_extract(uss.data, '$.abbreviation') = @selectedState
      queryParameters:
        selectedState: [email protected]

  customerType:
    type: datasource.static
    options:
      data:
        - id: 1
          type: New
          value: new
        - id: 2
          type: Gold
          value: Gold
        - id: 3
          type: Silver
          value: Silver
children:
  - type: component.form
    instanceId: customerForm
    options:
      isDiscardChangesAlertEnabled: false
      children:
        - type: component.text-field
          instanceId: companyName
          options:
            label: Company Name
        - type: component.field-row
          options:
            children:
              - type: component.text-field
                instanceId: firstName
                options:
                  label: First Name
              - type: component.text-field
                instanceId: lastName
                options:
                  label: Last Name
        - type: component.text-field
          instanceId: jobTitle
          options:
            label: Job Title
        - type: component.text-field
          instanceId: email
          options:
            label: Email
        - type: component.text-field
          instanceId: phone1
          options:
            label: Mobile
        - type: component.text-field
          instanceId: web
          options:
            label: Web
        - type: component.text-field
          instanceId: address
          options:
            label: Street
        - type: component.text-field
          instanceId: city
          options:
            label: City
        - type: component.field-row
          options:
            children:
              - type: component.dropdown
                instanceId: usState
                options:
                  label: State
                  data: [email protected]
                  item:
                    type: component.dropdown-item
                    options:
                      title: [email protected]
                      value: [email protected]
                      leftElement:
                        element: avatar
                        text: [email protected]
                        uri: [email protected]
              - type: component.text-field
                instanceId: zip
                options:
                  label: ZIP
        - type: component.field-row
          options:
            children:
              - type: component.text-field
                instanceId: region
                options:
                  label: Region
                  value: [email protected]
              - type: component.dropdown
                instanceId: customerType
                options:
                  label: Customer Type
                  data: [email protected]
                  item:
                    type: component.dropdown-item
                    options:
                      title: [email protected]
                      value: [email protected]

actions:
  - children:
      - type: action.execute-entity
        options:
          title: Create Customer
          provider: DATA_PROVIDER_REST
          entity: customers
          method: create
          # In this scenario, the backend system does not return an ID, 
          # you need to sync the data before we get the correct backend ID for 
          # the record. With this in mind, you'll need to be careful not to 
          # create and update the same record on the queue because the backend 
          # cannot associate the records after the sync. Have a look at the 
          # Update jig to see the correct way of dealing with this scenario.
          function: rest-create-customer
          parameters:
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            customerType: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)
            jobTitle: [email protected]
            phone1: [email protected]
            phone2: [email protected]
            region: [email protected]
            state: [email protected]
            web: =$lowercase(@ctx.components.web.state.value)
            zip: [email protected]
          data:
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            customerType: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)
            jobTitle: [email protected]
            phone1: [email protected]
            phone2: [email protected]
            region: [email protected]
            state: [email protected]
            web: =$lowercase(@ctx.components.web.state.value)
            zip: [email protected]   

Clear all commands in the queue for record

In this example, a secondary button is added to clear the queue for all commands using the action.clear-queue.

clear-customer-updates.jigx
title: Update Customer
type: jig.default

header:
  type: component.jig-header
  options:
    height: small
    children:
      type: component.image
      options:
        source:
          uri: https://www.dropbox.com/scl/fi/ha9zh6wnixblrbubrfg3e/business-5475661_640.jpg?rlkey=anemjh5c9qsspvzt5ri0i9hva&raw=1

datasources:
  region:
    type: datasource.static
    options:
      data:
        - id: 1
          region: US Central
        - id: 2
          region: US East
        - id: 3
          region: US West
  customerType:
    type: datasource.static
    options:
      data:
        - id: 1
          type: New
          value:
        - id: 2
          type: Gold
          value: Gold
        - id: 3
          type: Silver
          value: Silver
  customers:
    type: datasource.sqlite
    options:
      provider: DATA_PROVIDER_LOCAL
      entities:
        - entity: customers
      query: |
        SELECT 
          cus.id AS id, 
          json_extract(cus.data, '$.firstName') AS firstName, 
          json_extract(cus.data, '$.lastName') AS lastName,
          json_extract(cus.data, '$.companyName') AS companyName,
          json_extract(cus.data, '$.address') AS address,
          json_extract(cus.data, '$.city') AS city,
          json_extract(cus.data, '$.state') AS state,
          json_extract(cus.data, '$.zip') AS zip,
          json_extract(cus.data, '$.phone1') AS phone1,
          json_extract(cus.data, '$.phone2') AS phone2,
          json_extract(cus.data, '$.email') AS email,
          json_extract(cus.data, '$.web') AS web,
          json_extract(cus.data, '$.customerType') AS customerType,
          json_extract(cus.data, '$.jobTitle') AS jobTitle,
          json_extract(cus.data, '$.region') AS region
        FROM 
          [customers] AS cus
        WHERE id = @custId

      queryParameters:
        custId: [email protected]

children:
  - type: component.form
    instanceId: customer
    options:
      isDiscardChangesAlertEnabled: false
      children:
        - type: component.text-field
          instanceId: companyName
          options:
            label: Company Name
            initialValue: [email protected]
        - type: component.field-row
          options:
            children:
              - type: component.text-field
                instanceId: firstName
                options:
                  label: First Name
                  initialValue: [email protected]
              - type: component.text-field
                instanceId: lastName
                options:
                  label: Last Name
                  initialValue: [email protected]
        - type: component.text-field
          instanceId: jobTitle
          options:
            label: Job Title
            initialValue: [email protected]
        - type: component.text-field
          instanceId: email
          options:
            label: Email
            initialValue: [email protected]
        - type: component.text-field
          instanceId: phone1
          options:
            label: Mobile
            initialValue: [email protected]
        - type: component.text-field
          instanceId: web
          options:
            label: Web
            initialValue: [email protected]
        - type: component.text-field
          instanceId: address
          options:
            label: Street
            initialValue: [email protected]
        - type: component.text-field
          instanceId: city
          options:
            label: City
            initialValue: [email protected]
        - type: component.field-row
          options:
            children:
              - type: component.text-field
                instanceId: state
                options:
                  label: State
                  initialValue: [email protected]
              - type: component.text-field
                instanceId: zip
                options:
                  label: ZIP
                  initialValue: [email protected]
        - type: component.field-row
          options:
            children:
              - type: component.dropdown
                instanceId: region
                options:
                  label: Region
                  data: [email protected]
                  initialValue: [email protected]
                  item:
                    type: component.dropdown-item
                    options:
                      title: [email protected]
                      value: [email protected]
              - type: component.dropdown
                instanceId: customerType
                options:
                  label: Customer Type
                  data: [email protected]
                  initialValue: [email protected]
                  item:
                    type: component.dropdown-item
                    options:
                      title: [email protected]
                      value: [email protected]

actions:
  - children:
      - type: action.execute-entity
        options:
          title: Update Customer
          provider: DATA_PROVIDER_REST
          entity: customers
          method: update
          # Use replace to ensure you only have one update on the queue related 
          # to a record. Not doing this will not break the solution but will 
          # help to avoid chattiness and scenarios where backends have rate limits
          queueOperation: replace
          function: rest-update-customer
          parameters:
            id: [email protected]
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            customerType: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)
            jobTitle: [email protected]
            phone1: [email protected]
            phone2: [email protected]
            region: [email protected]
            state: [email protected]
            web: =$lowercase(@ctx.components.web.state.value)
            zip: [email protected]
          data:
            id: [email protected]
            firstName: [email protected]
            lastName: [email protected]
            companyName: [email protected]
            address: [email protected]
            city: [email protected]
            customerType: [email protected]
            email: =$lowercase(@ctx.components.email.state.value)
            jobTitle: [email protected]
            phone1: [email protected]
            phone2: [email protected]
            region: [email protected]
            state: [email protected]
            web: =$lowercase(@ctx.components.web.state.value)
            zip: [email protected]   
      # Use the clear-queue to discard any commands in the queue while the
      #  device is offline.
      - type: action.clear-queue
        options:
          title: Cancel all updates
          id: [email protected]

Testing and debugging queues

As you add the queueOperation property to actions, it is helpful to test or debug that the commands are being executed as configured. Here is a jig that can help you see the commands being queued when the device is offline, and then see the queue clear when the device is back online.

debugging-queues
title: Queue
type: jig.list
icon: database-2

datasources:
  listdata:
    type: datasource.sqlite
    options:
      provider: DATA_PROVIDER_LOCAL
      entities:
        - _commandQueue
      query: |
        SELECT 
          id, 
          json_extract(payload, '$.functionId') as functionId,
          [type], 
          [queue], 
          [state], 
          [error],
          @dummy as dummy
        FROM [_commandQueue]
      queryParameters:
        dummy: [email protected]

data: [email protected]
item:
  type: component.list-item
  options:
    title: [email protected] & ' ' & @ctx.current.item.dummy
    subtitle: [email protected] & ' ' & @ctx.current.item.state
    description: [email protected]

Last updated

Was this helpful?