Skip to main content

I have a pop up window where I am trying to require the user to populate data in two fields before they can close the popup.  Is this possible?   

Hi Stephen,

Are you setting the popup to save its data when it’s closed (using the after close action), or are the users clicking a Save button? The popup close button “X” can perform after-close actions, but none of those actions would be able to prevent the popup from being closed if there’s an incomplete field or error. 

Say your users are saving the data with a save button, and the save action encounters an error, such as the user didn’t fill out the required fields, or they entered an invalid value. You can add an on-error action to that Save step that will block the UI and display any message that you supply (like “Make sure to fill out all required fields” for example). The on-error action also has the option to “Roll back entire save on any error” which can be useful depending on how the rest of your processes are set up.

Salesforce lets you make fields “required,” but if you don’t want to do that on the Salesforce level, Skuid also provides a way to set a field in a model to “required,” just for that model on that page. You’ll find this option by going into the Model in question, then clicking the field you want to make required. Click Override Metadata, and you’ll see the option for “required.” If you do this, the on-error action can warn the users that they haven’t filled out all required fields.

- Mark DeSimone


I was hoping for a way to prevent the popup from being closed if the field isn’t populated.  If that can’t happen, the next best thing is to require a save.  But… I can’t get that to work either.  The user has to click on a field in order for the Save button to activate.  I tried setting the Field Editor default mode to “Edit”, but that doesn’t turn the button on either.


Stephen,


You can do this with javascript.


Since pop ups don’t have unique ids’, put everything in your pop up in a wrapper (which does have an id).


Then, add this snippet to whatever action is launching the popup. In this case, the website is required.



var params = argumentse0];
var rowInContext = params.row;
$ = skuid.$;
//sk-1w7Itv-132 is your unique wrapper id inside of the pop up
popUp = $("#sk-1w7Itv-132").parent();

$(popUp).dialog({

beforeClose: function( event, ui ) {

//this checks if the field 'Website' is not populated
//if it isn't populated, the function returns false and prevents the pop up from closing
if(!rowInContext.Website){

alert('You must fill in the website in order to close the pop up.');
return false;

}
//if the field 'Website' is populated, then the function returns true allowing the pop up to close
else{
return true;

}

}


});

FYI

I noticed that if the user enters or removes text, and then quickly presses escape, the changes to the model won’t register quickly enough for the javascript to have the latest information. If this need to be idiot proof, you’ll need to somehow check for pending model changes or wait long enough for the model to update the field value.


Here’s an XML sample page with more details.



<skuidpage unsavedchangeswarning="yes" personalizationmode="server" showsidebar="true" useviewportmeta="true" showheader="true"> <models>
<model id="Account" limit="1" query="true" createrowifnonefound="false" datasource="salesforce" type="" sobject="Account">
<fields>
<field id="Name"/>
<field id="Website"/>
</fields>
<conditions/>
<actions/>
</model>
</models>
<components>
<buttonset uniqueid="sk-1w5tOH-92" model="Account">
<buttons>
<button type="multi" label="Launch Popup">
<actions>
<action type="showPopup">
<popup title="Test Popup" width="90%" id="testPopUp">
<components>
<wrapper uniqueid="sk-1w7Itv-132">
<components>
<basicfieldeditor showheader="true" showsavecancel="true" showerrorsinline="true" model="Account" buttonposition="" uniqueid="sk-1wDW1V-389" mode="read">
<columns>
<column width="50%">
<sections>
<section title="Section A" collapsible="no">
<fields>
<field id="Name"/>
</fields>
</section>
</sections>
</column>
<column width="50%">
<sections>
<section title="Section B" collapsible="no">
<fields>
<field id="Website" valuehalign="" type="" required="true"/>
</fields>
</section>
</sections>
</column>
</columns>
</basicfieldeditor>
</components>
<styles>
<styleitem type="background"/>
<styleitem type="border"/>
<styleitem type="size"/>
</styles>
</wrapper>
</components>
</popup>
</action>
<action type="custom" snippet="makeSureRequiredFieldsAreFilled"/>
</actions>
</button>
</buttons>
</buttonset>
</components>
<resources>
<labels/>
<javascript>
<jsitem location="inlinesnippet" name="makeSureRequiredFieldsAreFilled" cachelocation="false">var params = arguments 0];
var rowInContext = params&#46;row;
$ = skuid&#46;$;
popUp = $("#sk-1w7Itv-132")&#46;parent();

$(popUp)&#46;dialog({

beforeClose: function( event, ui ) {

if(!rowInContext&#46;Website){

alert('You must fill in the website in order to close the pop up&#46;');
return false;

}

else{

return true;

}

}


});</jsitem>
</javascript>
<css/>
</resources>
<styles>
<styleitem type="background" bgtype="none"/>
</styles>
</skuidpage>

Hello Stephen -


As Shmuel infers, the key to accomplishing what you are after is to “check/validate” the fields “Before” the dialog closes, not after. Unfortunately, Skuid does not currently provide a declarative means to process anything in the “Before” phase of a dialog closing. As Mark mentions, using the “After Close Actions” won’t prohibit the dialog from closing.


Shmuel’s javascript to add a “beforeClose” handler will accomplish what you’re after even if the user clicks the “X” or hits escape. Unfortunately, assuming you also have a “Cancel” button on your dialog, it will also then check the fields and prohibit the dialog from closing if the fields do not have a value. This can be solved for by using some creative javascript to detect which “button” was pressed but it’s not the cleanest solution. Additionally, you lose the power of the Action framework when going the pure JS route.


In order to accomplish what you are after, what you really want to do is have discrete control paths for a sequence of events to execute depending on if the user wants to “keep/save” or “cancel” from the dialog. To do this, you need to eliminate the “X” and escape key and then only check the fields when the user is heading down the “keep/save” path.


To do this, you can configure the following:



  1. Disable “X” and “Escape key” - Download and install the latest version of the TFG Component Pack which includes the “TFG Popup Controller” (specific details on the “Popup Controller” are available here). Once added, you can add the “Popup Controller” to the top of your popup and enable the “Hide Close” and “Disable Escape” settings. This will eliminate the “X” and “Escape Key” from the dialog. The TFG Popup Controller also contains several other properties including “OnBeforeClose”, “OnCreate”, etc. that make doing what Shmuel describes a little simpler should you ever need that type of functionality.




  2. Validate the fields - In your “keep/save” button on the dialog, set it to “Run Multiple Actions”. In the first action, call a javascript snippet that will validate the fields in question. If the fields are “valid”, then close the popup. If not valid, in the on error sequence for the action, display a message to the user indicating that the fields are required (see below for more details on your options here).




  3. Make cancel really cancel - Depending on how you have your models configured, you can make your “cancel” button truly cancel any changes that the user might have made on the screen. For example, let’s say that the Popup allows the user to change the account name for an account. If the user makes a change to the name on the popup but then hits “Cancel” button, the change is still maintained even though they clicked “cancel.” How to avoid this is likely worthy of a seperate post in-itself but in short, depending on how you use models, you can use the “Cancel Model Changes” action to “undo” any changes the user made while they were in the dialog before they clicked “Cancel”



How to Validate Fields
There are a few different ways you can do this and the “best” way in your situation is based on the way your models are configured and being used. Since I don’t have the specifics around this, I’ll offer a couple of options:



  1. “Hardcoded” field checks - This is what Shmuel describes in his post where you could check the fields that you want required in your JS snippet that is called in “Validate the fields” above.




  2. validateRequiredFields API - There are two flavors of this API - one on skuid.ui.List and one on skuid.model.Model (see here for details). Which one you would use is based on how you are using your models. In your JS snippet, you can call this API and it will validate all fields on the model or list respectively.



How to Display Errors

There are different ways to inform the user that there are required fields that must be completed.



  1. “Show Message and Block UI” - In the “On Error” sequence for the validate fields snippet, you can simply show a message that says something along the lines of “Please fill out all required fields”




  2. “Show Popup with message” - In the "On Error sequence for the validate fields snippet, you can show a popup with the message “Please fill out required fields.” The advantage to this over #1 is that you don’t have to auto-expire the message and the user is forced to click “OK” (or a button of your choosing) on the error message popup.




  3. Add Messages to Page Title - You can add any error returned from validateRequiredFields (or your own custom ones) to the page title on your popup using the handleMessages API.



Putting all of the above together you get something similar to the sample page below based on which decisions you’ve made on the options presented.


Before using the sample page below, you must install the TFG Component Pack.


Hope this helps!


Sample Page XML


<skuidpage unsavedchangeswarning="yes" personalizationmode="server" useviewportmeta="true" showsidebar="true" showheader="true" tabtooverride="Account">    <models>
<model id="Account" limit="100" query="true" createrowifnonefound="false" datasource="salesforce" sobject="Account" type="">
<fields>
<field id="Name"/>
<field id="CreatedDate"/>
</fields>
<conditions/>
<actions/>
</model>
</models>
<components>
<pagetitle model="Account" uniqueid="sk-1_ikCX-87">
<maintitle>
<template>{{Model&#46;labelPlural}}</template>
</maintitle>
<subtitle>
<template>Home</template>
</subtitle>
<actions>
<action type="savecancel"/>
</actions>
</pagetitle>
<skootable showconditions="true" showsavecancel="false" searchmethod="server" searchbox="true" showexportbuttons="false" pagesize="10" createrecords="false" model="Account" mode="readonly" allowcolumnreordering="true" uniqueid="sk-1_ikCX-88">
<fields>
<field id="Name" hideable="true" allowordering="true" uniqueid="fi-1_ij3c-399" valuehalign="" type=""/>
<field id="CreatedDate" hideable="true" allowordering="true" uniqueid="fi-1_ij3c-400"/>
</fields>
<rowactions>
<action type="multi" label="Edit In Popup" icon="sk-icon-edit">
<actions>
<action type="showPopup">
<popup title="Edit {{Model&#46;Label}}" width="30%">
<components>
<tfg__popupcontroller uniqueid="sk-1_mY0f-230" hideclose="true" disableescape="true"/>
<pagetitle model="Account" uniqueid="sk-1_j1Gu-145">
<maintitle>
<template>{{Name}}</template>
</maintitle>
<subtitle>
<template>{{Model&#46;label}}</template>
</subtitle>
<actions>
<action type="multi" label="OK" icon="sk-icon-save">
<actions>
<action type="custom" snippet="validateFields">
<onerroractions/>
</action>
<action type="closeTopmostPopup"/>
</actions>
</action>
<action type="multi" label="Cancel" icon="sk-icon-cancel">
<actions>
<action type="cancel">
<models>
<model>Account</model>
</models>
</action>
<action type="closeTopmostPopup"/>
</actions>
</action>
</actions>
<conditions>
<condition type="contextrow" field="Id" mergefield="Id"/>
</conditions>
</pagetitle>
<basicfieldeditor showheader="true" showsavecancel="false" showerrorsinline="true" model="Account" buttonposition="" uniqueid="accountEditor" mode="edit" layout="">
<columns>
<column width="100%">
<sections>
<section title="Section A" collapsible="no">
<fields>
<field id="Name" valuehalign="" type="" uniqueid="fi-1_zB3z-1919"/>
</fields>
</section>
</sections>
</column>
</columns>
<conditions>
<condition type="contextrow" field="Id" mergefield="Id"/>
</conditions>
</basicfieldeditor>
</components>
<afterclose/>
</popup>
</action>
</actions>
</action>
</rowactions>
<massactions usefirstitemasdefault="true"/>
<views>
<view type="standard"/>
</views>
</skootable>
</components>
<resources>
<labels/>
<css/>
<javascript>
<jsitem location="inlinesnippet" name="validateFields" cachelocation="false">var params = argumentsr0] &#47;&#47; the first argument passed to the snippet
, editor = params&#46;editor &#47;&#47; the editor associated to the page title component
, fieldEditor = skuid&#46;component&#46;getById("accountEditor") &#47;&#47; the field editor component
, list = fieldEditor&#46;element&#46;list &#47;&#47; the list associated to the field editor component
&#47;&#47; we use the list API validateRequiredFields here because we only
&#47;&#47; want to validate what's displayed in the field editor, not all fields
&#47;&#47; on the model
, messages = list&#46;validateRequiredFields();
&#47;&#47; clear any messages currently in the page title
editor&#46;clearMessages();
&#47;&#47; if are no messages, then all required fields have a value
&#47;&#47; so we return true so that processing can continue
if (!messages || messages&#46;length === 0) {
return true;
}
&#47;&#47; if there are messages, then we add them to the page title
editor&#46;handleMessages(messages);
&#47;&#47; return false so that we go to the "on error" sequence for the snippet
return false;</jsitem>
</javascript>
</resources>
<styles>
<styleitem type="background" bgtype="none"/>
</styles>
</skuidpage>

The TFG Component Pack rocks.