Trigger popup from javascript?

Ant BelshamAnt Belsham Member
edited June 2019 in Questions
Is there a way to trigger a popup from javascript?
«1

Comments

  • Zach McElrathZach McElrath Skuad
    edited March 2017
    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.
                                 {{CaseNumber}}: {{Subject}}                    
    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 =   ''     + ''        + ''            + '{{CaseNumber}}: {{Subject}}'         + ''     + ''  + '';    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 =     ''          + ''              + ''                  + 'Escalate Cases: ' + selectedCaseNumbers.join(', ') + ''                  + ''                      + ''                  + ''              +''              + ''                  + ''                      + ''                          + ''                              + ''                                  + ''                                      + ''                                  + ''                              + ''                          + ''                      + ''                  + ''              + ''          + ''  + '';      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();      }  }});  </code>  
  • Ant BelshamAnt Belsham Member
    edited June 2018
    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?
  • Zach McElrathZach McElrath Skuad
    edited February 2018
    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();                  }                                }                        });      }    }      
  • Glenn ElliottGlenn Elliott Member
    edited April 2017
    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?
  • edited June 2019
    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?
  • Zach McElrathZach McElrath Skuad
    edited December 2016
    Hi John, which field in your Model did you drag into the Table? This snippet is configured to be used on a Name field. So try dragging in the Name field on your Model and then changing the Field Renderer.
  • edited February 2014
    Thanks Zach - I see - you're relying on the automatic linking you get with the name field to make the field linkable. I just tried it with the name field and it works! Now is there an easy way to apply the same approach to a different field? In our particular application, the name field isn't the most natural field to have as the link to the detail popup.
  • Zach McElrathZach McElrath Skuad
    edited December 2016
    John that really depends on how you want to initiate the action --- if you want to have a link on a field that does not come with a link, you'll have to add a link to your field's DOM element and then give it the on-click behavior given above. What type of Field are you working with? A text field? A lookup/master-detail field?
  • edited February 2014
    It's a name field from a lookup to another object. I just took a quick crack at adding an onclick even directly to the field definition in XML but that didn't do it. So how does one add the onclick even to a particular field?
  • Zach McElrathZach McElrath Skuad
    edited December 2016
    Here's a re-written version of the above Snippet that should let you use it on a non-Name field:
        //  // 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);    // Define the "show popup on click" action  var showPopup = 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();          }                }  };    // If we're NOT in edit mode, then have the "link" action show a popup    if (field.mode !== 'edit') {            if (ROW_ACTION_ICON) {          var linkTag = field.element.find('a');          if (!linkTag.length) {              // Extract field contents and make them the body of a new link tag              var contents = field.element.contents().detach();              linkTag = $('').append(contents);              field.element.append(linkTag);          }            linkTag.attr('href','#').on('click',function(){              showPopup();          });      }    }      
  • edited February 2014
    Thanks Zach - that works great.
  • Glenn ElliottGlenn Elliott Member
    edited December 2014
    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.
  • edited September 2014
    hi guys..
     can i get another page url in <component>    </component>  
  • Zach McElrathZach McElrath Skuad
    edited December 2016
    Pavan, can you explain in more detail what you are trying to do?
  • Jarrod HinsonJarrod Hinson Member
    edited September 2016
    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.
  • PeterPeter Member
    edited November 2015
    This still works great for me, too.
  • Rob HatchRob Hatch Skuad, Sonar ✭✭
    edited September 2016
    You should be able to create a very similar custom rendererer on the field editor field.  The snippets will be very much the same. 
  • Sunny SharmaSunny Sharma Member
    edited August 2015
    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);
                }
  • edited March 2017
    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?

  • Rob HatchRob Hatch Skuad, Sonar ✭✭
    edited September 2016
    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. 
  • Jack SanfordJack Sanford Member ✭✭
    edited January 2018
    I'd like to do this from an href in a template component, any ideas? 
  • Jack SanfordJack Sanford Member ✭✭
    edited January 2017
    Like this:

    new javascript in-line (NOT snippet)
    function popupWindow() { var popupXMLString =   '<popup title=" " width="70%">' + '<components>' +'<includepanel type="skuid" uniqueid="sk-VyQ_Q-161" pagename="UCCNew" module=""/>' +'</components>' +'</popup>';  var popupXML = skuid.utils.makeXMLDoc(popupXMLString);  popup = skuid.utils.createPopupFromPopupXML(popupXML); }
    Then a template component that looks like this:
    <a target="popup" onclick="return popupWindow('')" title="Create a New Entity Search"  > Some text or img src here </a>
    Note that lack of an href on that <a> tag, so you're link doesn't go anywhere but popup.

    Also, I didn't need a context, because my popup is a page include of a create new record page. But if you wanted context you can add that by referencing a model like is shown in the examples above. 
  • Ryan DenisRyan Denis Member
    edited September 2016
    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...
  • Rob HatchRob Hatch Skuad, Sonar ✭✭
    edited September 2016
    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.. 
  • nsns Member
    edited January 2017
    Hi Jack, The js worked on 1 button i have on an html page. However, I created 4 buttons and of course, when the 2nd, 3rd or 4th button were selected, they all returned the same popup window. I have 4 page includes that should only render for the assigned button.

    What do I need to do with either the js syntax you provided so that each button points to the exact target?  

    This is my js syntax modified from your suggesition (works perfect with one button):
    function popupWindow() { var popupXMLString =  '<popup title="triple 6" width="70%">' + '<components>' +'<includepanel type="skuid" uniqueid="sk-4MOStR-047" pagename="Create A Widget" module=""/>' +'</components>' +'</popup>';  var popupXML = skuid.utils.makeXMLDoc(popupXMLString);  popup = skuid.utils.createPopupFromPopupXML(popupXML); } 
    This is my html/css syntax and it works fine, just need to somehow have each button reference the correct js.

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

    Thanks!!!!
  • Jack SanfordJack Sanford Member ✭✭
    edited January 2017
    Couple things you could do, either create a new snippet for each button, popupWindow2, popupWindow3, etc. Or depending on your use could pass different variables from each place you have the HTML onclick action to your page include, and have it the end result be different from each variable. Same as if you have a page include component added declaratively, you can pass page parameters to your page include in your xml string in your popupWindow snippet. I'll post an example later today.
  • Jack SanfordJack Sanford Member ✭✭
    edited January 2017
    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. 
  • Jack SanfordJack Sanford Member ✭✭
    edited January 2017
    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>


  • nsns Member
    edited January 2017
    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?
  • nsns Member
    edited January 2017
    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?
Sign In or Register to comment.