Monday, 1 August 2016

Passing parameters from UI to PXSelectorAttribute

Hi All,

Today I want to speak with you about passing UI parameters to the server code and PXSelector attribute.
Lets assume that you have some document (like AR Invoice), where you have 2 keys: document type and document number. If you want to add reference to the this document for other screen (like payment), you need to have 2 selectors, that are depend on each other.

In this example we have a selector that depend on MultiPaymentInvoice.invoiceType. But how to correctly define this dependency. Here I want to describe 3 ways to archive it:
  • Current Parameter with Sync Position
  • Current Parameter with SyncGrid Paramenter
  • Optional Parameter with Control Parameter 
Lets discuss all of them.

First Solution - Sync Position
You can define your PXSelectorAttribute with Current parameter.

[PXSelector(typeof(Search<ARInvoice.refNbr,
       Where<ARInvoice.docTypeEqual<Current<MultiPaymentInvoice.invoiceType>>,
              And<Customer.bAccountIDEqual<Current<MultiPayment.customerID>>>>>))]

But this is only one poit, you also need to make sure that UI will synchronize current record with server. By default Acuamtica does not sync it, as it is some impact to performance.
SyncPosition parameter on the grid level will synchronize the server Current with the UI row selected on every change. In code it would look like this:
<px:PXGrid ID="grid" runat="server" DataSourceID="ds" SyncPosition="true">

Second Solution - SyncGrid 
The first solution works well in most situations, but what if you have a lot of information in the row and are not using the selector frequently? Previous approach may affect performance, as require to sync grid every-time.

So to fix it you could use the specially designed tag PXSyncGridParam. It works similarly to SyncPosition, but it does it only when needed. With the SyncGrid parameter, Current is synchronized only when the user opens the selector. In the code, it looks as shown below.

<px:PXSegmentMask ID="edRefNbr"  DataField="RefNbr"  AutoRefresh="True">           
    <Parameters>
          <px:PXSyncGridParam ControlID="grid" />
    </Parameters>
</px:PXSegmentMask>

Third Solution - Control Parameter
The solutions above can work in most situations, but what if for some reason our DataRow cannot be inserted in the cache? For example, our platform Acumatica cannot work with empty keys. And you may have multiple keys (like 3, 4) that are depend on each other.

To solve problem of empty keys you can disallow insertion into the cache, by canceling during the RowInserting event.

But what to do with selectors, as they are depend on current? To deal with this our platform support use of the Optional parameter on the server side.

[PXSelector(typeof(Search<ARInvoice.refNbr,
       Where<ARInvoice.docTypeEqual<Optional<MultiPaymentInvoice.invoiceType>>,
              And<Customer.bAccountIDEqual<Optional<MultiPayment.customerID>>>>>))]

If you use Current, it will work in the same way as always, but if Current is missing, you can pass parameters from the UI directly, as shown in the following code.

<px:PXSegmentMask ID="edRefNbr" DataField="RefNbr" AutoRefresh="True">
    <Parameters>
        <px:PXControlParam ControlID="grid" Name="MultiPaymentInvoice.InvoiceType"
                PropertyName="DataValues[&quot;InvoiceType&quot;]" />
    </Parameters>
</px:PXSegmentMask>

As you can see in the example above, the required parameter is taken from the currently selected row in the UI of a neighbor cell. You could take the value from the main form too, as shown below.
<px:PXControlParam ControlID="form" Name="MultiPayment.customerID" 
        PropertyName="DataControls[&quot;edCustomerID&quot;].Value" />

This way is not the best, as you have some hard-coded values, but may be useful in some cases.

Big thanks to the Acumatica development team for the example.
Have a nice development.

No comments: