Row Action with inline snippet to update field

  • 3
  • Question
  • Updated 4 months ago
  • Answered
How can I define a Row Action that updates a field within a row. I've attempted the following, but I'm getting a 'Visualforce Remoting Exception: Attempt to de-reference a null object' error.

Row Action, Action Type = Custom, using inline snippet as below:

skuid.snippet.registerSnippet('orderLineItem.CalculateCost', function(params) {
var price = params.item.row.Price__c;
var quantity = params.item.row.Quantity__c;
var cost = price * quantity;
params.model.updateRow(params.item.row, 'Cost', cost);
params.model.save();
};

Also, is it possible to just update the value when in edit mode, and let the user initiate the save. I commented out the "params.model.save()", but nothing is updated in the UI?
Photo of Evan Hollonds

Evan Hollonds

  • 90 Points 75 badge 2x thumb

Posted 5 years ago

  • 3
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Hi Evan,

The reason you're getting the Remoting Exception is that you're trying to update a Field that does not exist --- "Cost" should be "Cost__c", I believe.

As far as getting the Cost__c column in your Table to automatically update whenever Price and Quantity are updated by the user, this is a very good use case for a Custom Field Renderer. Basically, go to your "Cost" column in your table, and change the "Field Renderer" to be "Custom", then for "Render Snippet", enter "CostRenderer". Then, go to your JavaScript Resources, and create a new "Inline (Snippet)" resource, named CostRenderer, with the following Body:




var field = arguments[0],
value = arguments[1],
$ = skuid.$,
dt = field.metadata.displaytype;

if (field.mode != 'edit') {
skuid.ui.fieldRenderers[dt][field.mode](field,value);
} else {
skuid.ui.fieldRenderers[dt].edit(field,value);
}

var calculateCost = function(){
value = (field.model.getFieldValue(field.row,'Quantity__c',true) || 0)
* (field.model.getFieldValue(field.row,'Price__c',true) || 0);

value = Math.round(value * 100) / 100;

field.model.updateRow(
field.row,
'Cost__c',
value
);

if (field.mode == 'edit') {
field.element.find('input').val(value);
}
};

calculateCost();

var listener = new skuid.ui.Field(field.row,field.model,null,{fieldId: 'Cost__c'});
listener.handleChange = function(){
calculateCost();
};
field.model.registerField(listener,'Price__c');
field.model.registerField(listener,'Quantity__c');



The result should be that whenever you change the Price or Quantity fields in your table, your Cost field will be updated.

Photo of Glenn Elliott

Glenn Elliott, Champion

  • 7,738 Points 5k badge 2x thumb
This is nice. But what if we don't want to write the calculated value to the database? e.g. if we just want to display the cost (calculated as price x quantity), but not store it? It's almost like a custom field renderer for a template field, but that's not possible, is it?
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
Glenn,  Here is a shot in the dark.

You can create a custom renderer on another field (Which you use just as a dummy placeholder)  and in that renderer call the fields and do your calculations in Javascript.   I'm thinking the dummy field could be set to read only so that a save is not attempted on that field when you save the other fields in the table. 


Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Rob's approach should work --- just add a custom field renderer on a dummy field (e.g. the Id field) and then in your custom field renderer display the calculated value, and don't provide an "Edit" mode renderer at all. Just unilaterally output a number/currency in read mode, e.g.

var field = arguments[0],   
   value = arguments[1],
    $ = skuid.$;

field.metadata = {
   displaytype: 'CURRENCY',
   accessible: true,
   precision: 8,
   scale: 2,
   required: false,
   nameField: false,
   label: 'Cost'
};
field.required = false;

var calculateCost = function(){
    value = (field.model.getFieldValue(field.row,'Quantity__c',true) || 0)
        * (field.model.getFieldValue(field.row,'Price__c',true) || 0);
    value = Math.round(value * 100) / 100;
    field.element.empty();
    skuid.ui.fieldRenderers.CURRENCY.readonly(field,value);
};

calculateCost();

var listener = new skuid.ui.Field(field.row,field.model,null,{fieldId: 'Quantity__c',register:true});
listener.handleChange = function(val){
    calculateCost();
};
field.model.registerField(listener,'Price__c');


Photo of Glenn Elliott

Glenn Elliott, Champion

  • 7,738 Points 5k badge 2x thumb
Thanks gents. That looks like a plan. In fact we tried this yesterday using the id field, but it kept displaying the id rather than our calculation. But looking at your code here I can see some areas where I think we've gone wrong, so will give it another crack.
Photo of Glenn Elliott

Glenn Elliott, Champion

  • 7,738 Points 5k badge 2x thumb
Yep, this works beautifully. Love it.

Now, for bonus points ...

My actual use case is to calculate a Budget Amount and an Actual Amount, which I do using the above technique, but then also calculate a Variance which is simply Budget Amount minus Actual Amount. In the Variance custom renderer, I don't really want to have to repeat the calcs for budget and actual, I want to refer to those values already calculated in the other two fields. Is that possible? I suspect not, given that Budget, Actual and Variance all render simultaneously.
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
As usual there are definitely multiple ways to do this. One idea that comes to mind is to put your JS code in a static resource.  Each field in the table could reference a snippet defined in that common JS file.  Those snippets could in turn reference common calculation functions also contained in the file.  Just a matter of code abstraction.

Another idea that might not meet you stringent UI standards would be to combine all three items in a single field.  You have well used the "stacked field" model in your tables.  This would allow a single snippet to be do your calculations and then display the three values effectively.  
Photo of Jack Sanford

Jack Sanford, Champion

  • 8,322 Points 5k badge 2x thumb
I'm trying to do the same thing and it's not working. I've got a join object FCCA, and I want to update a field on my FC object with a field from my CA object. 

I've got a table of FCCA's and here's my row action snippet, in its entirety:

skuid.snippet.registerSnippet('updateMERS', function(params) { 
var caMERS = params.item.row.CustomerAccount__r.MERSSearchCompleted__c; 
params.model.updateRow(params.item.row, 'Foreclosure__r.MERSSearchComplete__c', caMERS); 
params.model.save(); 
});
I'm very new here so I may be missing something. 

Ideally this would be a Mass Action, and happen to all the selected rows, is that possible?

Here's the error I'm getting:


The line referred to in the error is var caMERS = params.item.row...

I may be missing something very basic here. 
Photo of Jack Sanford

Jack Sanford, Champion

  • 8,322 Points 5k badge 2x thumb
Same error, slightly different console log. 

I'm not sure I made it clear before, my table is full of rows of the 'FCCA' model which is a join object with the API name of FCCAAssociations__c. It looks like it shouldn't matter what the table's model is since the code it just pulling the row, but just in case.



Here's VFRemote.js:117

$VFRM.Util={log:function(a,b){if(!("undefined"===typeof console||!console.groupCollapsed||!console.log||!console.groupEnd))if("undefined"!==typeof b&&null!==b)try{console.groupCollapsed(a),console.log(b),console.groupEnd()}catch(c){}else try{console.log(a)}catch(d){}},warn:function(a){if("undefined"!==typeof console&&console.warn&&a)try{console.warn(a)}catch(b){}},error:function(a,b){if("undefined"!==typeof console&&console.error&&a)if(b)try{console.error(a,b)}catch(c){}else try{console.error(a)}catch(d){}},
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Hmm, I think I'd have to see your full data model to evaluate further, which is probably beyond what I can offer on the support community --- I don't think your fields being Date values should matter here, as from what I can tell, we're just copying a Date from a field on one object to a field on a related object, so as long as the original Date value is good, it should be fine. 
Photo of Jack Sanford

Jack Sanford, Champion

  • 8,322 Points 5k badge 2x thumb
The original Date value is good, i've got it showing up in the table to make sure there's a value. Maybe it's because it's having to go to a related object to find the value? Maybe we need to use the model for the related object with the existing value ('CustomerAccount') instead of using CustomerAccount__r ?

Thanks for all your help so far. Maybe it's something I can work out in the Chicago training in a couple weeks.

Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Ah yes, the Chicago training would be a good time to work it out :) My guess is that I'm misunderstanding exactly how your data model works with the association object.
Photo of Jack Sanford

Jack Sanford, Champion

  • 8,322 Points 5k badge 2x thumb
FCCAAssociations__c is a child object with two Master-Detail relationships, one to CustomerAccount__c and one to Foreclosure__c

It may be that it would work better to have a table of CustomerAccounts, then look up the Foreclosure__c WHERE Id IN Foreclosure__c [field] FROM FCCAAssociations__c WHERE CustomerAccount__c [field] =: {{{Id}}} [Id of row]

Hmm, starting to seem like maybe I can just do an Apex Class and a standard SF button...
Photo of Aboli Shinde

Aboli Shinde

  • 90 Points 75 badge 2x thumb
Hi Team,

I just want to convert the business account to person account on skuid, using button.

Can you please help me to provide me the code to update the recordtypeId on Account ?

Thanks