Potential compatibility issues with Custom Field Renderer JavaScript code in Summer 14

  • 3
  • Announcement
  • Updated 4 years ago
With the Skuid Summer 14 release, we've made some changes to Skuid to improve automatic synchronization of Model data with visual Components when the Model data changes are initiated from JavaScript.

In a nutshell, the changes amount to this: if you have a Model called Accounts, and a Table that is bound to that Model, and you then make changes to data in the Accounts Model from JavaScript using the following lines, the Table will rerender the appropriate areas within itself to reflect the changes:

var AccountsModel = skuid.model.getModel('Accounts');
var row = AccountsModel.getFirstRow();
AccountsModel.updateRow(row,'Industry','Manufacturing');

Previously, to get the Table component to show the changes, you would have had to either 
 - (a) Save the Model
 - (b) Rerender either the whole Table or parts of the Table

With Summer 14, we've eliminated the need to do either (a) or (b). If you call updateRow(), your changes will be reflected in the UI, immediately. You don't have to know which parts of the UI to refresh, Skuid will handle this for you.

However, while we believe that this is the expected behavior and is a change for the better, this may have some side effects on existing custom JavaScript code that has been written, in particular, on Custom Field Renderers.

There are 3 main changes we recommend making to your custom JavaScript code in Custom Field Renderers:
  1. Ensure that updateRow() is only called by direct user action or under well-defined circumstances
  2. Provide an initiatorId whenever using updateRow()
  3. Remove any code that manually re-renders UI Components just to get them to reflect your Model data changes
Let's break these down.

1. Ensure that updateRow() is only called by direct user action or under well-defined circumstances

One common pitfall in writing Custom Field Renderers is trying to provide a default value if none yet exists. For instance, we've seen field renderers looking like this:

var field = arguments[0],
    value = arguments[1];

field.model.updateRow(field.row, field.id, '');

The problem with this Snippet is the updateRow() call in bold. It is unconditionally called every time the Custom Field Renderer is called. However, this is easy to fix by simply wrapping this in some well-defined conditional logic to only perform this call to updateRow() if there is not yet a value, e.g.

if (!value && value!=='') {
   field.model.updateRow(field.row, field.id, '');
}

2. Provide an initiatorId whenever using updateRow()

Not all Custom Field Renderers will need to make calls to updateRow() -- for instance, if you are simply extending a default renderer, you will not need to make your own calls to updateRow() at all, these will be done automatically by Skuid.

However, if you are calling updateRow() from a Custom Field Renderer, you should always, always, pass in an initiatorId parameter to tell Skuid which UI component "initiated" the Model update. This is important because we DON'T want Skuid to rerender the UI components that initiated in a change directly after the change was initiated --- this is wasteful and can result in infinite loops if your code is not written correctly. In the case of Custom Field Renderers, your field renderer already knows about the Model change that occurred when it was the initiator --- it doesn't need to be told again! 

Here's how to solve this: wherever you call updateRow within your Custom Field Renderer, you need to pass in an "initiatorId" parameter containing the unique Id of the Field. Essentially this is saying, "Skuid, there is a specific Field that initiated this change --- Me. I know about the change, and my input elements already reflect the data change. So don't re-render me!" The "Me" part of the equation is provided by field._GUID, the unique Id of the skuid.ui.Field. 

So code that formerly looked like this:

field.model.updateRow(field.row,field.id,"foo bar");

Should be rewritten to look like this:

field.model.updateRow(field.row, field.id, "foo bar", { initiatorId: field._GUID });

3. Remove any code that manually re-renders UI Components just to get them to reflect your Model data changes

As described above, there are a couple of ways that previously were necessary to get Skuid to immediately reflect in the UI changes made via JavaScript to data in a Model. With the Summer 14 release, this code can and should be removed in order to optimize the speed of your page.  

One of the most common approaches in Custom Field Renderers was, in the past, to do something like this:

// Change our value
field.model.updateRow(field.row,field.id,'New value');

// Force Skuid to rerender Table/Field Editor rows/columns bound to this Field
$.each(field.model.registeredLists,function(){
    $.each(this.renderedItems,function(){
        if (this.row.Id === field.row.Id) {
           $.each(this.fields,function(){
               if (this.id===field.id) this.render();
            });
        }
    });
});

As of the Summer 14 release, all of the code starting with "Force Skuid to rerender..." can and should be removed.

CONCLUSION

If you have any questions about how to adjust your code, please post on the Community or contact support@skuidify.com
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,262 Points 20k badge 2x thumb

Posted 4 years ago

  • 3

Be the first to post a reply!