Read the previous installments in the series: Getting started with the OutSystems Agile Platform, Learning the basics of the OutSystems Agile Platform, Describing the OutSystems Agile Platform Service Studio experience, Working with the OutSystems Agile Platform's Integration Studio, Deploying an application created with the OutSystems Agile Platform, Adding ECT to an eSpace with the OutSystems Agile Platform, and Working with scheduled jobs in the OutSystems Agile Platform.
I have always been cautious about AJAX. When AJAX first became widespread, it was a royal pain to make work, and it wasn't worth it. It's also too easy to hide valuable information behind AJAX in a way that search engines cannot find it. In addition, there are questions around accessibility and usability that are not well answered with AJAX.
So I had no intentions of using AJAX in Rat Catcher at first because it is not required in the application. After all, I could live with the postbacks in the application if it meant that I did not have to do more work. But I saw how nice the AJAX worked on the OutSystems site, so I decided to give it a shot. It was great to find out that working with AJAX in the Agile Platform requires about five minutes to learn, a minor change to your logic, and a little drag-n-drop. And from what has been said, version 5.1 will improve upon this even more.When I decided to try AJAX in the application, it was with minor functionality: I wanted to replace the labels on the login area with the in-control labels that disappear when you start typing. This would save some horizontal screen space and make the screen layout a bit cleaner. You can see the results in Figure A. Figure A
Using the Input_SetPrompt AJAX widget on the login section
It is a cinch to make this effect. First, I needed to add a reference to the RichWidgets Extension to my eSpace. If you plan on using a lot of AJAX, make it easy on yourself and just grab everything in the RichWidgets Extension. I tried to pick and choose what I would make references to, but the add reference system does not make it clear which entities and actions are needed for each widget. The documentation helps, but for a project that will use a lot of the widgets (and once you get started, it's hard to resist the temptation to use more of the RichWidgets collection), you might as well get them all.Next, I needed to have the field on the screen. This is very important: You absolutely must provide a Name attribute for the field. Then, I dragged the Input_SetPrompt Web Block onto my Web Screen or Block from the RichWidgets Screen Flow on the right. (For the sake of organization, it is suggested, but not mandatory, that developers put the item next to the widget that it will affect.) Finally, I needed to tell Input_SetPrompt what text to display, and provide it the ID of the widget I wanted it to act upon (this is why it needs a name) (Figure B). Figure B
The Input_SetPrompt widget and its properties in Service Studio
Once I saw how easy this was, I moved on to using the Popup_Editor control combined with Feedback_Message and AjaxRefresh to allow the editing of user information in a nice overlay window. I wanted to do this because the user information editor was not going to have enough fields for a separate Web Screen to feel right. In Rat Catcher, users are associated with an account. This allows each customer (represented by the account) to have as many users of the system as they want, without paying extra money. I do not care how many users there are because it is the number of documents put into the system that put load on the servers. In fact, from where I sit, more users per account makes it easier for the customers to need a more expensive account! But this means that I need a way for account administrators to edit user information, reset passwords, and create new users; after all, I would rather have the users call someone in their company for help and not me.To make the user editor, all I needed to do was create a Web Screen that performed the necessary functionality. I made the EditUser screen look plain, because it is designed to be a popup window over the rest of the screen (Figure C). I gave it an optional input parameter with the user ID. If there is no user ID specified, the system will create a new user; if a user ID is specified, the screen will have the option of deleting the user. Figure C
The EditUser Web Screen, which is very plain without a header or a footer.There is nothing particularly special about the logic in this screen until you look at the SaveUser or the DeleteUser Actions to see what happens when the work is done (Figure D). The call to Popup_Editor_Notify lets the screen that created the popup know that the work is finished, and it can provide a return parameter. I provide the User ID, which is discarded, but I may need it in the future. The call to Popup_Editor_Close closes the window and should be the final Action on the Screen (Figure D). Figure D
The logic underneath my EditUser Web Screen -- the Popup_Editor_Notify and Popup_Editor_Close Actions -- is needed for the AJAX functionality to work.I wanted to tie the popup into the main account screen. The first thing I needed to do was to provide ways of getting the editor to display, so I added a link to the page. The link points to the EditUser Web Screen (Figure E). Next, I dragged the Popup_Editor block from RichWidgets next to the link (again, the positioning is only for organizational reasons). The widget needs a number of attributes to be filled; the most important is the link widget's ID. Because of this requirement, it is critical to provide a Name attribute for your link (just like with Input_SetPrompt) because it will not let you pick an ID in the expression editor otherwise. You can also provide a title and a width and a height. If you don't choose a width or a height, it will use auto sizing. I was not 100% happy with the auto sizing because the sizing is based on the controls, and it does not take into account the asterisk used to mark mandatory fields, so the auto size tends to be too small if you have mandatory fields. Finally, you provide it the name of an Action to perform when the On Notify event is fired when the editor wraps up and calls Popup_Editor_Notify. Figure E
The AccountSettings Web Screen and where it interacts with the EditUser screen
All of this AJAX work had put me in a bind: I was editing information that was being displayed to the user, but because I was not using postbacks to do it, the on-screen copy of the information was not being updated. This is where things got a little bit trickier, but the RichWidgets collection was still the ticket to a nice solution.
Originally, I had set up my table to be bound directly to a query that was run in the Screen's Preparation action. This was fine and dandy, but it does not work with the AJAX way of doing things. The problem was that I have no way of re-rerunning Preparation. Instead of binding the table to the query directly, I needed to create a local variable of type Record List and then use the Record Editor to give it the same entities that the query was selecting. Next, I bound the table's source to that variable, and then I went into Preparation and used an Assign widget to set that variable equal to the query's output. This kept things seamless.Figure F
The user editor in action
To get the table updated when I was done editing users, I made a new action called RefreshUserList. I copied the query from Preparation into this Action, as well as the Assign to the local variable. Then I added an AjaxRefresh widget to the Action, pointing its Widget attribute to the table and choosing which special effect I wanted the table to show when the update occurred. And I was done! One thing I discovered later on is that by using the List_LateLoad widget, I could eliminate the query in Preparation, which would have the advantage of giving me only one place to maintain the query.
Now that I had a AJAX-y table refresh, I decided that it would be a good idea to go through the rest of the application and do the same kind of makeover. On the Account Settings page, I used a popup editor for the mailing and billing addresses and the Default Domains to Ignore list. Next, I added My User Settings as a popup editor from the Header block. I also used the Feedback widget to provide nice messages for changing passwords and a few other minor functions that did not require a full blown confirmation page. I also evaluated a number of tables and decided to add the List_LateLoad widget to many of them (which causes the list's contents to be loaded as an AJAX event after the page loads instead of being delivered with the initial screen draw) as well as the List_Navigation component to allow for AJAX'ed paging/navigation.
Once I got the hang of making the changes needed to move from the postback method to the AJAX methods, it took me less than an hour to overhaul the application. Plus, now I am familiar with the patterns needed to use AJAX in the Agile Platform.
I look forward to the changes in version 5.1, but even in the current state, AJAX is too easy to use not do it.
J.JaDisclosure of Justin's industry affiliations: Justin James has a contract with Spiceworks to write product buying guides; he has a contract with OpenAmplify, which is owned by Hapax, to write a series of blogs, tutorials, and articles; and he has a contract with OutSystems to write articles, sample code, etc.
Justin James is the Lead Architect for Conigent.