1

I am developing code for @RemoteAction. In this example once the user selected a particular opportunity stage name then calling the @RemoteAction to get the list of opportunities.

In my case, I am not getting the Opportunity Name value:

enter image description here

OpportunityRemoteActionController

 global with sharing class OpportunityRemoteActionController {

      public Opportunity opp{get;set;}
      public String stageName{get;set;}


      public OpportunityRemoteActionController() {    
      }
       /**
       * Method that creates the select option dynamically.
       **/
       public List<SelectOption> getOptions() {
           List<SelectOption> options = new List<SelectOption>();
          Schema.DescribeFieldResult fieldResult = Opportunity.StageName.getDescribe();
          List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues();
          options.add(new SelectOption('--Select--', '--Select--'));

         for( Schema.PicklistEntry f : ple){
             //system.debug('f.getLabel()=>'+f.getLabel() +' ==f.getValue()' +f.getValue());
            options.add(new SelectOption(f.getLabel(), f.getValue()));
         }   
         return options;
      }

      /**
       * Remote action involved with Javascript remoting and is made global
       **/
        @RemoteAction
          global static Opportunity[] getOpportunityDetails(String stageNameDet) {
              return [select id,Name,Amount,stageName from Opportunity WHERE stageName =: stageNameDet];
          }
 }

VFPage

<apex:page controller="OpportunityRemoteActionController" showHeader="true">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript">
function getStageJS(){
 var oppStage= document.getElementById("{!$Component.theFm.oppStage}").value;  
  alert("stageName==>"+oppStage); 
  OpportunityRemoteActionController.getOpportunityDetails( oppStage,

    function(result, event){
         var html = '<table border="thick solid">';
          html = html + '<caption><b>Opportunity Details</b></caption><tr></tr>';
          html = html + '<tr><th>Opportunity Name</th>';
          html = html + '<th>Amount</th> </tr>';
         if (event.status && event.result) {
            debugger;
        // alert("event.result[0].Name==>"+event.result[0].Name);           
          for (var prop in event.result) {
          // important check that this is objects own property not from prototype prop inherited    
          //alert(prop + " = " + event.result[prop].Name);
          html = html + '<tr><td><a href="'+event.result[prop].Name+'</td> <td>'+event.result[prop].Amount+'</td></tr> ';
      }

       html = html + '</table>';


             alert("html==>"+html);
            $("#opportunityDetails").html(html);


      } else {

             alert(event.message);

      }

}, {escape:true});




}

 </script>

   <div align="center" width="550px">
      <apex:form id="theFm">
        <apex:selectList value="{!stageName}" size="1" id="oppStage" onchange="getStageJS()">
             <apex:selectOptions value="{!options}"/>
        </apex:selectList>
      </apex:form>
   </div>
   <br/>
   <br/>
   <div id="opportunityDetails" align="center">

         <!-- Opportunity details is displayed here. -->

   </div>
 </apex:page>

As per sdfcfox, below error comes: enter image description here

and console error:enter image description here

5
  • 1
    Based on your code here: <a href="'+event.result[prop].Name+', it's probably being put in the page as a link, and without an inner text node, there is nothing there to see or click on. – martin May 30 '17 at 18:05
  • Make sure you're testing names that include quotes and other special characters to make sure your code doesn't break. Rendering raw HTML like this is probably not ideal. – sfdcfox May 30 '17 at 18:11
  • @sfdcfox - How we can render the output when working with @RemoteAction? Any better approach ? – user45427 May 30 '17 at 18:40
  • @javaHelper in jQuery, use jQuery('<a />', { href: ... }) and the like to construct individual DOM elements that will be properly escaped, and then use append to build your node tree. You can do this without jQuery simply by using document.createElement, document.createTextNode, and Node.appendChild. I realize it's slightly more code to write, but has better performance and doesn't risk HTML injection. – sfdcfox May 30 '17 at 18:53
  • I am not expert on JS/HTML side yet. Could you please provide something good to refer for this use case ? – user4567570 May 30 '17 at 18:55
3

Writing raw, plain HTML to inject is considered a security risk. Instead, take the time to properly build your HTML using DOM elements:

<apex:page controller="OpportunityRemoteActionController" showHeader="true">
<script type="text/javascript">
function getStageJS(){
 var oppStage= document.getElementById("{!$Component.theFm.oppStage}").value;  
  OpportunityRemoteActionController.getOpportunityDetails( oppStage,

function(result, event){
    var root, table, a, caption, bold, text, tr, th, td;

    table = document.createElement("table");
    table.border = "thick solid";

    caption = document.createElement("caption");
    bold = document.createElement("b");
    text = document.createTextNode("Opportunity Details");
    bold.appendChild(text);
    caption.appendChild(bold);
    table.appendChild(caption);

    tr = document.createElement("tr");
    th = document.createElement("th");
    text = document.createTextNode("Opportunity Name");
    th.appendChild(text);
    tr.appendChild(th);

    th = document.createElement("th");
    text = document.createTextNode("Amount");
    th.appendChild(text);
    tr.appendChild(th);

    table.appendChild(tr);

    if(event.status && event.result) {
        event.result.length = Object.keys(event.result).length;
        Array.prototype.forEach.call(event.result, function(row) {
            tr = document.createElement("tr");
            td = document.createElement("td");
            a = document.createElement("a");
            a.href = "/"+row.Id;
            text = document.createTextNode(row.Name);
            a.appendChild(text);
            td.appendChild(a);
            tr.appendChild(td);

            td = document.createElement("td");
            text = document.createTextNode(row.Amount || 0);
            td.appendChild(text);
            tr.appendChild(td);
            table.appendChild(tr);
        });
    }
    root = document.getElementById("opportunityDetails");
    while(root.firstChild) {
        root.removeChild(root.firstChild);
    }
    root.appendChild(table);
}, {escape:true});




}

 </script>

   <div align="center" width="550px">
      <apex:form id="theFm">
        <apex:selectList value="{!stageName}" size="1" id="oppStage" onchange="getStageJS()">
             <apex:selectOptions value="{!options}"/>
        </apex:selectList>
      </apex:form>
   </div>
   <br/>
   <br/>
   <div id="opportunityDetails" align="center">

         <!-- Opportunity details is displayed here. -->

   </div>
 </apex:page>

There's a jQuery equivalent if you want slightly shorter syntax, but you should at minimum consider constructing proper elements. This has better performance and fewer security risks.

5
  • @user4567570 That's a shame. Probably not a "real" array, so try Array.prototype.forEach.call instead (see answer). – sfdcfox May 30 '17 at 19:29
  • I just tried your solution, some error comes, Refused to display 'https://ap5.salesforce.com/apexpages/devmode/devConsoleViewStateMetadataReceiver.apexp' in a frame because it set 'X-Frame-Options' to 'sameorigin'.. I don't know what is this, otherwise could have tried to fixed t too. No data shown when selected from drop down.. – user45427 May 30 '17 at 19:36
  • @javaHelper let me test real quick – sfdcfox May 30 '17 at 19:39
  • Nothing is displayed on the screen. Just saw error on console tab. – user45427 May 30 '17 at 19:41
  • @javaHelper I have created a new version that I verified to work in Chrome. Give it a try. – sfdcfox May 30 '17 at 20:06
1

Just wanted to update the Caspar's answer, still some minor changes are required. I have just shown one more column Account Name.

Changed below

<a href="'+ event.result[prop].Id +'">' + '+ event.result[prop].Name +' + ' </a>

To, Use below

<a href="'+ event.result[prop].Id +'">' +event.result[prop].Name+ ' </a>'

enter image description here

When we click on any Opportunity Name, below error comes

enter image description here

When I checked URL, it comes like https://c.ap5.visual.force.com/apex/0067F000002blLrQAI, I think it should simply come like

https://c.ap5.visual.force.com/0067F000002blLrQAI
0
1

In addition to @martin's comment, you haven't closed your <a> tag either.

So change this:

<a href="'+event.result[prop].Name+'

To this:

<a href="'+ event.result[prop].Name +'">' + '+ event.result[prop].Name +' + ' </a>

If you want the link go to the Opportunity, you should change the href value to the Opportunity Id as well:

<a href="/'+ event.result[prop].Id +'">' + '+ event.result[prop].Name +' + ' </a>
3
  • 1
    +1, but the href should also be using the record's Id, not its name. – sfdcfox May 30 '17 at 18:16
  • realised that min ago and updated my answer – Caspar Harmer May 30 '17 at 18:17
  • Added the fix - it's a relative url that you want and you need a slash for that - see my last line – Caspar Harmer May 30 '17 at 19:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.