How to update only certain parts of the UI?

I have the following screen:

<actions>
        <set field="facilityId" from="facilityId ?: ec.user.getPreference('FacilityActive') ?: ec.user.getPreference('FacilityGeneralDefault')"/>
        <set field="pageSize" from="pageSize ?: 60"/>
        <set field="result" from="ec.service.sync().name('UIServices.searchProduct').parameters(ec.context).call()"/>
        <set field="productList" from="result.productList"/>
        <set field="tileName" from="result.tileName"/>
        <script>paginateList("productList", null, context)</script>
    </actions>

    <widgets>
        <container-row><row-col md="6">
            <form-single name="SearchForm" transition="." focus-field="lookupId">
                <field name="lookupId"><default-field title="Cod bare"><text-line/></default-field></field>
                <field name="submitButton"><default-field title="Cauta"><submit/></default-field></field>
                <field-layout><field-row-big><field-ref name="lookupId"/><field-ref name="submitButton"/></field-row-big></field-layout>
            </form-single>
        </row-col>
        <row-col md="6">
            <form-single name="TileForm">
                <field name="tileName"><default-field title="Model gresie"><text-line/></default-field></field>
                <field name="scrapeDunca"><default-field title="Cauta Dunca"><submit/></default-field></field>
                <field-layout><field-row-big><field-ref name="tileName"/><field-ref name="scrapeDunca"/></field-row-big></field-layout>
            </form-single>
        </row-col></container-row>

        <form-list name="ProductList" list="productList" skip-form="true">
            <row-actions>
                <entity-find entity-name="mantle.product.ProductIdentification" list="prodIdentList">
                    <econdition field-name="productId"/><order-by field-name="productIdTypeEnumId"/></entity-find>
            </row-actions>
            <field name="pseudoId">
                <header-field title="ID" show-order-by="case-insensitive"/>
                <default-field><display also-hidden="false"/></default-field>
            </field>
            <field name="prodIdents"><default-field title="Other IDs">
                <section-iterate name="OtherProductIdSection" list="prodIdentList" entry="prodIdent"><widgets>
                    <label text="${prodIdent.idValue} (${prodIdent.type?.description ?: ''})" type="div"/>
                </widgets></section-iterate>
            </default-field></field>

            <field name="productName">
                <header-field title="Name" show-order-by="case-insensitive"/>
                <default-field><display also-hidden="false"/></default-field>
            </field>

            <field name="priceNicename">
                <header-field title="Price" show-order-by="true"/>
                <default-field><display also-hidden="false"/></default-field>
            </field>

            <field name="stockL1">
                <header-field title="Stoc L1" show-order-by="true"/>
                <default-field><display also-hidden="false"/></default-field>
            </field>
            <field name="stockL2">
                <header-field title="Stoc L2" show-order-by="true"/>
                <default-field><display also-hidden="false"/></default-field>
            </field>

            <form-list-column><field-ref name="pseudoId"/><field-ref name="prodIdents"/></form-list-column>
            <form-list-column><field-ref name="productName"/></form-list-column>
            <form-list-column><field-ref name="priceNicename"/></form-list-column>
            <form-list-column><field-ref name="stockL1"/></form-list-column>
            <form-list-column><field-ref name="stockL2"/></form-list-column>
        </form-list>
    </widgets>

Service definition:

<service verb="search" noun="Product" type="script"
             location="classpath://ro/colibri/legacy/service/ui/searchProduct.groovy">
        <in-parameters>
            <parameter name="facilityId" required="true"/>
            <parameter name="lookupId"/>
        </in-parameters>
        <out-parameters>
            <parameter name="productList" type="List"/>
            <parameter name="tileName"/>
        </out-parameters>
    </service>

And the service script:

package ro.colibri.legacy.service.ui

import com.google.common.collect.ImmutableList
import org.moqui.context.ExecutionContext
import ro.colibri.entities.comercial.Operatiune
import ro.colibri.entities.comercial.Product
import ro.colibri.entities.comercial.mappings.ProductGestiuneMapping
import ro.colibri.legacy.service.LegacySyncServices

ExecutionContext ec = context.ec
productList = []

for (var entity in ec.entity.find("mantle.product.ProductFindView")
        .condition("idValue", lookupId)
        .list()) {
    Map<String, Object> p = entity.getMap()

    var moquiPrice = ec.service.sync().name("mantle.product.PriceServices.get#ProductPrice")
            .parameter("productStoreId", facilityId)
            .parameter("productId", p.productId)
            .call()
            .get("price")

    Product legacyProduct
    if (lookupId) {
        Operatiune op = new Operatiune()
        op.setBarcode(lookupId)
        legacyProduct = LegacySyncServices.convertToProducts(ImmutableList.of(op)).stream().findFirst().orElse(null)
    }

    var price = moquiPrice ?: legacyProduct?.pricePerUom
    p.put("priceNicename", price + " RON/" + legacyProduct?.uom)

    Set<ProductGestiuneMapping> stocuri = legacyProduct?.stocuri
    p.put("stockL1", stocuri.find { it.gestiune.importName.equals("L1")}?.stoc + " " + legacyProduct?.uom)
    p.put("stockL2", stocuri.find { it.gestiune.importName.equals("L2")}?.stoc + " " + legacyProduct?.uom)

    if (lookupId) {
        var supplierPrice = ec.entity.find("mantle.product.ProductPrice")
                .condition("productId", p.productId)
                .condition("customerPartyId", facilityId)
                .condition("preferredOrderEnumId", "SpoMain")
                .one()

        var supplierName = supplierPrice ? ec.entity.find("mantle.party.Organization")
                .condition("partyId", supplierPrice.vendorPartyId)
                .one() : null

        if (supplierName?.organizationName?.equals("DUNCA CONSTRUCT SRL"))
            tileName = ro.colibri.util.Utils.extractTileName(p.productName)
    }

    productList.add(p)
}

As you can see I have 2 search fields in there: SearchForm and TileForm. As far as I can tell the actions run every time the page is reloaded and each time I use one of the search forms the page gets reloaded, since the transition=“.”, but the context is kept.

My question is how can I reload only certain parts of the UI without the context being lost? I want to do a search using TileForm, but I don’t want all the actions to run again. I only want the actions to run when I use the other form SearchForm. I tried a lot of combinations using transitions, but either the context gets lost, either it doesn’t work at all.

Okay, I didn’t manage to reload only some parts of the UI using transitions, but I managed to do it somehow using conditional actions. Note that the whole page is still reloaded with every search, but only some actions run, based on the filter texts:

<screen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/xml-screen-3.xsd"
        default-menu-title="Dashboard" default-menu-index="1">

    <actions>
        <if condition="lookupId">
            <set field="facilityId" from="facilityId ?: ec.user.getPreference('FacilityActive') ?: ec.user.getPreference('FacilityGeneralDefault')"/>
            <service-call name="UIServices.searchProduct" in-map="true" out-map="context"/>
            <script>paginateList("productList", null, context)</script>
        </if>
        <if condition="tileName">
            <service-call name="UIServices.scrapeDuncaTileInfo" in-map="true" out-map="context"/>
            <set field="scrapedList" from="response"/>
            <script>paginateList("scrapedList", null, context)</script>
        </if>
    </actions>

    <widgets>
        <container-row><row-col md="6">
            <form-single name="SearchForm" transition="." focus-field="lookupId">
                <field name="lookupId"><default-field title="Cod bare"><text-line/></default-field></field>
                <field name="submitButton"><default-field title="Cauta"><submit/></default-field></field>
                <field-layout><field-row-big><field-ref name="lookupId"/><field-ref name="submitButton"/></field-row-big></field-layout>
            </form-single>
        </row-col>
        <row-col md="6">
            <form-single name="TileForm">
                <field name="tileName"><default-field title="Model gresie"><text-line/></default-field></field>
                <field name="scrapeDunca"><default-field title="Cauta Dunca"><submit/></default-field></field>
                <field-layout><field-row-big><field-ref name="tileName"/><field-ref name="scrapeDunca"/></field-row-big></field-layout>
            </form-single>
        </row-col></container-row>

        <form-list name="ProductList" list="productList" skip-form="true">
            <row-actions>
                <entity-find entity-name="mantle.product.ProductIdentification" list="prodIdentList">
                    <econdition field-name="productId"/><order-by field-name="productIdTypeEnumId"/></entity-find>
            </row-actions>
            <field name="pseudoId">
                <header-field title="ID" show-order-by="case-insensitive"/>
                <default-field><display also-hidden="false"/></default-field>
            </field>
            <field name="prodIdents"><default-field title="Other IDs">
                <section-iterate name="OtherProductIdSection" list="prodIdentList" entry="prodIdent"><widgets>
                    <label text="${prodIdent.idValue} (${prodIdent.type?.description ?: ''})" type="div"/>
                </widgets></section-iterate>
            </default-field></field>

            <field name="productName">
                <header-field title="Name" show-order-by="case-insensitive"/>
                <default-field><display also-hidden="false"/></default-field>
            </field>

            <field name="priceNicename">
                <header-field title="Price" show-order-by="true"/>
                <default-field><display also-hidden="false"/></default-field>
            </field>

            <field name="stockL1">
                <header-field title="Stoc L1" show-order-by="true"/>
                <default-field><display also-hidden="false"/></default-field>
            </field>
            <field name="stockL2">
                <header-field title="Stoc L2" show-order-by="true"/>
                <default-field><display also-hidden="false"/></default-field>
            </field>

            <form-list-column><field-ref name="pseudoId"/><field-ref name="prodIdents"/></form-list-column>
            <form-list-column><field-ref name="productName"/></form-list-column>
            <form-list-column><field-ref name="priceNicename"/></form-list-column>
            <form-list-column><field-ref name="stockL1"/></form-list-column>
            <form-list-column><field-ref name="stockL2"/></form-list-column>
        </form-list>

        <form-list name="ScrapedList" list="scrapedList" skip-form="true">
            <field name="name">
                <header-field title="Name" show-order-by="case-insensitive"/>
                <default-field><display/></default-field>
            </field>

            <field name="oradea">
                <header-field title="Stoc Oradea" show-order-by="true"/>
                <default-field><display/></default-field>
            </field>
            <field name="bucuresti">
                <header-field title="Stoc Bucuresti" show-order-by="true"/>
                <default-field><display/></default-field>
            </field>
            <field name="iasi">
                <header-field title="Stoc Iasi" show-order-by="true"/>
                <default-field><display/></default-field>
            </field>
        </form-list>
    </widgets>

</screen>

And service definitions:

<service verb="search" noun="Product" type="script"
             location="classpath://ro/colibri/legacy/service/ui/searchProduct.groovy">
        <in-parameters>
            <parameter name="facilityId" required="true"/>
            <parameter name="lookupId"/>
        </in-parameters>
        <out-parameters>
            <parameter name="productList" type="List"/>
            <parameter name="tileName"/>
        </out-parameters>
    </service>
    
    <service verb="scrape" noun="DuncaTileInfo" type="remote-rest"
             method="GET" location="${System.getProperty('SCRAPE_URL')}/${URLEncoder.encode(tileName, 'UTF-8')}">
        <in-parameters>
            <parameter name="tileName" required="true"/>
        </in-parameters>
        <out-parameters>
            <parameter name="response" type="List">
                <parameter name="name"/>
                <parameter name="oradea"/>
                <parameter name="bucuresti"/>
                <parameter name="iasi"/>
            </parameter>
        </out-parameters>
    </service>