3

I started writing some code using apex functions and then I thought let's try it out with Javascript remoting.

Remoting method was called from my VF page and all worked fine, until I tried to use local variable inside the remoting method. Here is my class:

global class MyClass
{
    public static List <Account> accounts {get; set;}

    public void init()
    {
        accounts = [SELECT Id, Name FROM Account]; 
    }

    @RemoteAction
    global static Account findAccount(String accountName)
    {
        for(Account acc : accounts)
        {
             if(acc.Name == accountName)
                 return acc;
        }

        return new Account();
    }
}

This is the code in my VF page:

var accountName = 'Test';

Visualforce.remoting.Manager.invokeAction(
    '{!$RemoteAction.MyClass.findAccount}',
    accountName,
    function(result, event)
    {
        if (event.status) 
        {
            console.log(result);
        }
        else if (event.type === 'exception')
        {
            alert('Error');
        }
        else
        {
            alert('Error');
        }
    }, 
    {escape: true}
);

When I debug the "accounts" in the remoting method, the list is empty. Is there anything that I'm missing here?

I have tried making the list static too and accessing it by referencing the class name but still no luck.

Please note that this is a simple example, the actual code is a bit different and the init method is called from the VF page through the action parameter of the page tag.

Appreciate your help!

  • This is a very vague title for your question – Daniel Blackhall Nov 29 '12 at 5:11
  • Is that better? :) Apologies for that, was in a rush... – Boris Bachovski Nov 29 '12 at 5:17
2

There are a couple of things.

  1. In Apex, init() methods are not called by default. Any code you want to run during the instantiation of an Object must go in the constructor, see Using Constructors. Since JavaScript Remoting requires your methods to be Static, setting Instance Variables will not do you any good.
  2. Your static method, "findAccounts" is trying to reference an instance variable, "accounts". I got a compile error when initially trying to save your class. If you want to reference the "accounts" variable from within another static method, make sure to define this as a static list.
  3. Since init() is never getting called, the list of accounts will be null. If you call this init() method within the findAccounts method, then the data will be populated. Note, that you must change init() to be a static method then, as well. As referenced above, it's generally not a best practice to query all objects into memory, as you will soon hit governor limits for the total number of records retrieved by SOQL queries of 50k query rows.

If you put all that together, this should get you at least a working example.

global class MyClass
{
    public static List <Account> accounts = new List <Account>();

    public static void init()
    {
        accounts = [SELECT Id, Name FROM Account]; 
    }

    @RemoteAction
    global static Account findAccount(String accountName)
    {
        init();
        for(Account acc : accounts)
        {
             if(acc.Name == accountName)
                 return acc;
        }

        return new Account();
    }
}

Note, that this will infact requery the database everytime findAccounts is called. A design pattern I like to use, and have seen a lot looks something like the following:

public static List <Account> accounts;
public static void setAccounts()
{
    if (accounts == null){
        accounts = new List<Account>();
        accounts = [SELECT Id, Name FROM Account]; 
    }
}

This will ensure that the query is only run once within the lifespan of the request.

You also may want to utilize a Map<String, Account> accountNameToAccountMap see Apex Maps, so that you can avoid iterating through the list every single time. But, I'll leave that as an exercise.

  • As I said in my comment for the previous answer, this was simple example. The problem is not in the init() or somewhere else, I am calling the init method in the action parameter of the VF page. Also I said that I've tried making the list static and it's still empty when I try to access it from the remoting method. Thanks for your help though! – Boris Bachovski Nov 29 '12 at 4:36
  • @e-bacho2.0 per the docs on action parameter, "This action should not be used for initialization." salesforce.com/us/developer/docs/pages/Content/… I'd suggest taking a look at my code, as I've confirmed it's working in my org. :) – Mikey Nov 29 '12 at 4:48
  • calling init() in the remoting method worked! – Boris Bachovski Nov 29 '12 at 5:17
0

A couple things stand out to me here.

1) Querying for all accounts and storing them in a global variable like that, you will hit a governor limit eventually.

2) I'm not familiar with that method of calling a remote action. I have always seen it in the form: ClassName.methodName([parameters], function(result, event))

Having said that, this should work for you:

Apex:

public with sharing class MyClass {

    @RemoteAction
    public static Account findAccount(String accountName) {
        return [SELECT Id, Name 
                  FROM Account 
                 WHERE Name = :accountName 
                 LIMIT 1];
    }

}

VF:

...
<script>
  var REMOTING_EXCEPTION = 'exception';

  function findAccount(accountName) {
    MyClass.findAccount(accountName, function(result, event) {
      if(event.type === REMOTING_EXCEPTION) {
        alert('something went wrong see console for more');
        console.log(event.message);
      } else if(result) {
        alert('successfully retrieved.  check console for more.');
        console.log(result);
      } else {
        alert('could not find specified account');
      }
    });
  }

  findAccount('Test');
</script>
...
  • Given above was a simple example, just wrote the code quickly, no dramas about the governor limits :). I have also tried calling the method with Controller.Method([params], resulthandler but still no luck. Thing is, I don't want to re-query to return a result. I would like to use a list that is already prepopulated on init(). – Boris Bachovski Nov 29 '12 at 4:18
  • I have corrected a defect in my example and have verified it works on my end. Do you get any console.log statements in your js console ever? In regards to not wanting to re-query, javascript remoting works best when you start storing state client side and injecting it into your remoting calls. So I would just query for all your accounts on dom ready using remoting, store the results in a list client side, and then findAccounts() would be a javascript method like the apex version you've written. – Phil Rymek Nov 29 '12 at 4:37

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.