Using jquery selectors with dynamic visualforce

JQuery is a great js library, you can easily grab a component or list of components using any number of selectors.

Using it with visualforce can present a problem, however. When you specify an id attribute on a VF component, it outputs in a hierarchical colon separated value, for example:

<apex:page id="thePage">
  <apex:form id="theForm">
    <apex:outputPanel id="thePanel" />
  </apex:form>
</apex:page>

It will generate something of the following format for the innermost component:

<span id="thePage:theForm:thePanel" />

In order to reference this selector in JQuery, ideally you’d do the following:

$("#thePage:theForm:thePanel")

Unfortunately “:” is a special character in JQuery selectors, and will result in the following error:

Uncaught Syntax error, unrecognized expression: Syntax error, unrecognized expression: theForm

There are a number of ways to get around this.

  1. The simplest way is to provide a unique styleClass on the element, e.g.

        <apex:outputPanel id="thePanel" styleClass="thePanel" />
    

    which will generate

        <span id="thePage:theForm:thePanel" class="thePanel" />
    

    and at this point you can access it like this:

    $(".thePanel")
    

    Not a perfect solution, because classes really shouldn’t be forced to be unique, but it works.

  2. If you’re not accessing the id dynamically, you can double-blackslash escape the “:”, e.g.
    $("#thePage\\:theForm\\:thePanel")
    

    Kind of a pain, and ugly, but it works. If you are accessing the id dynamically with the $Component variable, e.g.

    {!$Component.thePage.theForm.thePanel}
    

    obviously you cannot double-blackslash escape without doing some javascript string replacement, but that’s even uglier.

  3. The easiest and seemingly best solution is to just pass the results of document.getElementById to the $() method, which can take an HTML object as a parameter, e.g.
    $(document.getElementById("thePage:theForm:thePanel"))
    

    or

    $(document.getElementById("{!$Component.thePage.theForm.thePanel}"))
    
  4. Seems to work in every scenario.

Tagged , | Comments Off

SOQL/SOSL: Using NOT LIKE

I’ve had to struggle to remember how to do this enough times that I think it’s worth documenting.

Using the LIKE keyword to filter records in a SOQL or SOSL query is a great way to do searching using a partial string match. Here’s an example:

  select Id, Name
  from Account
  where BillingState = 'WA'
    and Name like '%ray%'

But to exclude using the LIKE operator is a little more counter intuitive. Here’s the correct way to do it:

Good

  select Id, Name
  from Account
  where BillingState = 'WA'
    and (not Name like '%ray%')

Note that if you do any similar variants which you think might work, they will fail with the following error:

MALFORMED_QUERY … unexpected token: ‘not’

or

MALFORMED_QUERY … unexpected token: ‘like’

Here are some ways which you’d think might work, but do not.

Bad

  select Id, Name
  from Account
  where BillingState = 'WA'
    and not Name like '%ray%'

Bad

  select Id, Name
  from Account
  where BillingState = 'WA'
    and not (Name like '%ray%')

Bad

  select Id, Name
  from Account
  where BillingState = 'WA'
    and Name not like '%ray%'
Tagged , , | Comments Off

Apex Pattern: Save and Open PDF from button

I’ve heard variations to this request a few times now:

Can we have a button on the {object} where we generate a PDF, save it as an attachment on the {object} record, and open it?

Saving a PDF is so simple with Visualforce it’s ridiculous, simply utilize <apex:page> with the attribute renderAs=”pdf”. In fact, this is one of the early things that made me fall in love with the platform.

Here’s a simple pattern / template I built recently for being able to generate a PDF, save it, and open it all with one button click. This doesn’t contain any specific business logic or content in the PDF, feel free to use this and customize it to your heart’s desire! It could use some error checking and better tests before it’s production ready.

Here’s a demo of it in action:

Unable to display content. Adobe Flash is required.

Here’s the code:

Cheers!

Tagged , , , | Comments Off

Salesforce URL Hacking basics with retURL, saveURL and cancelURL

I have a rather large post about URL Hacking on Salesforce, but it occurs to me that some basics aren’t really addressed. Let’s address one of the fundamental components of URL hacking: navigation.

What happens to the URL when you edit a record in Salesforce using the standard edit button from the record detail page? It looks like this:

https://na10.salesforce.com/a0CA0000007RT6g/e?retURL=%2Fa0CA0000007RT6g

If we understand what every component of that URL is, we can “URL hack” it and modify the standard behavior so that we can customize navigation in our Salesforce application.

Let’s break the URL down from above:

  1. https://na10.salesforce.com/ — the domain
  2. a0CA0000007RT6g — the record we’re editing
  3. /e — the action for the record, e stands for edit in this case
  4. ? … — everything after the ? is the query string, which begins with a ? and is & delimited with key = value pairs, e.g. ?a=b&c=d&e=f, we have 3 variables being set: a which is set to b, c which is set to d and e which is set to f

Specifically above, the application is saying by default that we are editing the record a0CA0000007RT6g, and we want to the application to know that the retURL is set to %2Fa0CA0000007RT6g. %2F is url encoded (from a button or formula field we can use URLENCODE()), and once it’s decoded it becomes a forward slash, /. So once the URL is loaded, it knows that the retURL value is /a0CA0000007RT6g, which is the record we’re editing.

Let’s look at another example, open a Contact then click Edit next to a record on a related list. The URL that is loaded when I open the contact is

https://na10.salesforce.com/003F000001BPE2W

And the URL that is loaded when I click the edit link from the related list is

https://na10.salesforce.com/a0lF0000002Z86T/e?retURL=%2F003F000001BPE2W

a0lF0000002Z86T corresponds to the related record that I’m editing, and the retURL brings us back to the contact upon completion of editing.

What if we don’t want to go back to the Contact when we’re done, but instead stay on the order? It’s a simple URL hack to accomplish this non-standard behavior:

https://na10.salesforce.com/a0lF0000002Z86T/e?retURL=%2Fa0lF0000002Z86T

Now that we know how the URL is composed, we can predictably mold it to do what we want. retURL is a powerful URL parameter, and understanding how it works is fundamental to URL hacking.

There are other important URL parameters to keep in mind. What should we do if we want to redirect the user to a different place if they cancel the changes than if they save the changes? We can simply use saveURL and cancelURL for this purpose, or we can use them all three in conjunction.

The logic behind where the user is redirected on click of the Save button is as follows*:

  1. Is there a saveURL URL parameter set? If so, redirect to that.
  2. Is there a retURL URL parameter set? If so, redirect to that.
  3. Redirect to /home/home.jsp

*The behavior is slightly different if we’re creating a new record.

Similarly the navigation on the Cancel button follows:

  1. Is there a cancelURL URL parameter set? If so, redirect to that.
  2. Is there a retURL URL parameter set? If so, redirect to that.
  3. Redirect to /home/home.jsp

So if we want to specify a different location for the user to go on Cancel than on Save, we could simply supply a cancelURL and retURL. For example:

https://na10.salesforce.com/a0lF0000002Z86T/e?cancelURL=%2Fa0lF0000002Z86T&retURL=%2F003F000001BPE2W

This would bring us to the related record if we canceled, and bring us back to the Contact if we saved.

There are many other URL parameters used by Salesforce, but retURL, saveURL and cancelURL are the most fundamental.

In conclusion (tl;dr): we can use these fundamentals of url hacking for navigation to construct custom buttons and links to answer the question “can we forward the user somewhere else?” The answer: yes, absolutely.

Tagged , | Comments Off

Node.js http getaddrinfo ENOTFOUND

I had this code snippet (slightly modified from how to make external http requests with node.js):

var http = require('http');

var options = {
  host: 'http://api.untappd.com/',
  port: 80,
  path: '/v4/user/info/rdehler?...'
};

http.get(options, function(resp){
  resp.on('data', function(chunk){
    //do something with chunk
  });
}).on("error", function(e){
  console.log("Got error: " + e.message);
});

and I was given this error:

Express server listening on port 3000

events.js:72
throw er; // Unhandled ‘error’ event
^
Error: getaddrinfo ENOTFOUND
at errnoException (dns.js:37:11)
at Object.onanswer [as oncomplete] (dns.js:124:16)

Pretty dumb, the error explains the issue pretty clearly. ‘http://api.untappd.com/’ is not a valid hostname to pass into options.host, whereas ‘api.untappd.com’ is a valid hostname. Duh moment of the day.

Tagged | 1 Comment

Best Practice: Related Fields with Standard Controller Extensions

So you want to build a StandardController extension, and you’re considering using the StandardController.getRecord() method to get the record supplied by the Standard Controller. The problem is, it doesn’t pull in each of the fields you need to use, right? Now, before you give up and use StandardController.getId() method along with a manually specified list of all of the record’s and parent record fields you need, let’s see what we can do with the getRecord() method.

Consider this code snippet, a simple Controller Extension for Contact:

<apex:page standardController="Contact" extensions="ContactExt">
City: {!city}
</apex:page>

and

public class ContactExt {
    public Contact record {get; set;}
    public String city {
        get {
            return record.Account.BillingCity;
        }
        private set;
    }

    public ContactExt(ApexPages.StandardController sc) {
        record = (Contact)sc.getRecord();
    }
}

Load up /apex/ContactExt?id={some contact id from your org} and you will be greeted by this message:

SObject row was retrieved via SOQL without querying the requested field: Account.BillingCity

Well, how about we utilize the StandardController.addFields(List<String>) method:

public class ContactExt {
    public Contact record {get; set;}
    public String city {
        get {
            return record.Account.BillingCity;
        }
        private set;
    }

    public ContactExt(ApexPages.StandardController sc) {
        sc.addFields(new List<String> {'Account.BillingCity'});
        record = (Contact)sc.getRecord();
    }
}

You could extend it to dynamically list all fields and even parent fields if you wanted. It’s onerous, but it’s still better than manually doing a query using the sc.getId() value. Load up the VF page again, it works! Well, not so fast… try and create a test method. Here’s a sample:

    private static testMethod void testContactExt() {
        Account acct = new Account(Name = 'Test Account');
        insert acct;
        
        Contact contact = new Contact(LastName = 'Test', AccountId = acct.Id);
        insert contact;

        new ContactExt(new ApexPages.StandardController(contact));
    }

You’ll be greeted by this error when you run the test:

System.SObjectException: You cannot call addFields when the data is being passed into the controller by the caller.

There’s a known workaround for this method, which creates an untestable line but solves this issue:

    public ContactExt(ApexPages.StandardController sc) {
        if (!Test.isRunningTest()) {
            sc.addFields(new List<String> {'Account.BillingCity'});
        }
        record = (Contact)sc.getRecord();
    }

By definition, any line inside !Test.isRunningTest() is untestable, and there are a few patterns which require its use — testing callouts comes to mind. This is a workaround, not a good solution by any means.

(Wow, that was quite an introduction. Drumroll please…) There is a better workaround!

You don’t have to include a field in StandardController.addFields() to get it to be included and avoid that pesky SObject row was retrieved via SOQL without querying error. Simply include it in the visualforce page as an <apex:outputField>! Check out this snippet, which resolves the error in question:

<apex:page standardController="Contact" extensions="ContactExt">
<apex:outputField value="{!contact.Account.BillingCity}" />
City: {!city}
</apex:page>
public class ContactExt {
    public Contact record {get; set;}
    public String city {
        get {
            return record.Account.BillingCity;
        }
        private set;
    }

    public ContactExt(ApexPages.StandardController sc) {
        record = (Contact)sc.getRecord();
    }
}

No error! You don’t even need to use the outputField in your page, you can include rendered=”false” — but you do need to have it merge there to have the field pulled into getRecord().

Tagged , , , | Comments Off