Teach Me Salesforce

A community approach to learning salesforce.com

Archive for the ‘Code Sample’ Category

Apex Workshop Webinar 3: Collections

leave a comment »

In the third Apex Workshop Webinar we explored Collections: Lists, Sets and Maps.

In the exercise we again work with an Collections Exercises to practice manipulating Apex Collections (Answers to the exercises here). With Lists we learned different ways to populate a list while exploring the List Methods from the Apex Documentation. We practiced working with List indexes and expanded the concept to Lists of Lists and how to return values from anywhere in them. And we used a simple SOQL query to populate a list and noted how you must include any fields that you want to later reference.

From Lists we moved on to Sets. We described the differences between List (an ordered collection) and Sets (an unordered collection of unique value). We learned that with Sets, you can reference individual collection members by using the contains() Set Method and studied the Set Methods from the Apex Documentation. We also Lists into Sets and observed how duplicate values are automatically de-duped when added to Sets.

With Maps, we emphasize how very essential this collection type is to efficient Apex development. We explored different methods of putting key-value pairs in to Maps and retrieving the data with the keyset() and values() methods. And along the way we checked out the Map Methods from the Apex Documentation.

Through out the exercises, we used the Apex test methods of assert() and assertEquals() to test our work and to familiarize ourselves with unit testing.

Unfortunately we had technical difficulties with the recording and lost the first 20 minutes, but the most important discussions are in the 84 minutes we captured.

Advertisements

Written by Always Thinkin

March 20, 2017 at 7:49 pm

Posted in Apex, Beginner, Code Sample

Simplest Apex Trigger Patterns: Updates

leave a comment »

Last time I shared two very simple triggers for modifying records on Insert, which is to say, when the record is created. Which only happens once. So most of the time you probably want triggers that are working on Update since that happens a lot more often. In fact, the After Insert trigger I posted last time results in an Update event on the records; but the Before Insert does not. Why? Well it’s sort of like this: before the record is inserted, it doesn’t exist, so any change you make to it is just all wrapped up in that insert event. But after insert, the record exists in the database and any change you make to it is considered an update. But now we’re getting complex and this is suppose to be about simple patterns, so here you go…

The Before Update triggers can be written just like Before Insert triggers: if you want to set a value on a field, you just need to say SomeField = “some value”. Here we are setting the Email Opt Out checkbox (API name is HasOptedOutOfEmail) to true, so as a Boolean, we don’t need quotes, just true or false.

trigger ContactBeforeUpdate on Contact (before update) {
    For(Contact c : Trigger.new){
        c.HasOptedOutOfEmail = true;
    }
}

But After Update triggers get tricky fast. First there’s the need to use “DML” or Data Manipulation Language to perform an additional update to the just-updated record. We did this in the first post on Inserts too. And if we do another update that means the record will go through the Update events again and you’re going to get recursion – it’s going to keep trying to update over and over!

We’re going to do a very simple way of avoiding recursion, but using a condition like we did in the last post on Trigger Conditions. Since we are setting Email Opt Out to be true, our condition will only do this for Contacts on which Email Opt Out is false. So after our first update to the record, the next time through the After Update event, the record doesn’t meet the condition and no further updates to the record are executed.

trigger ContactAfterUpdate on Contact (after update){
    List<Contact> consToUpdate = new List<Contact>();
    
    for(Contact conInTrigger : trigger.new){
        if(!conInTrigger.HasOptedOutOfEmail){
            Contact c = new Contact(Id = conInTrigger.Id, HasOptedOutOfEmail = true);
        	consToUpdate.add(c); 
        }
    }
    update consToUpdate;
}

Now, let’s look at that line by line to see what’s going on:

trigger ContactAfterUpdate on Contact (after update){

This is the busiest line. It tells Apex that the code is a trigger (must be this, changing it will just break it), then it sets the name of this particular trigger (your choice, pretty much anything is allowed), then is says which object the trigger is on (but only one object!) and finally in the parentheses it tells Apex which events the trigger should be executed on (can be any or all: before insert, before update, after insert, after update, before delete, after delete & after undelete). Oh yeah, and a must-have curly bracket to start things off right.

List<Contact> consToUpdate = new List<Contact>();

This line prepares a list for the Contacts that we will want to update later. It starts out empty and we’ll add Contacts to it.

for(Contact conInTrigger : Trigger.new){

This line is the start of the “for loop” and is necessary because you might just be dealing with lots of records, not just one. Think of a situation where you’re uploading hundreds of new Contacts and need to evaluate each one independently. The for loop allows you to do that by saying “for each single thing in this collection of things, do something”. In this case it is saying: for each Contact (which I’m going to nickname “conInTrigger”) that is inserted at the same time (Trigger.new is a list of all the inserted Contacts) do what ever I tell you between this next set of curly braces.

if(!conInTrigger.HasOptedOutOfEmail){

This line is the conditional (like a Workflow filter criteria) and says “as you go through each record in the for loop, which we nicknamed ‘conInTrigger’, get the value of HasOptedOutOfEmail…and make it the opposite. That’s what the exclamation point is doing in front of conInTrigger.HasOptedOutOfEmail: changing true to false and false to true. The if() condition only proceeds if it’s true, so since we only want to change HasOptedOutOfEmail to true for records that were false, we use that value to check first. And this helps us to prevent repeated updates to the same record.

Contact c = new Contact(Id = conInTrigger.Id, HasOptedOutOfEmail = true);

Here we finally get around to updating our record. To do this we have to tell Apex that we want to work with a Contact, which we’ll call “c”. The next part if a little counterintuitive because you say first new Contact, but then in the parentheses, you give tell Apex that this Contact already has an Id. If we were actually creating a new Contact, we wouldn’t provide the Id, that would get added after its inserted. Here we use the “conInTrigger” nickname to refer to the Contact record that is in this iteration of the for loop and .Id to get its record ID. After that, we are going to set a new value, so we don’t need “conInTrigger” again, we just say that to set this Contact’s HasOptedOutOfEmail field equal to true.

consToUpdate.add(c); 

The next line adds this Contact record to the consToUpdate List we created on Line 2. If we have updated more than one Contact (maybe using Mass Edits in a List View), the for loop goes on to the next contact and keeps going until all Contacts that were updated together have been addressed.

update consToUpdate;

Our final line tells Apex that we want to update any and all Contact records that we’ve added to the consToUpdate list.

The Unit Test

Now that we’re updating records, our unit test has to first prepare some test records by inserting them and then update those same records so we can confirm that our trigger works. It’s a good idea to make sure that the trigger is actually doing the work by verifying that before the update, Email Opt Out is false and that after the update, Email Opt Out is true so we know that it was caused by the update.

 

@isTest
public class ContactAfterUpdateTest {
    public static testMethod void testContactUpdate(){
        Contact aNewContact = new Contact(LastName = 'New-Contact');
        insert aNewContact;
        
        //first let's confirm that Email Opt Out is false
        Contact theInsertedContact = [Select HasOptedOutOfEmail from Contact limit 1];
        System.assertEquals(false, theInsertedContact.HasOptedOutOfEmail);
        
        aNewContact.FirstName = 'Some First Name';
        update aNewContact;
        
        //now that we've updated the Contact, we'll confirm that 
        Contact theUpdatedContact = [Select HasOptedOutOfEmail from Contact limit 1];
        System.assertEquals(true, theUpdatedContact.HasOptedOutOfEmail);
    }
}

Written by Always Thinkin

April 2, 2016 at 1:39 pm

Simplest Apex Patterns: Trigger Conditions

with one comment

The Trigger

Triggers fire every time for every change: inserts, updates, deletes…and it’s pretty rare that you want to execute your actions for every change. So that means you need to code in some conditions to only change the records you want changed. Just like building Workflow Rules with Rule Criteria, in Apex you’ll want to add criteria and for that you need if() statements. Probably the trickiest thing about learning to use if() statements is remember that you have to use two equals signs to say “this equals that”. Why, well, it’s just common coding syntax because you need a way to distinguish between the idea of “make something equal some value” (e.g. x = 5) and “check if two things are already equal” (e.g. a == b). And the second trickiest thing is remember whether you use single quotes, double quotes or no quotes because it depends on whether you’re criteria is checking text, numbers, IDs or booleans.

But enough chit-chat, let’s just take a look at the ContactBeforeInsert trigger we made last time with it’s new filter criteria condition that says “if the contact’s last name is ‘Hates-email'” then we want to opt them out of email.

trigger ContactBeforeInsert on Contact (before insert) {
	for(Contact c : Trigger.new){
            if(c.LastName == 'Hates-email'){
                c.HasOptedOutOfEmail = true;
        }
    }
}

Let’s break that down line-by line:

trigger ContactBeforeInsert on Contact (before insert) {

This is the busiest line. It tells Apex that the code is a trigger (must be this, changing it will just break it), then it sets the name of this particular trigger (your choice, pretty much anything is allowed), then is says which object the trigger is on (but only one object!) and finally in the parentheses it tells Apex which events the trigger should be executed on (can be any or all: before insert, before update, after insert, after update, before delete, after delete & after undelete). Oh yeah, and a must-have curly bracket to start things off right.

for(Contact c : Trigger.new){

This line is the start of the “for loop” and is necessary because you might just be dealing with lots of records, not just one. Think of a situation where you’re uploading hundreds of new Contacts and need to evaluate each one independently. The for loop allows you to do that by saying “for each single thing in this collection of things, do something”. In this case it is saying: for each Contact (which I’m going to nickname “c”) that is inserted at the same time (Trigger.new is a list of all the inserted Contacts) do what every I tell you between this next set of curly braces.

if(c.LastName == 'Hates-email'){

Now we finally get to our filter criteria conditional which says “as you go through each record in the for loop, check whether it’s last name is ‘Hates-email’ and if so, then do whatever is between the next set of curly braces. And if not, don’t do what’s in the curly braces, just go to the next record. This is where our nickname of “c” for Contacts is used because c.LastName means “this contact’s last name”. And those double equal signs say to check that the Last Name is already equal to ‘Hates-email’.

c.HasOptedOutOfEmail = true;

And at last we get around to doing something: setting Email Opt Out to true. Again we use our nickname for Contacts, “c”, to say set this Contact’s Email Opt Out field (which we refer to in Apex by the field’s API Name of HasOptedOutOfEmail) to true (see how that single equal signs is saying “make it equal…”?).

After that, we just have to close up all those curly braces we started earlier.

The Unit Test

So of course to put this in production, you need a unit test so you can get the code coverage. More importantly, you want to know that it works and for that you need to two unit tests because now we have a conditional. So we have one tests that confirms that if the last name is “Hates-email” that the Email Opt Out is set to true. And a second test that checks if last name is something else, that Email Opt Out is false.

@isTest
public class ContactBeforeInsertTest {
    public static testMethod void testHatesEmail(){
        
        Contact aNewContact = new Contact(LastName = 'Hates-email');
        
        Insert aNewContact;
        
        Contact theInsertedContact = [Select HasOptedOutOfEmail from Contact limit 1];
        
        System.assert(theInsertedContact.HasOptedOutOfEmail);
        
    }
    
    public static testMethod void testLikesEmail(){
        
        Contact aNewContact = new Contact(LastName = 'Likes-email');
        
        Insert aNewContact;
        
        Contact theInsertedContact = [Select HasOptedOutOfEmail from Contact limit 1];
        
        System.assert(!theInsertedContact.HasOptedOutOfEmail);
        
    }
}

Written by Always Thinkin

March 20, 2016 at 6:45 pm

Simplest Apex Trigger Patterns: Inserts

with 3 comments

The Trigger

Because sometimes you just want to see the basic programming patterns for Apex and reverse engineer them to fit your needs.

These two are the most basic trigger patterns. They both do the same thing: set a field’s value on a record when that record is added to Salesforce. It only happens on insert, not on update because that’s slightly less simple (and a different post). The only difference is one does it Before Insert and one does it After Insert. Why? Because they can. Maybe someday we’ll discuss here why you use one or the other but today we just want code that works, so here you go:

Trigger ContactBeforeTrigger on Contact (before insert) {
    for(Contact c : Trigger.new){
        c.HasOptedOutOfEmail = true;
    }
}

 

Trigger ContactAfterInsert on Contact (after insert) {
    List<Contact> conList = new List<Contact>();
    
    for(Contact conInTrigger : trigger.new){
        Contact c = new Contact(Id = conInTrigger.Id, HasOptedOutOfEmail = true);
        conList.add(c);    
    }
    update conList;
}

 

The Unit Test

And of course anything you want to use in Production needs a test class, so here you go, one that works for both!

@isTest
public Class TestContactTrigger{
    public static testMethod void testContactInsert(){
    
    Contact aNewContact = new Contact(LastName = 'qwerty');
    
    Insert aNewContact;
    
    Contact theInsertedContact = [Select HasOptedOutOfEmail from Contact limit 1];
    
    System.assert(theInsertedContact.HasOptedOutOfEmail);
    
    }

}

Written by Always Thinkin

February 28, 2016 at 4:53 pm

Unit Tests: Not Just for Dev – Idea 1

leave a comment »

Unit tests offer much more than just the means to get 75%+ code coverage, and they can be used to protect more than just Apex. Read the intro to this series in Idea 0.

Our first example is the simplest: protecting fields that you don’t want deleted. A use case for this is External Integrations that rely on Salesforce fields. If someone tries to delete the fields, you want to ensure that they are alerted to the impact. By including the fields Current Generators and SIC Code in the example Unit Test below, an attempt to delete the fields will throw an error referencing the unit test.

@isTest
private class UnitTest1 {
    static testMethod void protectFields(){

    /*Create a new Lead, populating any Fields you need to protect
    * In this case, we want to ensure Current Generators and SIC Code cannot be deleted
    * or the field type changed by including them in this Apex Unit Test
    */
    Lead l = new Lead(Company = 'Test Lead',
                      LastName = 'Lead Last Name',
                      CurrentGenerators__c = 'Generator X',
                      SICCode__c = '1234-ABC');
    insert l;
    }
}

Let’s describe what’s happening here.

We start off with the all-important @isTest flag. This tells Salesforce that the Apex Class contains only test Methods. That means nothing you do in the Class will be saved to the server, and will not delete or change anything on the server. It also means you cannot use most records that exist on the server, so you have to create your own test records.

@isTest means that Salesforce will recognize it as a test Class, and when you Run All Tests, it will know to run this test too.

Next is the Class Name, UnitTest1 and its access modifier is set to “private”. Access modifiers are irrelevant for Unit Tests, private is the default.

The actual test Method here is protectFields(). Your test exists between the curly braces {}. Here you must create your test records and perform any actions you want tested. The critical item here is “testMethod” which ensures that Salesforce recognizes the method as performing tests. Without this, Salesforce will not report a test failure regardless of the outcome.

In this case we are creating a Lead. We are using standard Apex code to create a Lead, to populate fields on that Lead, and finally to use DML to insert the Lead. Required fields and Validation Rules will get enforced when you create the Lead, so be sure to include any fields affected by them.

Once this Unit Test is saved to the server, any attempt to delete the field or change its data type will be blocked with a reference to this Unit Test.

For this limited scenario, that’s all we need!

Written by Always Thinkin

August 3, 2014 at 3:53 pm

Posted in Apex, Code Sample, Intermediate

Tagged with ,

Salesforce Trigger when Rollups Summaries Not Possible

with 6 comments

Master-Details relationships in Force.com  are very handy but don’t fit every scenario. For instance, it’s not possible to implement a rollup summary on formula field or text fields. Here’s a small trigger that you can use for a starter for these types of situations. The code for each class is available at GitHub for your forking pleasure.

So here’s the (not very useful) use case. Sales Order is the Master object which can have multiple Sales Order Items (detail object). The Sales Order Item has a “primary” Boolean field and a “purchased country” field. Each time Sales Order Items are inserted or updated, if the Sales Order Item is marked as “primary” then the value of “purchased country” is written into the “primary country” field on the Sales Order. I’m assuming that there can only be one Sales Order Item per Sales Order that is marked as primary. Essentially this is just a quick reference on the Sales Order to see which country is primary on any of the multiple Sales Order Items. Not very useful but illustrative.

The code is broken down into a Trigger and an Apex “handler” class that implements the actual functionality. It’s best practice to only have one trigger for each object and to avoid complex logic in triggers. To simplify testing and resuse, triggers should delegate to Apex classes which contain the actual execution logic. See Mike Leach’s excellent trigger template for more info.

SalesOrderItemTrigger (source on GitHub) – Implements trigger functionality for Sales Order Items. Delegates responsibility to SalesOrderItemTriggerHandler.

trigger SalesOrderItemTrigger on Sales_Order_Item__c (after insert, after update) {

  SalesOrderItemTriggerHandler handler = new SalesOrderItemTriggerHandler();
    
  if(Trigger.isInsert && Trigger.isAfter) {
    handler.OnAfterInsert(Trigger.new);
    
  } else if(Trigger.isUpdate && Trigger.isAfter) { 
    handler.OnAfterUpdate(Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap);
    
  }

}

SalesOrderItemTriggerHandler (source on GitHub) – Implements the functionality for the sales order item trigger after insert and after update. Looks at each sales order item and if it is marked as primary_item__c then moves the primary_country__c value from the sales order item to the associated sales order’s primary_country__c field.

public with sharing class SalesOrderItemTriggerHandler {

  // update the primary country when new records are inserted from trigger
  public void OnAfterInsert(List newRecords){
    updatePrimaryCountry(newRecords); 
  }
  
  // update the primary country when records are updated from trigger  
  public void OnAfterUpdate(List oldRecords, 
      List updatedRecords,  Map oldMap, 
      Map newMap){
    updatePrimaryCountry(updatedRecords); 
  }
  
  // updates the sales order with the primary purchased country for the item
  private void updatePrimaryCountry(List newRecords) {
    
    // create a new map to hold the sales order id / country values
    Map salesOrderCountryMap = new Map();
    
    // if an item is marked as primary, add the purchased country
    // to the map where the sales order id is the key 
    for (Sales_Order_Item__c soi : newRecords) {
      if (soi.Primary_Item__c)
        salesOrderCountryMap.put(soi.Sales_Order__c,soi.Purchased_Country__c);
    } 
    
    // query for the sale orders in the context to update
    List orders = [select id, Primary_Country__c from Sales_Order__c 
      where id IN :salesOrderCountryMap.keyset()];
    
    // add the primary country to the sales order. find it in the map
    // using the sales order's id as the key
    for (Sales_Order__c so : orders)
      so.Primary_Country__c = salesOrderCountryMap.get(so.id);
    
    // commit the records 
    update orders;
    
  }

}

Test_SalesOrderItemTriggerHandler (source on GitHub) – Test class for SalesOrderItemTrigger and SalesOrderItemTriggerHandler. Achieves 100% code coverage.

@isTest
private class Test_SalesOrderItemTriggerHandler {

  private static Sales_Order__c so1;
  private static Sales_Order__c so2;

  // set up our data for each test method
  static {
  	
  	Contact c = new Contact(firstname='test',lastname='test',email='no@email.com');
  	insert c;
    
    so1 = new Sales_Order__c(name='test1',Delivery_Name__c=c.id);
    so2 = new Sales_Order__c(name='test2',Delivery_Name__c=c.id);
    
    insert new List{so1,so2};
    
  }

  static testMethod void testNewRecords() {
 
    Sales_Order_Item__c soi1 = new Sales_Order_Item__c();
    soi1.Sales_Order__c = so1.id;
    soi1.Quantity__c = 1;
    soi1.Description__c = 'test';
    soi1.Purchased_Country__c = 'Germany';
 
    Sales_Order_Item__c soi2 = new Sales_Order_Item__c();
    soi2.Sales_Order__c = so1.id;
    soi2.Quantity__c = 1;
    soi2.Description__c = 'test';
    soi2.Purchased_Country__c = 'France';
    soi2.Primary_Item__c = true;
    
    Sales_Order_Item__c soi3 = new Sales_Order_Item__c();
    soi3.Sales_Order__c = so2.id;
    soi3.Quantity__c = 1;
    soi3.Description__c = 'test';
    soi3.Purchased_Country__c = 'Germany';
    soi3.Primary_Item__c = true;
     
    Sales_Order_Item__c soi4 = new Sales_Order_Item__c();
    soi4.Sales_Order__c = so2.id;
    soi4.Quantity__c = 1;
    soi4.Description__c = 'test';
    soi4.Purchased_Country__c = 'Germany';
    
    Sales_Order_Item__c soi5 = new Sales_Order_Item__c();
    soi5.Sales_Order__c = so2.id;
    soi5.Quantity__c = 1;
    soi5.Description__c = 'test';
    soi5.Purchased_Country__c = 'Italy';
            
    insert new List{soi1,soi2,soi3,soi4,soi5}; 
     
    System.assertEquals(2,[select count() from Sales_Order_Item__c where Sales_Order__c = :so1.id]);
    System.assertEquals(3,[select count() from Sales_Order_Item__c where Sales_Order__c = :so2.id]); 
    
    System.assertEquals('France',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);
    System.assertEquals('Germany',[select primary_country__c from Sales_Order__c where id = :so2.id].primary_country__c);
 
  }
  
  static testMethod void testUpdatedRecords() {
    
    Sales_Order_Item__c soi1 = new Sales_Order_Item__c();
    soi1.Sales_Order__c = so1.id;
    soi1.Quantity__c = 1;
    soi1.Description__c = 'test';
    soi1.Purchased_Country__c = 'Germany';
 
    Sales_Order_Item__c soi2 = new Sales_Order_Item__c();
    soi2.Sales_Order__c = so1.id;
    soi2.Quantity__c = 1;
    soi2.Description__c = 'test';
    soi2.Purchased_Country__c = 'France';
    soi2.Primary_Item__c = true;
    
    insert new List{soi1,soi2}; 
    
    // assert that the country = France
    System.assertEquals('France',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);
    
    List items = [select id, purchased_country__c from Sales_Order_Item__c 
      where Sales_Order__c = :so1.id and primary_item__c = true];
    // change the primary country on the sales order item. should trigger update
    items.get(0).purchased_country__c = 'Denmark';
   
    update items;
    // assert that the country was successfully changed to Denmark
    System.assertEquals('Denmark',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);
 
  }
  
}

Written by Jeff Douglas

August 23, 2011 at 9:00 am

Posted in Apex, Code Sample, Trigger

JavaScript Remoting

with 2 comments

One of the major enhancements from visualforce this release(Summer 11) and my favorite is JavaScript remoting.

JavaScript Remoting is the ability to invoke apex class method from javascript embedded in a visualforce page.JavaScript Remoting is now available after summer 11 release.

Here is a quick example : we have a Apex class which defines a get Account method.

global with sharing class MyJSRemoting
{
    public static Account account { get; set; }

    @RemoteAction
    global static Account getAccount(String accountName) {
        account = [select id, name, phone, type, numberofemployees from
             Account where name = :accountName limit 1];
        return account;
    }
}

This method is having @RemoteAction annotation making it available to be called from a visualforce page. Now in our visualforce page we have a script tag set which is how we embed javascript within visualforce pages.

<apex:page controller="MyJSRemoting">

<script type="text/javascript">
function getAccountJS() {
    var accountNameJS = document.getElementById('accountname').value;

    ankit.MyJSRemoting.getAccount( accountNameJS, function(result, event)
    {
        if (event.status)
        {
           document.getElementById("{!$Component.theBlock.pbs.pbsi2.accId}")
                                  .innerHTML = result.Id;
           document.getElementById("{!$Component.theBlock.pbs.pbsi1.name}")
                                  .innerHTML = result.Name;
        }
    }, {escape:true});
}
</script>

    <input id="accountname" type="text">

    <button onclick="getAccountJS();">Get Account</button>

    <apex:pageblock id="theBlock">
        <apex:pageblocksection columns="2" id="pbs">

            <apex:pageblocksectionitem id="pbsi1">
                 <apex:outputtext id="name">
            </apex:outputtext></apex:pageblocksectionitem>

            <apex:pageblocksectionitem id="pbsi2">
                <apex:outputtext id="accId">
            </apex:outputtext></apex:pageblocksectionitem>

        </apex:pageblocksection>
    </apex:pageblock>
</apex:page>

This JavaScript code then invokes the getAccount method in the apex class to take action.

Please note I have used namespace “ankit.MyJSRemoting” as there is a namespace registered on my organization.

JavaScript Remoting is the feature which is primarily be used by developers and will allow them to create richer and more interactive user interfaces that everyone will benefit from.

In particular, lets developers create rich javascript based user interfaces by invoking apex controller methods within JavaScript code. JavaScript is typically used to render small parts of user interface. Result from a Javascript remoting call are much faster then using a typical visualforce reRender model, developers can provide more instant feedback to the user. This is because remoting calls are stateless. Means only data is set back and forth instead a lot of bulky HTML.

Additional Visualforce enhancements :

1) Filtered Lookup via Visualforce.
2) Inline editing for rich text area field.
3) Field Set property accessors.

This is a cross post to my Blog.

Written by Ankit Arora (forceguru)

June 19, 2011 at 4:33 pm