This document is a work in progress and we invite you to send us your feedback.
This document is intended for experienced developers and leads as guidance in migrating Traditional Web Apps to Reactive Web Apps. The migration requires good knowledge of the new front-end features and knowledge of the application architecture. Check our training Becoming a Reactive Web Developer for an introduction.
We provide this reference document as a collection of notes about the differences between Traditional and Reactive App. The purpose is to help developers in migrating an existing Traditional app to a Reactive runtime.
In this document we use the following terms:
- source - refers to the existing Traditional Web App we're migrating from
- target - refers to the new Reactive Web App we're migrating to
Preparation and initial planning
These are the considerations relevant in the preparatory steps of the Traditional Module migration.
- URLs should be working for new Screens, Resources, Images. If you use hardcoded URLs, you need to update them in your new Screens.
- Migrate Entities while preserving the data
- Handling Site Properties
- Migrating Roles
- Impact of Timers and Processes
Once you start migrating your app to the Reactive App, we suggest the following order:
- UI Flows
- External sites
- Blocks (in order of dependency)
There are several accelerators that can help you migrate your app faster.
Copy and paste
You can copy the following elements from your Traditional App to Reactive App:
- Server Actions
Additionally, you can:
- Paste a Server Action to a Screen Client Action, but only the elements that work are preserved.
- Paste Aggregates to a Screen to create a Screen Aggregate.
Scaffolding and Screen Templates
If your Screens are based on scaffolding or Screen Templates, it is faster to recreate them the same way. Makes sure you're using the latest version and try out different scaffolding patterns.
Here is an overview of the elements that are relevant to the module (eSpace) level of migration, with details on how to approach creating new logic in Reactive App.
When migrating logic that deals with Entities to the target app, pay attention to those which you need to make public or set Expose Read-Only to false.
Creating a new Entity in your target app creates a new physical table. With this in mind, the suggestion is to change the Entity to be public, then add a reference to it in the target module.
You can also use the accelerators for migrating Entities (copy and paste them).
We're working on improving this feature. Occasionally during page refreshes the locale settings can reset to English.
For the client-side UI and client-side logic use Multilingual Component from Forge. Extract the text from the source module into the spreadsheet and convert it to the format needed for AddTranslations or AddTranslationsFromResource. Determine (e.g., by searching for uses of the translated text) which translation keys correspond to which elements and add
data-trans attributes or GetTranslation calls where possible.
For the server-side logic use the built-in GetCurrentLocale function and explicitly inline the translations.
On Session Start
Use On Application Ready Event in Reactive App to set the initial values of Client Variables as the configuration parameters in the app.
When On Session Start is used in the source app to initialize the Session Variables, migrate the logic in the target app in a place where the variables are read, and protect it by checking if the variable is defined. For example, if you're using Cookies, add the logic before calling GetCookie, so it runs only if the Cookie was not set before.
If On Session Start is used otherwise in the source app, it needs to be evaluated case-by-case. Take into consideration that this logic runs on the first server request and when a user logs in and on the first request after having logged out.
Change the Process to be public in the original module and reference the Process, to avoid losing data.
To avoid losing data, change the Role to be public in the original module and reference the Role. Afterwards, confirm that all the Screen permissions are the same.
The way you migrate Server Action to the new app depends on whether the Action is public, its use in the Screens, and whether it can be used client-side in the new Reactive App. Check the accelerators section to learn how you can use copy and paste with these Actions.
Not used a function
You can migrate it directly to Server Action in the target app.
Used as a function with input parameters
Migrate this Action so that it is called after the input parameters are calculated, typically at the end of a Data Action. Create a Data Action with an output for each Action. The Data Action should call the Server Actions in the order that they were used in the Screen.
Used as a function with a constant number of times in a Screen
The Server Action is used a constant number of times on the Screen (not in a List). Migrate the Action to Server Action in the target module while creating a local variable for each function call.
Used as a function with a variable number of times in a Screen
The Server Action is used a variable number of times on the Screen (in a List). Migrate the Action to Server Action in the target module. Then, add a calculated Attribute in an Aggregate that creates the list that calls the function.
Client Variables in Reactive are in many ways similar to Session Variables. Use Client Variables with the On Application Ready Event to initialize global configurations for the apps, like instantiating username, locale, filters.
Keep in mind:
- The value of a Client Variables is cleared when the user signs out
- On Application Ready System Event is triggered every time you run the app in a new tab/browser
The migration a Session Variable depends on whether the variable is tied to a browser session or user preference, its data type, and whether they may contain confidential information.
To preserve their current values, copy the values from the original module to the new one, via Service Center.
Tied to a browser session
If the Session Variable is tied to the browser session and the data type is simple, migrate it to a browser-session-lived Cookie using some name convention (e.g., SessionVariable_NAME). Keep in mind that the values are not cleared on log out.
Replace reads and writes by the GetCookie and SetCookie methods of HTTPRequestHandler.
If there're widgets bound to the variable, create a Local Variable to bind them to and then sync the value with the Cookie.
Used for storing a user preference
If the variable is used to store a user preference (i.e., it is tied to the user, not the browser session):
- If it is only used in the UI/client-side logic store the value in a Client Variable
- If the Session Variable is used by the server side, migrate the variable so the logic uses a row in a database Entity, associated to a User Id.
Additional work might be needed for variables of List and Record data types (e.g., you may need to devise an encoding, use a separate table...).
If the Session Variable use case is not described in this section, then the migration needs to be evaluated case-by-case.
Copy the CSS you need from the source app into the Theme of the target app.
The new UI FLows of the target app should point to the new Theme. The advanced properties HTTP Security, Integrated Authentication, and Internal Access Only cannot be migrated.
UI Flow elements
Here is an overview of the UI Flow elements and tips on the migration.
There is no conversion for this element. In the target app, right-click a Screen in the Elements Tree and select Mark as Default Screen to set the Screen as an "entry" point to a module.
Migrate the element to the External Site in the target app. All the input parameters and properties should have a direct mapping.
Pay attention to the External Sites with relative URLs to different modules, as these may fail due to different URL endings (there's no more .aspx or .jsf). The relative URLs to different modules are now handled by an internal router, so the full URLs need to be used.
Flow Exception Handler
The OnException handlers are on the client side. For the server-side errors you need to use the exception handlers on the flows if you want to handle the exception server-side. If you don't mind having exceptions bubbling up to the client-side, then you can handle them either on the client-side Action that called the Server Action or on the OnException handler on the UI Flow.
Check also the recommendations for Screen Actions.
You should create new Screens in your target app or reference them from other modules, with the following in mind:
- You should not store sensitive information in Local Variables.
- As the UI is generated on the client side, the Screens shown before the Role validation.
The Title property needs to be transformed to the text value instead of an Expression.
HTTP Security, Integrated Authentication, and Cache In Minutes have no mappings in the target app. Note that HTTPS is enabled by default.
Check the introductory notes for Screen.
Notify and NotifyGetMessage are not available in Reactive Web. Use Events and create corresponding handler Actions.
Referenced Web Block
Insert them through dependencies.
Screen and Block Logic
Here are details on how to migrate logic in your source Screens and Blocks to your new Reactive app.
The UI elements in the target module refresh automatically on data change, so the Ajax Refresh is not needed.
Use the new Download node by adding it to the logic flow.
Preparation is one of the elements not available in the Reactive App and you need to refactor this dedicated Screen Action. You should be careful not to replicate the Preparation logic in a way that slows down the app, for example, by forcing the client-side to wait for the server-side response.
Instead, evaluate the use cases:
- Are you're fetching data only to show it in the UI? Use Screen data Aggregates and bind the result to your Widgets. Aggregates run asynchronously and in parallel, and your UI updates when the Aggregates finish fetching data.
- Are you manipulating data before showing it? Use Data Actions to manipulate the data to fit your needs, and then send the right information back to the client side.
To get data to a Screen in a Reactive app, right-click a Screen in the Elements Tree and select Fetch Data from Database or Fetch Data from Other Sources.
Don't forget to address Max Records in the target app, as its value is calculated in the source app from the widgets they're bound to.
Aggregates with no dependencies (or dependents) can be moved to Screen Aggregates. If the Aggregate Max Records is empty, it should be entered as the value:
- If bound to a single widget, of the Line Count of that widget
- If bound to multiple widgets, of the highest expected Line Count
When migrating the Refresh Data node, you need to analyze the dependencies between the data sources and how data is used in the Screen. You also need to check that the client side in the target module receives only the data it requires, ensure that your app is not exposing confidential information.
If the refreshed Data Source doesn't have dependencies, then the logic should be isolated, in a Screen Aggregate or a separate Data Action - and then refreshed.
If there are dependencies between the data sources, consider using the Fetch property of Aggregates (On Start and Only On Demand) and creating dedicated async patterns. You can check an example in the document Implement asynchronous data fetching using Aggregates.
Send Email Tool
This feature is currently not available in Reactive, but we're working on it. In the meantime, here are some workarounds:
- Reuse logic from your Traditional App
- Use a third-party service
Keep in mind the app performance when refactoring your Screen Action logic. Identify which logic should run on the server, and which on the client. You should then separate the logic into Server Actions and Client Actions. You may need to have input parameters for your Server Actions, depending on how you change the Screen in order not to use the Preparation.
You can't use the following actions directly from UI, as they imply direct access to the server runtime:
- System Actions where there's no client-side alternative, which includes transaction- and login-related Actions.
- Aggregates on server Entities.
If there's no equivalent Client Action to the Server Action you want to use, consider:
- Wrapping a System Action into a Server Action, keeping in mind performance implications.
For more ideas and examples, check the Reactive Utilities Forge component by our community.
Check Web Block.
Notify Get Message
Check Web Block.
When creating new UI experiences in the target app you should keep in mind the client-side development paradigm and the differences outlined in this section.
These are the considerations applicable to all Widgets.
Min. Height, Width, Height
In Reactive, use the "style" extended property. For example:
min-height: 15px;. If Width is set in chars, set the length in em as an extended style property.
The Style property is Expression in Reactive Web App.
You should not use the Visible property of the widgets to hide sensitive information.
In Reactive Apps the interface is primarily rendered on the client side, before the checks against the defined user roles. Be careful when migrating Widgets to the target app and check if the right information is delivered to the right client-side.
Widget comparison overview
This section contains notes about the widget differences. Here is the table with the Traditional Widgets and their Reactive Web counterparts, where available.
|Traditional Web||Reactive Web|
|Editable Table||Table with inputs inside cells|
|Radio Button||(in development)|
Convert the Display property to a "style" extended property with
if(<expression>, "", "display: none;").
You should use Dropdown Widget in your target apps instead of Combobox. You can use Options Content with Text Only to generate
<span> elements or Custom to generate
<div> elements for more flexibility.
Expressions should be straightforward to implement. If you used Escape Content option in Traditional App, for inserting HTML tags use the HTML Element in Reactive.
Check an example on how to Validate Form Inputs.
Ensure that you're not bringing confidential information to the conditions of the If Tool. For example, if you have information visible only to admin on the server-side, reusing the same logic in the target app could fetch confidential information to the client-side UI of the Reactive app.
The Label property is not available in the Reactive Image Widget. Use the Extended Property "title" to set the title of the image.
You can use Reactive Input Widget or TextArea Widget. If you need a password field, use Input Widget and set the Input Type property to Password.
In the Reactive App, the Label Widget contains only Text property with a text value.
Use List in the target app. The Line Separator impacts the widget generation:
- None: The Mode property set to Custom, with a span tag.
- New Line: The Mode property set to Custom, with a span tag and a trailing Text widget with a "\n" text.
- Bullets: The Mode property set to Custom. Needs an enclosing HTML Element with a span tag. You also need the ul tag with another HTML Element (with the li tag) for each list element.
This Widget is currently in development.
See Table Records
See Table Records
Rich Widgets overview
Rich Widgets are legacy built-in widgets that are not supported in OutSystems UI. We replaced them with more modern implementations in Reactive App. Use this table for guidance on how to replace Rich Widgets in your new app.
|Application Switcher||Change Data Source to Data Action.|
|Dropdown Menu||Use the Dropdown Widget from OutSystems UI.|
|Feedback Ajax Wait||No Ajax in Reactive, review the UX and update it.|
|Feedback Message||Use Message Tool on client side, fetching message from server if needed.|
|Icon||Use new Icon, with Size instead of Font Size.|
|Input Autocomplete||Migrate to an Input Widget with OnChange login or DropdownSearch from OutSystems UI.|
|Input Calendar||Use the Date Picker Widget from OutSystems UI.|
|List Counter||Create your own or check the Pagination Widget from OutSystems UI.|
|List Navigation||Check the Pagination Widget from OutSystems UI.|
|List Sort Column||Scaffold a Table with sorting and inspect the logic, reusing and adapting as needed.|
|Layout Chicago / Email / Popup||Use Blocks.|
|Layout London||Use Block and light UI.|
|Popup Editor||Use PopOver Menu and Popup.|
|Popup Info Balloon||Use PopOver Menu and Popup.|
Here are the migration notes about Widget Events.
The functionalities of On Click set of properties in Traditional Web can be implemented in Reactive app like this:
- Destination - point to Screen Action or Screen
- Validation - use the Built-in Validation to check on the client side, but also implement validation on the server side before committing any values to the database
- Method Submit or Ajax Submit - set to Screen Action calling a referenced Server Action from the source app (reuse old logic)
- Navigate - redirect to another destination