Ability to attach js to navigation Actions

  • 1
  • Question
  • Updated 5 years ago
  • Answered
Maybe this is possible already but it would save a lot of headache if I could attach code to Action Buttons of navigation type.

The two use cases that I've come across have to do with the Wizard component, but I'm sure there are more.


  • Validating Wizard before proceeding to next step

  • Running logic when before proceeding to next step



If you could attach a callback function to execute before & after that would just be icing on the cake! The before function could return true/false to indicate if the wizard should proceed to the next step (to use for validation)
Photo of Charlie Jonas

Charlie Jonas

  • 670 Points 500 badge 2x thumb

Posted 5 years ago

  • 1
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
You can absolutely invoke code on action buttons.  Almost all of the action buttons in wizards, page titles, table actions etc allow a "Custom" type which then exposes a snippet name property.  Write your Javascript file in our resources section (or include a reference there to some external file) and you can go to town.  

Here is a previous forum post where an example of this is discussed:  http://community.skuidify.com/skuid/topics/wait_for_an_update_to_complete_and_then_retrieve_the_firs...
Photo of Zach McElrath

Zach McElrath, Employee

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

Yes, this is possible.

Wizard Step Actions can be of type "Custom", which lets you execute a Skuid Snippet. So you enter the name of the Snippet, and then you define the Snippet from the JavaScript Resources tab, by creating a new JavaScript Resource of type "Inline(Snippet)". All Skuid Snippets are passed context params/arguments relevant to the Snippet scenario --- so for a Wizard Step navigation action, you are handed a reference to the Step object you are on, as well as to the parent Wizard object itself.

So first off, you add a new Action button to a Wizard Step, then you set its type to Custom, and enter the name of a Skuid Snippet, e.g. "GoToStep2":



Then in your JavaScript Resources, create "GoToStep2" as a new JavaScript Resource of type "Inline (Snippet)":



For my example, we've got a New Opportunity Wizard. On the first step you enter details about the Opportunity. Then the "Go to Step 2" button runs our GoToStep2 Skuid Snippet, which I'll post here and then explain:



var params = arguments[0],
step = params.step;

// Make sure that a Pricebook was selected on our Opportunity Model
var OpportunityModel = skuid.model.getModel('Opportunity'),
Opp = OpportunityModel.getFirstRow();

if (!OpportunityModel.getFieldValue(Opp,'Pricebook2Id')) {
alert('You must select a Pricebook before proceeding.');
} else {

// Create a default row in our LineItems model,
// (if there is not already one)
// now that we have a Pricebook selected
var LineItemsModel = skuid.model.getModel('OpportunityLineItems');
if (LineItemsModel.data.length === 0) {
var newLineItem = LineItemsModel.createRow({
additionalConditions: [
{ field: 'Quantity', value: 1 }
]
});
}

step.navigate('step2');
}



This Snippet does some checking on the values in the new Opportunity record: specifically here, it makes sure that a Pricebook has been selected for the Opportunity. Of course this is just an example, you could have pre-selected the Opportunity's Pricebook, but it serves the example.

If the user has not selected a Pricebook, the user gets an error, and no step navigation is performed.

If the user HAS picked a pricebook, though, we do 2 things. First, we check to see if there is a record in our LineItems model yet. If not, we create one, passing in some default field values. Then, we navigate to Step 2 by calling the "navigate()" method of the Step object that was handed to us in our Snippet's arguments.

If you're wondering about what arguments are handed to Snippets in different contexts, then best way to find out is to do a console.log on the arguments, e.g.

console.log(arguments);

I usually immediately declare a "params" variable in Skuid Snippets, set to the first argument passed in to the Snippet, which is usually an object.

Here's what the Wizard basically looks like:





Photo of Charlie Jonas

Charlie Jonas

  • 670 Points 500 badge 2x thumb
BEAUTIFUL. Didn't know that it would be passed to the snippet.

On a side note... once again... wish the documentation for these things was a bit more concrete, but I understand.

Plus, I can't complain when you provide great answers like this within minutes.
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
I wanted to answer the "running logic" part of your question first before addressing the "validating user input" in more detail.

While you can manually validate user input in your Snippet logic, as we did above checking that the Pricebook2Id field had been populated, you might want to check to make sure all Required Fields are populated.

Skuid always checks required fields when you run model.save(), but it does not do this when you navigate between steps unless you do the Save navigation action. So you have a couple of options here from your Snippet:

Option 1:
Save your Opportunity Model and only go to Step 2 if there are no errors. Skuid will perform client-side validation of required fields before the Save operation is ever sent to the server.

One benefit of this is that, rather than waiting until the end to save everything, you handle the multi-record creation piece by piece. But this can also be a con, because then you'd have to delete all your records if the user wanted to cancel at some later stage in the wizard.

(SNIPPET CODE)


var params = arguments[0],
step = params.step;

var OppModel = skuid.model.getModel('Opportunity');

/*
* Save is Asynchronous,
* so we won't decide whether to navigate
* to Step 2 until we get our async result back.
* In the meantime, we block the UI.
* (Skuid includes jQuery BlockUI)
*/

skuid.$.blockUI({
message: 'Saving Opportunity...'
});

// This performs client-side Required Field validation
// before it ever hits the server
OppModel.save({callback: function(result){

// Regardless of what happens, unblock the UI
skuid.$.unblockUI();

if (result.totalsuccess){
step.navigate('step2');
} else {
// Error messages will be displayed in red on the screen,
// but we can also check the result object
// to see the specifics of which records failed and how
console.log(result.insertResults);
}
}});




Option 2:
Don't actually save the Opportunity Model, just run client-side Required Fields validation.

The trick here is that required field validation is actually performed by Skuid Lists, an internal component. Field Editor, Table, and Queue are the main components that have associated Lists under the hood. Lists are registered with Models, so each Model has an array property called registeredLists that contains all Lists registered on the Model.

So, we can iterate over all Lists registered on a particular Model, or just get a particular one, and call validateRequiredFields() on the List, like this, which returns a list of error objects);

(SNIPPET CODE)


var params = arguments[0],
step = params.step;

var OppModel = skuid.model.getModel('Opportunity');

var validationErrors = [];

skuid.$.each(OppModel.registeredLists,function(i,list){
// Will display error messages to the user
var errors = list.validateRequiredFields();
if (errors.length) {
skuid.$.each(errors,function(){ validationErrors.push(this); });
}
});

if (validationErrors.length) {
alert(validationErrors[0].message);
} else {
step.navigate('step2');
}


Photo of Glenn Elliott

Glenn Elliott, Champion

  • 7,738 Points 5k badge 2x thumb
This is great. And just to join up some dots on the forum, Zach's Option 2 above answers my question from last month here: http://community.skuidify.com/skuid/t...