Trigger popup from javascript?

  • 6
  • Question
  • Updated 2 years ago
  • Answered
Is there a way to trigger a popup from javascript?
Photo of Ant Belsham

Ant Belsham

  • 1,022 Points 1k badge 2x thumb

Posted 5 years ago

  • 6
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Yes, there is.

There is a skuid utils method that you can use for exactly this purpose:

var popup = skuid.utils.createPopupFromPopupXML(popupXML,context);

that returns a jQuery object around a new jQuery UI Dialog / Popup box, which you could then run jQuery UI Dialog methods on, such as:

popup.dialog('close');

where popupXML is a jQuery object around the XML definition of a popup, such as would be created by Skuid using one of the Popup Action types, e.g.



<popup title="Editing Case: {{CaseNumber}}" width="80%">
<components>
<pagetitle model="CaseData">
<maintitle>{{CaseNumber}}: {{Subject}}</maintitle>
</pagetitle>
</components>
</popup>



and context is an optional JavaScript object providing Skuid with some context in which to create the popup --- for instance, you might want to pass in a particular row here so that Skuid knows which row to render the popup relative to, e.g.

// Get the 14th row in our Cases Model
var case = CasesModel.data[13];

context = {
row: case
};

// So to show the above popup XML in context of this row, here's what we would need to do in Skuid from a JavaScript Snippet:



var popupXMLString =
'<popup title="Editing Case: {{CaseNumber}}" width="80%">'
+ '<components>'
+ '<pagetitle model="CaseData">'
+ '<maintitle>{{CaseNumber}}: {{Subject}}</maintitle>'
+ '</pagetitle>'
+ '</components>'
+ '</popup>';

var popupXML = skuid.utils.makeXMLDoc(popupXMLString);

var CasesModel = skuid.model.getModel('Cases');
var case = CasesModel.data[13];

context = {
row: case
};

var popup = skuid.utils.createPopupFromPopupXML(popupXML,context);



For a more extended example, here is a use case:

Case Actions: Escalate, Mass Escalate

We want to add a "Escalate" Row Action to a table on Cases, and a "Mass Escalate" Mass Action as well. These Actions will both launch a popup, via JavaScript, that lets you enter a reason for the Escalation, and then will Escalate these Cases and add Case Comments to each selected Case corresponding to the reason provided.

Here is what the final product looks like:









THE CODE

Essentially this is all achieved through 2 JavaScript Snippets, and 2 extra Models added to a Skuid Cases page. Here are the Snippets:



And here are the two Models: NewCaseComments, EscalationReason.





For both Models, go to their Advanced Properties, and set "Load Model data on page load" to FALSE (unchecked). Our popup snippet will create rows in the Models when appropriate.

The first Snippet, ShowEscalationPopup, is called by a "Escalate" Row Action and a "Mass Escalate" Mass Action, and the



The second Snippet, FinishCaseEscalation, is called by a PageTitle Action within the Popup that we launch from JavaScript.

Here is the code for the two Snippets, which is commented so as to help explain how it works. Enjoy!

Snippet 1: ShowEscalationPopup



var params = arguments[0],
$ = skuid.$,
list = params.list,
model = params.model,
$xml = skuid.utils.makeXMLDoc,
selectedItems = params.item ? [params.item] : list.getSelectedItems(),
NewCaseCommentsModel = skuid.model.getModel('NewCaseComments'),
EscalationReasonModel = skuid.model.getModel('EscalationReason');

// Cancel any preexisting changes to our CaseComments model
// and our EscalationReason model
skuid.model.cancel([
NewCaseCommentsModel,
EscalationReasonModel
]);

var selectedCaseNumbers = [];
var casesAlreadyEscalated = [];

$.each(selectedItems,function(i,item){

var caseRow = item.row;

// If one of the Cases is ALREADY Escalated,
// then we will complain.
if (caseRow.IsEscalated || caseRow.Status=='Escalated') {
casesAlreadyEscalated.push(caseRow.CaseNumber);
return true;
}

selectedCaseNumbers.push(caseRow.CaseNumber);

// Create a new row in our NewCaseComments model
// for each selected item
NewCaseCommentsModel.createRow({ additionalConditions: [
{ field: 'ParentId', value: caseRow.Id }
]});

});

if (!selectedCaseNumbers.length){
alert('All of the selected Cases are already Escalated.');
}

// And create a new row in our EscalationReason model
EscalationReasonModel.createRow();

// Build the XML of your popup.
// If you already have a reference to this xml string stored somewhere else,
// then you don't need to create it here.
var popupXMLString =
'<popup width="50%" title="Escalate Cases">'
+ '<components>'
+ '<pagetitle model="EscalationReason">'
+ '<maintitle>Escalate Cases: ' + selectedCaseNumbers.join(', ') + '</maintitle>'
+ '<actions>'
+ '<action type="custom" snippet="FinishCaseEscalation" label="Complete Escalation" icon="ui-silk-accept"/>'
+ '</actions>'
+'</pagetitle>'
+ '<basicfieldeditor model="EscalationReason" mode="edit" showsavecancel="false">'
+ '<columns>'
+ '<column width="100%">'
+ '<sections>'
+ '<section title="Escalation Reason">'
+ '<fields>'
+ '<field id="CommentBody" required="true"/>'
+ '</fields>'
+ '</section>'
+ '</sections>'
+ '</column>'
+ '</columns>'
+ '</basicfieldeditor>'
+ '</components>'
+ '</popup>';

var popupXML = $xml(popupXMLString);

var context = {

};

// Launch a Popup asking the user to provide an Escalation reason
var popup = skuid.utils.createPopupFromPopupXML(popupXML,context);




Snippet 2: FinishCaseEscalation




var params = arguments[0],
$ = skuid.$,
EscalationReasonModel = params.model,
escalationReason = params.model.getFieldValue(params.row,'CommentBody',true),
CasesModel = skuid.model.getModel('Cases'),
NewCaseCommentsModel = skuid.model.getModel('NewCaseComments'),
EscalationReasonModel = skuid.model.getModel('EscalationReason');

// If we weren't given an escalation reason
// that is at least 5 characters long,
// don't do anything
if (!escalationReason || escalationReason.length<5){
alert('Please provide an Escalation Reason that is at least 5 characters.');
return;
}

// Apply the Escalation Reason specified
// to all of our new case comment records
// which were created behind-the-scenes
// when we showed our popup

var selectedCaseNumbers = [];

$.each(NewCaseCommentsModel.data,function(i,newCommentRow){

var parentCase = CasesModel.getRowById(newCommentRow.ParentId);

// Update the Status of all of our selected Items to be "escalated"
if (parentCase) {
CasesModel.updateRow(parentCase,'Status','Escalated');
CasesModel.updateRow(parentCase,'IsEscalated',true);
NewCaseCommentsModel.updateRow(newCommentRow,'CommentBody',escalationReason);
} else {
NewCaseCommentsModel.deleteRow(newCommentRow);
}
});

// Block the UI while we're saving.
$.blockUI({
// Use this syntax if you want to show a Custom Label
// instead of hard-coding the message in our popup.
// Create a Custom Label called "escalating_cases"
// and include it in this page
//message: skuid.label.read('escalating_cases')
message: 'Escalating Cases...'
});

// Save our Case Escalations first,
// then save our new Case Comments ONLY
// if the Case Escalations all succeeded

CasesModel.save({callback: function(result){
if (result.totalsuccess) {
// Now save our Case Comments
NewCaseCommentsModel.save({callback: function(result2){
$.unblockUI();
if (result2.totalsuccess) {
// Close the popup
$('.ui-dialog-content').dialog('close');
}
}});
} else {
$.unblockUI();
}
}});

Photo of Ant Belsham

Ant Belsham

  • 1,022 Points 1k badge 2x thumb
That's a pretty comprehensive reply (as per normal :)). It actually gives us a few other ideas on how to use popups. Nice.

However, it almost answered my question. I was trying to do something slightly simplier. Instead of having a button to trigger to popup action on a row, I wanted to build the link into a custom renderer for a field so that clicking on it pops up the detail.

Looking at your example, it also seems there is a skuid.utils.createPopupFromAction() method. Is this what i'm looking for and if so what syntax do I use for the parameters?
Photo of Zach McElrath

Zach McElrath, Employee

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

As regards skuid.utils.createPopupFromAction, I would not recommending using this here. It is an internal use only method that we may change, although this method does in fact do what you think it does: it accepts an action XML node, such as a Row Action, Global Action, etc., and creates a Popup based on any Popups within that action.

BUT, we do not recommend that you use it, primarily because we have not documented a way of getting at the XML for the Actions in the Table from JavaScript. (It's doable, but we're not supporting it just yet).

There is an alternative, though.

It is fairly simple in principle: if you already have a Row Action on your Table that launches a Popup, and you just want to have a Field Renderer launch that Popup, then you can use jQuery's click() method to initiate the click handler on the Row Action.

The trickiness is in reliably accessing the Row Action's DOM element from JavaScript. You have to pick some way of distinguishing Row Actions from each other. One way is by order: e.g. pick the 1st one, 3rd one, etc. Another way is by Icon, e.g. pick the one that's using "ui-silk-building" as its icon, as this is more likely to be unique, and allows you to move around the Row Actions without having to adjust your JavaScript code.

So, assuming that you already have a Row Action that shows your Popup, here is the JavaScript code for a Custom Field Renderer that you can apply to a Reference Field in your Table that will do exactly what clicking on the Row Action does: launch the Popup.

For my example, I have a Contacts table, and I want to launch an "Account Details" popup whenever I click on the Account record, instead of initiating a URL redirect over to the full Account record in salesforce.





Here's the Inline Snippet code.

The only thing you need to change is the value of ROW_ACTION_ICON, where you identify the Row Action's icon, so that the Snippet can find the corresponding Row Action and call its on-click handler.



//
// CONFIG: identify the Row Action's icon
//
var ROW_ACTION_ICON = 'ui-silk-building';

var field = arguments[0],
value = skuid.utils.decodeHTML(arguments[1]),
$ = skuid.$;

skuid.ui.fieldRenderers[field.metadata.displaytype][field.mode](field,value);

// If we're NOT in edit mode, then have the "link" action show a popup

if (field.mode !== 'edit') {

if (ROW_ACTION_ICON) {
field.element.find('a').attr('href','#').on('click',function(){
var closestTr = field.element.closest('tr');
var tds = closestTr.children('td');
var actionsCell;
if (tds.length) {
if (tds.first().find('input[type="checkbox"]').length) {
actionsCell = tds[1];
} else {
actionsCell = tds[0];
}
}

if (actionsCell) {
var rowAction = $(actionsCell).find('.'+ROW_ACTION_ICON);
if (rowAction) {
// Trigger the click action
rowAction.click();
}

}

});
}

}


Photo of Glenn Elliott

Glenn Elliott, Champion

  • 7,738 Points 5k badge 2x thumb
This idea of firing an existing row action popup suits our need too. We have a table of tasks using the standard Task object. We have an existing popup row action with a distinct icon, so it's easy to identify. We want to override the link on the standard Subject field.

But I'm getting an error at the line "skuid.ui.fieldRenderers[field.metadata.displaytype][field.mode](field,value)". Is it because Subject is a combobox and the field arguments are different?
Photo of John Hillstrom

John Hillstrom

  • 306 Points 250 badge 2x thumb
Hi Zach - I'm trying to make this latest code snippet work but I'm missing something. What I did is:

  1. Created a row-action popup that works
  2. Used your javascript above an create an In-Line Snippet called "openPopup" (and update the row-action-icon to the one I'm using)
  3. Created a column in my table from the model with the "Field Renderer" set to "Custom (run a Snippet)" and I set the Render Snippet Name = "openPopup".

The result is that my field isn't clickable (doesn't appear as a link). What am I missing?
Photo of John Hillstrom

John Hillstrom

  • 306 Points 250 badge 2x thumb
Thanks Zach - that works great.
Photo of Glenn Elliott

Glenn Elliott, Champion

  • 7,738 Points 5k badge 2x thumb
Zach ... should this work for Task.Subject that I mentioned in my comment? I gave it a quick try and in my test so far it just renders the standard link.
Photo of pavan thutupalli

pavan thutupalli

  • 144 Points 100 badge 2x thumb
hi guys..
 can i get another page url in <component>    </component>  
Photo of Zach McElrath

Zach McElrath, Employee

  • 48,984 Points 20k badge 2x thumb
Pavan, can you explain in more detail what you are trying to do?
Photo of Peter Bender

Peter Bender, Champion

  • 6,246 Points 5k badge 2x thumb
This still works great for me, too.
Photo of Jarrod Hinson

Jarrod Hinson

  • 2,310 Points 2k badge 2x thumb
How can I do this same thing but for a Field within a Field Editor?

I have a a couple different reference fields on my Account that are used to associate a specific related contact or other object and I don't have my fields for this within a table.

I really like the built in mini page layout hovers in SF but a click with a pop up would be very nice too.
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
You should be able to create a very similar custom rendererer on the field editor field.  The snippets will be very much the same. 
Photo of Sunny Sharma

Sunny Sharma

  • 678 Points 500 badge 2x thumb
hi 

I was able to use the above piece of code but facing an issue related to layout of popup. i need to display a rich text field (though it is not causing any issue). when i click on button, popup has 7 blank lines above actual text. has anyone else faced such issue?
Code:
var params = arguments[0],            
            
                $ = skuid.$,
            
            
                list = params.list,
            
            
                model = params.model,
            
            
                $xml = skuid.utils.makeXMLDoc,
            
            
                HomepageTileModel = skuid.model.getModel('HomepageTile');
           
            var contextModel;
            var varURLPath = document.referrer;
            var varURL;
            if(varURLPath.indexOf("?") !== -1)
            varURL = varURLPath.substring(varURLPath.lastIndexOf(".com/")+4,varURLPath.indexOf("?"));
            else 
            varURL = varURLPath.substring(varURLPath.lastIndexOf(".com/")+4);
           
            $.each(HomepageTileModel.data, function(i, row){
               console.log(row.Id + '-' + row.Is_Active__c + '-'  + row.URL__c);
            var varRowURL = row.URL__c;
            if(varRowURL.indexOf(varURL) !== -1 && row.Is_Active__c === true) {
            contextModel = row;
            console.log('contextModel' + contextModel.Id);
            }
            });
            if(contextModel !== undefined) {
            var popupXMLString = '<popup title="" width="90%">';
                popupXMLString+='<components>';
                   popupXMLString+='<basicfieldeditor showheader="false" showsavecancel="false" model="HomepageTile" buttonposition="" mode="readonly" layout="above">';
                      popupXMLString+='<columns>';
                         popupXMLString+='<column width="100%">';
                            popupXMLString+='<sections>';
                               popupXMLString+='<section title="New Section" collapsible="no" showheader="false">';
                                  popupXMLString+='<fields>';
                                     popupXMLString+='<field id="Description__c" valuehalign="" type="">';
                                        popupXMLString+='<label> </label>';
                                     popupXMLString+='</field>';
                                  popupXMLString+='</fields>';
                               popupXMLString+='</section>';
                            popupXMLString+='</sections>';
                         popupXMLString+='</column>';
                      popupXMLString+='</columns>';
                   popupXMLString+='</basicfieldeditor>';
                popupXMLString+='</components>';
             popupXMLString+='</popup>';
var popupXML = skuid.utils.makeXMLDoc(popupXMLString);
            
            
            
            var context = {
            
            row: contextModel
            
            };
            
            console.log(contextModel);
            
            // Launch a Popup asking the user to provide an Escalation reason
            
            
            var popup = skuid.utils.createPopupFromPopupXML(popupXML,context);
            }
Photo of Salesforce Dev007

Salesforce Dev007

  • 308 Points 250 badge 2x thumb
I am doing similar thing and want to make the row action icon invisible while implementing this solution? Is there a way to do this?
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
You can add custom CSS that targets the row action icon you are interested and sets to be hidden. Getting your selectors just right to target the icon may be a challenge,  but just inspect the rendered page and root around until you find the classes and divs. 
Photo of Jack Sanford

Jack Sanford, Champion

  • 8,322 Points 5k badge 2x thumb
I'd like to do this from an href in a template component, any ideas? 
Photo of Jack Sanford

Jack Sanford, Champion

  • 8,322 Points 5k badge 2x thumb
So if you adjust your popupWindow snippet to the following:

function popupWindow() {
var popupXMLString = 
'<popup title="triple 6" width="70%">'
+ '<components>'
+'<includepanel type="skuid" uniqueid="sk-4MOStR-047" pagename="Create A Widget" module=""/>'
+'querystring="type=' +typeVariable+'</components>'
+'</popup>';

var popupXML = skuid.utils.makeXMLDoc(popupXMLString);

popup = skuid.utils.createPopupFromPopupXML(popupXML);
}
Then you can adjust your html to the following

To create a widget of type Jedi:
<div class="text-xsr"><a target="popup" onclick="return popupWindow('Jedi')" title="Business Entity" class="btn btn-black">SUBMIT</a></div>

To create a widget of type Empire
<div class="text-xsr"><a target="popup" onclick="return popupWindow('Empire')" title="Business Entity" class="btn btn-black">SUBMIT</a></div>

You can also pass multiple page parameters to your page include, for example if you have a type and an amount:

function popupWindow() {
var popupXMLString = '<popup title="triple 6" width="70%">' + '<components>' +'<includepanel type="skuid" uniqueid="sk-4MOStR-047" pagename="Create A Widget" module=""/>' +'querystring="type=' +typeVariable++'&amp;amount='+amountVariable '</components>' +'</popup>'; var popupXML = skuid.utils.makeXMLDoc(popupXMLString); popup = skuid.utils.createPopupFromPopupXML(popupXML); }

Then you can have html that looks like: 
<div class="text-xsr"><a target="popup" onclick="return popupWindow('Jedi','{{{Amount__c}}}' )" title="Business Entity" class="btn btn-black">SUBMIT</a></div>

Note that you can use Skuid merge syntax to pass in fields from whatever context your html onclick is, or you could even reference any model's field via {{{$Model.Widgets.data.0.Amount__c}}}

FWIW, another cool trick is moving the onclick code into a custom render snippet, so if you wanted it on several fields in a table, you could just maintain one custom render snippet with your onclick action. 

Hope that helps. Let me know if you have any other questions. 
Photo of Jack Sanford

Jack Sanford, Champion

  • 8,322 Points 5k badge 2x thumb
Nate, sorry just re-read your post more carefully, you want to change the entire page include for each one:

function popupWindow() {
var popupXMLString = '<popup title="triple 6" width="70%">' + '<components>' +'<includepanel type="skuid" uniqueid="sk-4MOStR-047"
pagename="+pageName+" module=""/>' +'</components>' +'</popup>'; var popupXML = skuid.utils.makeXMLDoc(popupXMLString); popup = skuid.utils.createPopupFromPopupXML(popupXML); }

Then your html would be:

For a page called CreateAWidget
<div class="text-xsr"><a target="popup" onclick="return popupWindow('CreateAWidget')" title="Business Entity" class="btn btn-black">SUBMIT</a></div>

or for a page called CreateAnAccount
<div class="text-xsr"><a target="popup" onclick="return popupWindow('CreateAnAccount')" title="Business Entity" class="btn btn-black">SUBMIT</a></div>
Photo of ns

ns

  • 1,822 Points 1k badge 2x thumb
Hi Jack, the last option here did not work. I have created 3 separate js in-line resources and it seems to always choose the last  js in the javascript window. Any ideas?
Photo of ns

ns

  • 1,822 Points 1k badge 2x thumb
Jack, The unique Id I referenced was from the old pop up button referencing the page includes. Since I am not using a pop up button, I'm using my custom html button, what other way can I locate the unique Id?
Photo of ns

ns

  • 1,822 Points 1k badge 2x thumb
Jack, I got it! 

I referenced the correct page this time, so it was the unique Id and creating a snippet for each button as you mentioned. 

Thank you!
Photo of Ryan Denis

Ryan Denis

  • 542 Points 500 badge 2x thumb
This is great! Got it working. Is there any way to take it one step further and hide the row actions? Therefore there is just the link to the popup? I assume probably not...
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
Find the Div for the row actions and add custom css  "Display:none"   You probably want to have your CSS start with the table ID and navigated down to the specific class for the icons so you don't hide all row actions on your pages..