Over the last few months, I have been on a journey, if not more aptly described adventure, of learning how to get started converting screens from webforms (.aspx) to TypeScript for the coming Acumatica Modern UI. With 2023R2 release around the corner, it’s time to share some landmines to avoid in making the switch. It also seems like a great time to add onto the previous post about getting started and creating my first VERY BASIC screen in the modern UI.
Breaking down the typescript files types
Every screen in the modern UI is comprised of at least two files… a .html file for the screen layout, and a .ts file to generate the views and classes that will populate the .html file. Think of the .html file as a generic form letter, and the .ts file as the definition of the data that will fill it. With typescript, it is quite easy to segregate the .ts file into multiple files to make it much easier to group the configuration information resulting in much more readable files for the developer. The extra file(s) are “imported” into the main .ts file to access the classes contained within. These files are contained within a folder of the same name as the Screen ID, and the main .html file and .ts file share the same naming convention. When using supplemental .ts files, the naming convention is up to the developer. Acumatica stores classes in views.ts which are imported via WO204000.ts in the current example.
Basics of the .ts file
The typescript file contains 3 primary elements. The imports and optional text declarations, the screen definition tying the graph and screen views together, and the classes defining the data to be displayed.
The Imports and Optional Text Declarations
- The base typescript file imports classes from client-controls which will be used within the other classes defined in the typescript file.
- In this example, the classes related to the DAC’s that will be displayed are defined in a separate file called views.ts, so these classes must be imported from views. (The .ts is implied here.)
- This screen has several tabs, so the tab labels are defined in a class called TabHeaders and decorated with @localizable to tap into localization capabilities.
The Screen Definition
- Here, the screen (WO204000) class is defined, extending ScreenBaseViewModel.
- The screen class is decorated with @graphInfo to specify the fully qualified graph type and primary view.
- Since this screen uses a class called TabHeaders, that class must be initialized. In this case, the same name is used, but it can be named anything within reason provided the same name (on the left side of the equals sign) is used later in the .html file.
- Single record views are initialized using createInstance(Primary DAC of the view). If the view is complex, only the first DAC is included here.
- Collections (multi-record) views are initialized using createCollection(Primary DAC of the view). If the view is complex, again only the first DAC is included here. As shown in FailureModes, various settings like those made in the legacy .aspx file are set here.
The “DAC” Classes
The DAC’s and their respective fields to be displayed on the screen may also be defined within this file. In this example, those definitions are moved to views.ts and imported starting at line 10 below. In the next section, the views.ts file will be explained, so read on.
Basics of a views.ts File
Since some screens display many fields from many DAC’s, sometimes it make sense to move those into their own views.ts file. In extreme cases, it might make sense even to break them into many files and imported individually into the SCREENID.ts file (example above).
Just like the WO204000.ts file above, this separate file first must import the classes from client-controls that are used in defining the individual “DAC” classes.
Next, each “DAC” class is defined, specifying only the fields that should be displayed on the screen. Much like the mobile app, this presentation layer can be made more efficient by restricting what fields we ask the server to process. A field can be added to these classes and not used, but it cannot be used on the screen without defining it in the class here.
Each “DAC” class extends BaseViewModel. If prefixed with @commitChanges, the field will trigger a postback to the server just as when using CommitChanges in the legacy .aspx file. To make a field enhance the screen’s header (i.e. when the Stocked Items screen shows the description beside the Inventory CD), prepend with @headerDescription. All fields must inherit PXFieldState to specify that these are fields.
In some cases, the view contains multiple DAC’s, and the screen needs to display a value from one of the related DAC’s. In the case of WOEquipmentBOM, notice the second field. The DAC contains the Inventory ID, but the screen also needs to show the description of that item. This is defined as DAC__Field (2 underscores) just as in the legacy .aspx file. While it is not part of the WOEquipmentBOM table in the database, it is being described to the presentation layer as if it is. Note that the .html file will reference this field exactly the same way later.
While these classes could be defined directly in WO204000.ts, it is much quicker and easier to find these definitions in the separate views.ts file. Remember that this file is imported at the top of WO204000.ts, so take another look above after reviewing the views.ts file below.
Basics of the .html File
Now that the data elements and the graph details have been defined, the .html file pulls it all together to populate the “form letter” to the screen. HTML 5 can be mixed with typescript to form a highly customizable user experience. In this example, only div tags augment the typescript used to build the screen, and standard Acumatica will populate the data section of the screen with the template defined here.
The elements must have complementary opening and closing tags. The ones used in this example are:
- template – defines the page template to be displayed
- qp-shrinking-panel – this is a collapsible panel like used in a form tab screen
- qp-fieldset – this relates to defining a form where a view is bound which contains the fields to be displayed as identified within this section
- field – each field to be displayed in a form (qp-fieldset) is defined here
- qp-tabbar – this defines a tab control which will contain one or more tabs
- qp-tab – this defines a single tab of a qp-tabbar and contains a qp-fieldset for forms or a qp-grid for grids, or even a combination of the two
- qp-grid – this defines a gridand binds to the view to be displayed. The grid displays all of the fields contained in the related class of the view’s collection.
- columns – individual columns can be described within columns, although this screen leaves the columns section blank.
Notice also that the TabHeaders that were defined in WO204000.ts have been specified in qp-tab using header.bind to specify the static labels of the TabHeaders class.
Overcoming the “Refresh” Nightmare
Over the past three months, perhaps the most painful lesson I learned was that Reset Caches in the Apply Updates screen is NOT enough to clear the necessary caches to see screen updates. These typescript-based screens result in cache on the client that must be cleared for every refresh. Sometimes this alone is enough, but sometimes Reset Caches in Apply Updates is also required. When Reset Caches is required is still a mystery to me as I have yet to find a rhyme or reason, but if a clean refresh on my browser doesn’t do it, then I jump to Reset Caches.
Making the client side clear the cache every time can be really painful if you just can’t remember to do it. However, it is super easy using the F12 key on Edge to open the DevTools side panel. Click on the gear to edit preferences, and navigate to the Network section. Check the box for Disable cache (while DevTools is open) and then just make sure you hit F12 to leave DevTools open while working.
This post clearly has some length. Getting to this point has taken literally 3 months since my last post on the Modern UI, and I have a LOT left to learn. I hope you find it packed with many insights to make your journey to learning the modern UI much less “adventurous” than mine. I can assure you that if I didn’t make mistakes in this post, you certainly will. Be patient with yourself, and look for other examples. If you want to do something complicated, like everything else, break it down into smaller pieces. For instance, want a FormTab screen? Create the layout with just the form and an empty tab bar with a couple of empty tabs on it. Grow your files to fill in the spaces as you gain confidence, and you will build competence. None of this is hard… now. It just takes time and experience. I sincerely hope my learning curve shortens yours.
Good luck, and Happy Coding!