Freedom UI: HasUnsavedData = true on page load due to ForwardReference attributes and calculated fields — how to suppress it?
Dear colleagues,
I have a Freedom UI page (Creatio v8.3.2.4166) that activates the Save button immediately on load, without any user interaction.
After extensive debugging, I identified three contributing factors:
1. ForwardReference attributes in modelConfigDiff. The page has multiple attributes of type "ForwardReference" resolving data from two related entities. These are all read-only fields. Example:
"NcsRelatedEntityNcsDescription_abc123": { "path": "NcsRelatedEntity.NcsDescription", "type": "ForwardReference" }
When these attributes resolve asynchronously after page load, the framework registers them as user changes and sets HasUnsavedData = true.
2. RichTextEditor controls with <strong>needHandleSave: true</strong> — for some reason the page wizard generated them with true by default, even though all of them are readonly and display data from a related entity, not from the current object's own fields. We changed all of them to needHandleSave: false, which helped partially but did not fully resolve the issue.
3. Calculated field handlers. The page has a handler that calculates age from a birth date field (and another similar purpose handlers):
{ request: "crt.HandleViewModelAttributeChangeRequest", handler: async (request, next) => { if (request.attributeName === 'PDS_NcsBirthDate_abc123') { const birthDate = await request.$context.PDS_NcsBirthDate_abc123; if (birthDate) { const today = new Date(); const birth = new Date(birthDate); let age = today.getFullYear() - birth.getFullYear(); if (today.getMonth() < birth.getMonth() || (today.getMonth() === birth.getMonth() && today.getDate() < birth.getDate())) { age--; } request.$context.PDS_NcsAge_xyz789 = age; } else { request.$context.PDS_NcsAge_xyz789 = null; } } return next?.handle(request); } }
When the page loads with an existing record, PDS_NcsBirthDate_abc123 fires as an attribute change event during model initialization, the handler writes PDS_NcsAge_xyz789, and that write marks the page dirty — even though both values were already saved in the database.
The same pattern applies to a handler that composes a full name from first and last name fields, and to the RichTextEditor ForwardReference fields that display read-only rich content from a related entity.
What we tried:
Approach 1 — Subscribing to events$ in HandleViewModelInitRequest to detect finish-load-model-attributes, setting a window._NcsPage_pageReady flag, and guarding all attribute change handlers behind it. This correctly prevents our own handlers from triggering dirty state, but does not prevent the framework itself from setting HasUnsavedData = true when ForwardReferences resolve.
Approach 2 — Adding a HandleViewModelResumeRequest handler that forces HasUnsavedData = false after the model is ready. This fires too early — ForwardReferences resolve after Resume, so they overwrite the reset.
Approach 3 — Using setInterval (up to 35 cycles × 200ms = 7 seconds) inside the Init handler to repeatedly reset HasUnsavedData = false. This does not work because the request.$context captured in the Init handler closure becomes stale after initialization — Creatio replaces the ViewModel reference, so we are resetting a dead object.
Approach 4 — Intercepting all HandleViewModelAttributeChangeRequest as the first handler in the chain, calling await next?.handle(request) and then forcing HasUnsavedData = false on the fresh request.$context. This works during the initial load phase (while _pageReady = false), but ForwardReferences continue resolving after _pageReady = true, still triggering dirty state.
Approach 5 — Combining Approach 4 with permanent suppression for all known ForwardReference attributes (since they are always readonly):
{ request: "crt.HandleViewModelAttributeChangeRequest", handler: async (request, next) => { const result = await next?.handle(request); const isForwardRef = request.attributeName?.startsWith('PDS_NcsRelatedEntity'); if (!window._NcsPage_pageReady || isForwardRef) { request.$context.HasUnsavedData = false; } return result; } }
This still fails — HasUnsavedData keeps flipping back to true after each reset.
Questions:
- Is there a supported Freedom UI pattern to prevent
HasUnsavedDatafrom being set by ForwardReference resolution on page load? - Is there a lifecycle event or request that fires reliably after all ForwardReferences have fully resolved?
- Is there a way to prevent a handler that writes a calculated field (like age from birth date) from marking the page dirty when it fires during initialization?
- Is there any way to mark specific attributes as non-dirty-tracking so the framework ignores their changes for save state purposes?
Any guidance from the community would be greatly appreciated. Thank you very much
Regards
Julio Falcón
Like
Hi Julio,
For the change event handler, you can use request.silent == false. The request.silent indicates if the change was from a model load (silent=true) vs a user change (silent=false).
{ request: "crt.HandleViewModelAttributeChangeRequest", handler: async (request, next) => { if (request.attributeName === 'PDS_NcsBirthDate_abc123' && !request.silent) { // only handle user changes //... } return next?.handle(request); } }
Ryan
Hi Julio,
For the change event handler, you can use request.silent == false. The request.silent indicates if the change was from a model load (silent=true) vs a user change (silent=false).
{ request: "crt.HandleViewModelAttributeChangeRequest", handler: async (request, next) => { if (request.attributeName === 'PDS_NcsBirthDate_abc123' && !request.silent) { // only handle user changes //... } return next?.handle(request); } }
Ryan
For question 3, you can use the method marked as being the answer in this thread: https://community.creatio.com/questions/it-possible-make-changes-attributes-code-freedom-ui-silently
Unsure about the others - I haven't seen forward references causing the page to think there's changed data that needs saving in earlier versions of Creatio, so if that is causing the issue it would sound like a bug in 8.3.2?
I'm also not aware of a supported way of resetting the page/specific attributes so that Creatio doesn't think they have changed or will never pay attention to changes in them. It would be nice to have this option though.