A common frustration for adding code in the init requests on a Freedom UI page is that the crt.HandleViewModelInitRequest request often doesn’t have the values on the page loaded yet, or if setting values, they get overwritten as the page loads the defaults for everything (which might be a default causing values you’ve set to be lost). This article will show how to wait for the model to be completely loaded and ready before you proceed with reading or setting any values for the page.
The page’s init request typically looks like this:
{
request: "crt.HandleViewModelInitRequest",
handler: async (request, next) => {
// do stuff
return await next?.handle(request);
}
}
The problem is that if you attempt to read any page attributes they are likely (definitely) not yet loaded at this point. Also, if you set page values here, they will get overwritten as the page is initialized when the record is loaded (which is also the case for a “new” record as well). Creatio states that this was not technically intended to be used as an equivalent of onEntityInitialized (from classic pages), however there has not been any other options to be used, leaving this as the only choice.
In the past, I’ve used some hacks, such as using setTimeout to delay things and allow them to load. However, in recent versions (as of 8.1.3 and higher) you can subscribe to all the model events on the page and then wait for the model to be loaded before you proceed. You can subscribe to all the model events using the following:
{
request: "crt.HandleViewModelInitRequest",
handler: async (request, next) => {
await next?.handle(request);
request.$context.events$.subscribe((async (evt) => {
// use evt here to get state
}));
}
}
Subscribing to this will give us a lot of different event details. The ones we are after are the following:
- evt.type = A type of “finish-load-model-attributes” will let us know that the model is finished loading (note this event happens many times for each attribute)
- evt.payload = The payload will include the attributes that have been loaded so far. Meaning, to see if “UsrName” is loaded, we can simply check if evt.payload.UsrName exists yet. Note, that this will only have values that have been added to the page, not other column values not added to the page.
So, if in our init request we want to get the record’s Id value and UsrName column, we would listen for that using the code below. Additionally, it might be good to also check which mode our page (model) is in. There’s a few ways to check this, but we’ll use the request.$context.getPrimaryModelMode() function, which will give us “create” for add mode, or “update” for edit mode.
{
request: "crt.HandleViewModelInitRequest",
handler: async (request, next) => {
await next?.handle(request);
request.$context.events$.subscribe((async (evt) => {
const modelMode = await request.$context.getPrimaryModelMode();
if (modelMode === "update" && evt?.type === "finish-load-model-attributes" && evt?.payload?.UsrName && evt?.payload?.Id) {
// we can now get the values since they are now loaded
const recordId = await request.$context.Id;
const name = await request.$context.UsrName;
}
}));
}
}
In the above code, we’re receiving the model events and waiting for the following conditions to be true:
- The page is in “update” mode (meaning we’re editing a record)
- The model event type is “finish-load-model-attributes”
- The event payload contains the attributes that have been loaded in the model. We can use this to check of the attributes we want to read are loaded (in this case, Id and UsrName). NOTE: The evt.payload does NOT contain the actual values of the fields. It will contain a value of true if the column has been loaded in the model.
Or similarly, to set values if the page is in add mode:
{
request: "crt.HandleViewModelInitRequest",
handler: async (request, next) => {
await next?.handle(request);
request.$context.events$.subscribe((async (evt) => {
const modelMode = await request.$context.getPrimaryModelMode();
if (modelMode === "create" && evt?.type === "finish-load-model-attributes" && evt?.payload?.UsrName) {
request.$context.UsrName = "New record"
}
}));
}
}
Even though we’re in add (create) mode and not wanting to read a value from an attribute on the page, we still need to check that the field(s) we’re setting are loaded or they will get overwritten when the “new” (or empty) record is loaded.
NOTE: I am hopeful that this approach is only temporarily needed. I am expecting that at some point we’ll have a proper request for when the model is fully loaded. As you would expect, using this approach could become tedious since you need to check for every field you’re setting or reading. My expectation is that a future version of Creatio will have a request specifically for what we’ve used onEntityInitialized for in classic pages. At least until that time, this approach will let us handle this event without resorting to unnecessary hacks.




Hi Ryan, great article as always!
I recently had to deal with this really annoying behavior and I also looked for a sort of “workaround” without using the timeouts…
The solution I found is the following:
1. I set the attributes normally in the HandleViewModelInitRequest
2. I reset the “old” values after Creatio overwrites them in the HandleViewModelAttributeChangeRequest, like this:
//workaround to avoid page resetting values to null (at start) if (request.silent === true && request.value === null) { const globalCustomAttributes = ["firstAttribute", "secondAttribute"]; if (globalCustomAttributes.includes(request.attributeName)) { request.$context[request.attributeName] = request.oldValue; } }What do you think about this approach?
PS: If the attribute type is text, the check that needs to be done will be:
request.value === “”
Thanks, this was useful. I found I often needed to unsubscribe once the ID had loaded
const sub = request.$context.events$.subscribe((async (evt) => {
if (evt?.type === “finish-load-model-attributes” && evt?.payload?.OmniChatDS_Id ) {
//unsubscribe when ID loaded
sub.unsubscribe();