In a previous blog post, The Reasons Why pgAdmin Chose ReactJS, I had discussed the rationale behind choosing ReactJS for porting away from Backbone/jQuery. Following the post, I want to share the experience and course of actions EDB's pgAdmin team followed to achieve our goal. To date, the pgAdmin team has successfully ported all the properties dialog of the browser nodes to React based forms and has reached the testing phase.
The existing UI framework
The existing implementation was based on Backform which is based on Backbone. Backform takes a schema object as input and transforms it into a form along with input events and form data session management. A schema object is a collection of fields where each field provides enough information for converting it into an input control.
For example,
{
id: 'name', label: gettext('Name'), type: 'text', group: 'General',
mode: ['properties', 'edit', 'create']
}
Here, the field is converted to text input with label Name under the tab General and is applicable for Create/Edit dialogs and is visible in the Properties tab.
The advantage of this is that there is no need to write the JS code for each individual node. Forms can be generated dynamically just by defining a schema and any changes done will be reflected on all the nodes without needing to go and change individual nodes.
The Schema View UI Framework based on ReactJS
The team spent a good amount of time on creating a similar UI framework based on ReactJS which could re-use most of the existing schema definitions for faster, effortless migration. It was important to make it robust and flexible since there were more than 50 different schemas (for nodes, e.g. servers, databases, functions, tables etc). The framework was made to force moving from plain schema objects to ES6 class based schemas inherited from a base class to make the schema definitions more consistent. The framework is capable of:
- Converting the schema to a tabbed/non-tabbed input form
- A read-only accordion based form view (used by Properties tab)
- Optional SQL tab
- Form data session management with reset
- Data grids for collections with expandable forms
- Error handling based on validations done by the schemas
- Improved UI performance
More about Schema View UI Framework
This part is more on the technical side so readers uninterested can skip this section.
The source code for the UI framework named as SchemaView can be found here.
The above diagram shows the overall design of the framework.
1. All the components are functional components and use React hooks.
2. SchemaView is the parent component. The component handles the complete state management, error state and display, form reset, and tracks changed data. It provides callbacks for save and help button clicks. It also provides a callback to get the current changed data. You can optionally hide the footer if not required. For the input controls, it hands it over to the FormView component.
The following is an example of SchemaView.
3. FormView component is the starting building block which converts the schema definition to a set of controls grouped by tabs. FormView uses the MappedFormControl component to get the control for a type. In the above image, the field of type text is converted to text input and a field of type select is converted to a dropdown.
4.If the type is collection, FormView will hand it over to the DataGridView component. DataGridView generates an editable table. DataGridView uses MappedCellControl to render cells which can be of different types similar to FormView. The rows of the table can be optionally allowed to expand and show a nested form. Below is an example of DataGridView of table columns. The rows can be expanded using the edit button which is useful when all the fields cannot be shown on the grid row. When expanded, it uses the FormView component again to show the form.
5. The FormView can have nested forms as type (which were previously handled by a custom subnode control in Backform). To generate nested forms, it uses FormView. Below is an example of a nested form where the form has two tabs - Table and TOAST table.
6. If the type is a fieldset, then it will hand it over to the FieldSetView component. Below is an example of FieldSetView. Controls are enclosed in a fieldset with a title.
7. All the schema UI class definitions have to be inherited from BaseUISchema class.
Porting the nodes
Now that the framework was ready, it was decided that our team would try it on three nodes - Server group, Server, and Database. It was also decided to move out the UI related stuff to a separate file. So we created the new schema classes inheriting from base schema UI class in new files like - server_group.ui.js. The schema fields from old code were moved to the new files. After many iterations of code changes and corrections in the schema view framework, the schema view framework was ready to take on all other remaining nodes.
At this time, we had the launchpad ready. Learning how to use the new framework was easy and so, a list of all the nodes was created. Developers started picking up the nodes and were able to migrate. More supported input controls had to be added as required as migration was in progress.
Conclusion
The most important part was to make the migration process efficient and effortless. It took time initially for the view framework to be ready but the efforts paid off when the team was able to complete the migration process smoothly. The pgAdmin team is putting a lot of effort into migrating pgAdmin to ReactJS to make pgAdmin more performant and robust.
Read more: How to Get the Most Out of the Schema Diff Tool in pgAdmin 4