Getting Combo Box Values Set For Rest API
Hello everybody,
today I want to share idea on how to get getting Combo-box values set for REST API.
As usually values of comboboxes values are just hardcoded in web api calls, but sometimes it may be necessary to load them from Rest API, for example for cases if you want to target multiplve versions of Acumatica. For such a purpose I'd suggest to create Graph, which via reflection will read values from dlls. Quite similar to what Acumatica team does.
Below goes source code of graph, which via reflection loads data:
using PX.Data; using System; using System.Collections; using System.Reflection; namespace LA { public class ListAttributesInq : PXGraph<ListAttributesInq> { public PXCancel<ClassFilter> Cancel; public PXFilter<ClassFilter> Filter; public override bool IsDirty { get { return false; } } public ListAttributesInq() { Records.Cache.AllowInsert = false; Records.Cache.AllowDelete = false; Records.Cache.AllowUpdate = false; Actions["Process"].SetVisible(false); Actions["ProcessAll"].SetVisible(false); Actions["Schedule"].SetVisible(false); } [PXFilterable] [PXVirtualDAC] public PXFilteredProcessing<KeyValueRecord, ClassFilter> Records; protected IEnumerable records() { //var row = new ClassFilter { ClassName = "PX.Objects.CR.CRMSourcesAttribute" }; var row = Filter.Current; if (row != null && !string.IsNullOrWhiteSpace(row.ClassName)) { var type = Type.GetType(row.ClassName) ?? Type.GetType(row.ClassName + ", PX.Objects"); if (type != null) switch (type.BaseType.Name) { case "PXIntListAttribute": { int[] values; string[] labels; GetRecords(type, out values, out labels); for (int i = 0; i < values.Length; i++) yield return new KeyValueRecord { Key = values[i].ToString(), UiValue = labels[i] }; break; } case "PXStringListAttribute": { string[] values, labels; GetRecords(type, out values, out labels); for (int i = 0; i < values.Length; i++) yield return new KeyValueRecord { Key = values[i], UiValue = labels[i] }; break; } } } } private void GetRecords<T>(Type type, out T[] values, out string[] labels) { var obj = Activator.CreateInstance(type); var flags = BindingFlags.NonPublic | BindingFlags.Instance; values = type.GetField("_AllowedValues", flags).GetValue(obj) as T[]; labels = type.GetField("_AllowedLabels", flags).GetValue(obj) as string[]; } } }
It has few features:
- Method get records, which creates instance, and fills values
- Method records, which is delegate overload, and which distinguishes between PXIntListAttribute and PXStringListAttribute.
- yield return purpose of it is to be pageble ( for page opening it doesn't have big value ) and getting all records at once
- As entry, it is needed to enter class with it's namespace. Like PX.Objects.CR.CRMSourcesAttribute, and not like CRMSources
Aspx source code looks like this:
<%@ Page Language="C#" MasterPageFile="~/MasterPages/FormDetail.master" AutoEventWireup="true" ValidateRequest="false" CodeFile="LA401000.aspx.cs" Inherits="Page_LA401000" Title="Untitled Page" %> <%@ MasterType VirtualPath="~/MasterPages/FormDetail.master" %> <asp:Content ID="cont1" ContentPlaceHolderID="phDS" Runat="Server"> <px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" TypeName="LA.ListAttributesInq" PrimaryView="Filter"> <CallbackCommands></CallbackCommands> </px:PXDataSource> </asp:Content> <asp:Content ID="cont2" ContentPlaceHolderID="phF" Runat="Server"> <px:PXFormView SkinID="" ID="form" runat="server" DataSourceID="ds" DataMember="Filter" Width="100%" Height="" AllowAutoHide="false"> <Template> <px:PXLayoutRule ID="PXLayoutRule1" runat="server" StartRow="True"></px:PXLayoutRule> <px:PXTextEdit CommitChanges="True" runat="server" ID="CstPXTextEdit1" DataField="ClassName" ></px:PXTextEdit></Template> </px:PXFormView> </asp:Content> <asp:Content ID="cont3" ContentPlaceHolderID="phG" Runat="Server"> <px:PXGrid AllowFilter="True" AllowPaging="True" AllowSearch="True" SyncPosition="True" ID="grid" runat="server" DataSourceID="ds" Width="100%" Height="150px" SkinID="Inquire" AllowAutoHide="false"> <Levels> <px:PXGridLevel DataMember="Records"> <Columns> <px:PXGridColumn DataField="Key" Width="120" ></px:PXGridColumn> <px:PXGridColumn DataField="UiValue" Width="180" ></px:PXGridColumn></Columns> <RowTemplate> <px:PXTextEdit runat="server" ID="CstPXTextEdit2" DataField="Key" ></px:PXTextEdit> <px:PXTextEdit runat="server" ID="CstPXTextEdit3" DataField="UiValue" ></px:PXTextEdit></RowTemplate></px:PXGridLevel> </Levels> <AutoSize Container="Window" Enabled="True" MinHeight="150" ></AutoSize> <ActionBar > </ActionBar> </px:PXGrid> </asp:Content>
One more necessary DAC Class ClassFilter looks like this:
using PX.Data; using System; namespace LA { [Serializable] public class ClassFilter : IBqlTable { #region ClassName public abstract class className : PX.Data.IBqlField { } [PXString(50, IsUnicode = true, InputMask = "")] [PXUIField(DisplayName = "Class Name")] public string ClassName { get; set; } #endregion } }
DAC class KeyValueRecord looks very trivial in this case:
using PX.Data; using System; namespace LA { [Serializable] public class KeyValueRecord : IBqlTable { #region Key public abstract class key : PX.Data.IBqlField { } [PXString(10, IsUnicode = true, InputMask = "")] [PXUIField(DisplayName = "Key")] public string Key { get; set; } #endregion #region Value public abstract class uiValue : PX.Data.IBqlField { } [PXString(50, IsUnicode = true, InputMask = "")] [PXUIField(DisplayName = "Value")] public string UiValue { get; set; } #endregion } }
And the end result looks like this:
Summary
If you need to read something from Attributes, just use reflection.