How can I obtain the context row in a javascript snippet?

  • 1
  • Question
  • Updated 2 years ago
  • Answered
I have a table displaying multiple rows from a custom object.
The edit action is a popup which displays the selected row in a field editor.
The popup also has a page title component with a Save button.
The page title and the field editor both reference the same model.
The first Save action is to run a javascript snippet.

How can that snippet determine which row is the active one on the popup?

The snippet is passed an argument, "arguments[0]", which is an object.
Developer Tools console displays the object as:

 Object
  component: p
  context: Object
  editor: c
  initiatorId: 63
  model: W
  row: Object
  __proto__: Object

The context, model, and row nodes can be expanded. For example, row opens up as:

 row: Object
  CreatedBy:ObjectCreatedById:"005t0000000HfckAAC"
  CreatedDate:"2016-08-18T22:34:18.000+0000"
  Crush__c:true
  Frequency__c:"1"
  Id:"a1fr00000004SszAAE"
  Id15:"a1fr00000004Ssz"
  <etc.>

However, in spite of the fact that the popup was displaying and editing the second row from the table, the row object from the argument is the content of the first row from the table.

And although the "context" object might be expected to provide context, it does not. It also has model and row objects, but they only provide another instance of the same data from the first row of the table.

The model node does provide some help. It can be expanded to show a "changes" object, and this object is the context row that is being edited:

 model: W
  ...
  changes: Object
    a1fr00000004M0DAAU:Object
        Frequency__c:"2"
  ...

And my use case does in fact need me to compare "changes" to "originals" (which is also found in the model node):

 model: W
  ...
  originals: Object
    a1fr00000004M0DAAU:Object
        Frequency__c:"1"
  ...

My question is how do I obtain that M0DAAU Id as a javascript value?

I can assign var modStruct=arguments[0].model, which is the object W.
And I can assign var frequency=modStruct.originals.a1fr00000004M0DAAU.Frequency__c, which is the value 1.
But arguments[0].model.originals is an object, and arguments[0].model.originals[0] is undefined.
And arguments[0].model.originals.Frequency__c is also undefined.

I am not seeing a way to assign that context Id to a javascript variable so I can do this:
  var theId = ???;
  if ( modStruct.changes.theId.Frequency__c > modStruct.originals.theId.Frequency__c ) {...}
Photo of Mike Dwyer

Mike Dwyer

  • 3,390 Points 3k badge 2x thumb

Posted 2 years ago

  • 1
Photo of Chandra V

Chandra V, Champion

  • 6,966 Points 5k badge 2x thumb
var params = arguments[0],   
contextRow = params.item ? params.item.row : params.row,
contextModel = params.model,
$ = skuid.$;

var contextId = contextModel.getFieldValue(contextRow, 'Id', true);


// do what you want here with contextRow and contextModel and contextId
Photo of Mike Dwyer

Mike Dwyer

  • 3,390 Points 3k badge 2x thumb
Thanks, Chandra. But that just returns the first row of the model. The contextId is the same Id that would be obtained from params.row.Id. I think you've simply obtained a copy of it from another part of the skuid model.

I have found a solution, however, from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects which poinyed me to the Object.keys() method for traversing object properties.

In my case, I can use

 var theId = Object.keys(arguments[0].model.originals);
 var oldFrequency = arguments[0].model.originals[theId].Frequency__c;

In your code, it would be

  var contextId = Object.keys(params.model.originals);
  var oldFrequency = params.model.originals[contextId].Frequency__c;


(For what it's worth, theId and contextId are arrays of the name/key of the originals object. It might be more appropriate to use theId[0].)
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,076 Points 10k badge 2x thumb
Hello Mike -

The context property of arguments[0] should contain the row that is "in context."

The reason that you are likely getting the "first" row in the model and not the "in context" row is because you do not have the context property of the Page Title configured.  Please try the following for the Page Title (the same setting should be applied to your field editor if it isn't already):

1) Open Page Composer
2) Go to the popup
3) Select the Page Title
4) Choose "Context" menu in the properties pane
5) Add a context item for "Id of record = Id of row in context" or whatever is appropriate for your situation

Once you do this, arguments[0].context.row should be the row that you are expecting.

As an aside, please note that changes and originals are objects whose properties are the rowId for the corresponding originals/changes.  If multiples rows are edited, then there will be multiple properties on those objects so you need to ensure that you access the correct row by first grabbing the originals/changes based on the Id of the row you are targeting which you can obtain via context.row.Id or using the getFieldValue API.

Hope this helps!
(Edited)
Photo of Janick

Janick

  • 916 Points 500 badge 2x thumb
Hey Mike

I think there is another way to do this:

1. create a new model with condition to fill this model with the clicked row (on edit)
2. The Popup would then show the data from this new model (which has only 1 row)
--> Since this model does only have 1 row now, you can run the Snippet on this model
3. On save, close popup and requery the model with all data in it.

Good luck!
Photo of Mike Dwyer

Mike Dwyer

  • 3,390 Points 3k badge 2x thumb
Barry -

Thanks! I should have known. I would have set context for a table, but context is generally not necessary for a page title or a field editor. Setting it for the javascript interface makes sense, and that word "context" was staring me right in the face.

As to changes/originals, the warning is appreciated. In this page I don't need to worry about multiple records, but elsewhere someone might.

I would mark yours as the Correct Answer, but I don't see a button to do that.
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,076 Points 10k badge 2x thumb
Glad that worked for you Mike!
Photo of Mike Dwyer

Mike Dwyer

  • 3,390 Points 3k badge 2x thumb
Janick -

Thanks for the suggestion. I had already considered that I might need to do this, but the extra model would add even more overhead to an already-complex page. There are times when the 1-row model is needed anyway - usually for adding new rows - and your method would flow naturally there. But I think Barry's got the most generally applicable solution.

- Mike
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,076 Points 10k badge 2x thumb
Another use case for a 1-row model is when you would want to allow the user to edit a record but also have a way to "cancel" the changes without affecting that same data in another model.  For example, you have a table of accounts and popup to edit that account.  By loading a new 1 row model, you can cancel changes without effecting the model on the table or adopt the row back in to the model that the table is using if the user "saves."

That said, as Mike mentions, in general, adding models to pages adds overhead to page performance as well as costs/risks in development/design.  In the case where you have lots of fields, that's a lot of extra config that needs to be maintained (every time you add a field in one place, you need to add in another).

Definitely use cases for an extra model but should generally be avoided unless necessary.