Tell Me More: About the Code

There are two code objects behind our Visualforce custom action: the Apex class QuickOrderController and the Visualforce page QuickOrderPage.

The Apex class is the controller for the Visualforce page, and uses the @RemoteAction annotation on the methods. When you use this annotation, the Visualforce page wraps the logic in a JavaScript-friendly way. This is known as Visualforce remoting.

Visualforce remoting allows for quick and tight integration between Apex and JavaScript. This communication model works asynchronously as opposed to the synchronous model in the traditional Visualforce/Apex MVC paradigm. So after passing parameters into your controller, you can get the result from a response handler function, and write any additional client-side logic before doing any DOM manipulation or building your page with mobile templates or frameworks.

Visualforce remoting is ideal for mobile developers on the Salesforce1 Platform because it simplifies the direct server-side access to Salesforce objects and allows you to use Apex tools such as SOQL, Apex methods, and so on for rapid platform development. And, you don’t have to deal with view state, which makes pages perform better.

Apex Class QuickOrderController

This class uses Visualforce remoting and contains the logic called by the Visualforce page to find warehouses and create orders and line items.

1global class QuickOrderController{
2    public static List<Merchandise__c> merchandise;
3    public static Line_Item__c quickOrder;
4    
5    
6    public QuickOrderController(ApexPages.
7        StandardController controller){  
8    }
9    
10    @RemoteAction
11    global static List<Merchandise__c> findWarehouses(String accId,
12        String merchName, String warehouseDist){
13         merchandise = new List<Merchandise__c>();
14         String queryString = '';
15         
16         Account acc = [Select Location__Longitude__s, 
17         Location__Latitude__s, Name, Id 
18         from Account where Id =: accId];
19         
20         //Finds warehouses nearby if you have location 
21         //specified on the Account
22         if(acc.Location__Latitude__s != null && 
23             acc.Location__Longitude__s != null){
24             queryString = 'SELECT Id, (SELECT Id, Name, Quantity__c,
25                 Warehouse__r.Name, Warehouse__r.Id, 
26                 Warehouse__r.Street_Address__c, 
27                 Warehouse__r.City__c '+
28                 'FROM Merchandise__r WHERE Name 
29                 like \'%'+merchName+'%\') '
30                 +'FROM Warehouse__c WHERE '
31                 +'DISTANCE(Location__c, GEOLOCATION('
32                 +acc.Location__Latitude__s+','
33                 +acc.Location__Longitude__s+'), \'mi\')';
34             if(warehouseDist != null){
35                 queryString += ' <'+ warehouseDist;
36             }
37             
38         }
39         //If no location defined on the Account, this will run 
40         //query against the merchandise name only
41         else {
42             queryString = 'SELECT Id, Name, 
43                 Location__Longitude__s, 
44                 Location__Latitude__s, '
45                 +'(SELECT Id, Name, Warehouse__r.Name, 
46                 Quantity__c 
47                 FROM Merchandise__r WHERE Name 
48                 like \'%'+merchName+'%\') '
49                 +'FROM Warehouse__c limit 25';
50             
51         }
52         
53         //This creates a list of merchandise 
54         //to display in the search results
55         Warehouse__c[] warehouses = Database.Query(queryString);
56         for(Warehouse__c warehouse : warehouses){
57             Merchandise__c[] merch = 
58                 warehouse.getSObjects('Merchandise__r');
59                 if (merch != null) {
60                    for (Merchandise__c m : merch){ 
61                        merchandise.add(m);
62                    }
63                 }    
64         }
65         return merchandise;
66         
67    }
68    
69    //This remote action creates the invoice for the quick order
70    @RemoteAction
71    global static Line_Item__c createQuickOrder(
72        String accId, String merchandiseId){
73        Invoice__c newInvoice = new Invoice__c();
74        newInvoice.Account__c = accId;
75        insert newInvoice;
76        
77        quickOrder = new Line_Item__c();
78        Merchandise__c m = [Select Id, Name from Merchandise__c 
79            where Id=: merchandiseId limit 1];
80        quickOrder.Merchandise__c = m.Id;
81        quickOrder.Invoice__c = newInvoice.Id;
82        
83        return quickOrder;
84    }
85    
86    //This remote action creates the line item related to the 
87    //invoice for the quick order
88    @RemoteAction
89    global static Boolean insertQuickOrder(String o, String q){
90        try {
91            Integer quantity = integer.valueof(q);
92        
93        Line_Item__c order = new Line_Item__c();
94        /* The order variable being passed in as a param is being 
95        passed in the form of a JSON object. You need to use 
96        the JSON deserialize method in Apex to convert it 
97        into a SObject */
98        order = (Line_Item__c)JSON.deserialize(
99            o, Line_Item__c.class);
100        
101        order.Quantity__c = quantity;
102        insert order;
103
104    //Need to requery for the name for the post to chatter 
105    //since it wasn't explicitly specified
106    Line_Item__c li = [Select Name, Merchandise__r.Name, Id, 
107        Quantity__c, Invoice__c from Line_Item__c 
108        where Id =: order.Id];
109            
110        FeedItem post = new FeedItem();
111        post.ParentId = aId;
112        post.Body = UserInfo.getName() + ' just created a quick order';
113        post.type = 'LinkPost'; 
114        post.LinkUrl = '/' + li.Invoice__c;
115        post.Title = li.Merchandise__r.Name + ': ' + li.quantity__c;
116        insert post; 
117    } catch(System.Exception ex) {
118        system.debug(ex.getMessage());
119    }
120        return true;
121    }
122    
123    //This remote action handles deleting the invoice if 
124    //the user doesn't want to insert the line item
125    @RemoteAction
126    global static Boolean goBack(String invoiceId){
127        // Delete created invoice and return to original 
128        //search screen
129        Invoice__c cancelledInvoice = [select Id from Invoice__c 
130            where Id=: invoiceId];
131        delete cancelledInvoice;
132        
133        return true;
134    }
135    
136}
The Apex controller also has an insertQuickOrder method that creates a feed item about the new order in the account feed as shown in this code snippet. The feed item is a link post that links to the invoice.
1FeedItem post = new FeedItem();
2    post.ParentId = aId;
3    post.Body = UserInfo.getName() + ' just created a quick order';
4    post.type = 'LinkPost'; 
5    post.LinkUrl = '/' + li.Invoice__c;
6    post.Title = li.Merchandise__r.Name + ': ' + li.quantity__c;
7    insert post;

Visualforce Page QuickOrderPage

This page calls the controller with the user input and then displays the merchandise and warehouse information to the user. If the user wants to create an order, this page also calls the controller to create the order associated with the customer account and add a line item. At the beginning of the page, the code also does some styling of the page using the Salesforce mobile design templates.

1<apex:page standardController="Account" 
2    extensions="QuickOrderController" docType="html-5.0"
3    standardStylesheets="false"     showheader="false" sidebar="false">
4     
5    <!--Include stylesheets for the mobile look and feel -->
6    <apex:stylesheet value="{!URLFOR(
7        $Resource.Mobile_Design_Templates, 
8        'Mobile-Design-Templates-master/
9            common/css/app.min.css')}"/>
10    <apex:includeScript value="{!URLFOR(
11        $Resource.Mobile_Design_Templates, 
12        'Mobile-Design-Templates-master/common/js/
13            jQuery2.0.2.min.js')}"/>
14    <apex:includeScript value="{!URLFOR(
15        $Resource.Mobile_Design_Templates, 
16        'Mobile-Design-Templates-master/common/js/
17            jquery.touchwipe.min.js')}"/>
18    <apex:includeScript value="{!URLFOR(
19        $Resource.Mobile_Design_Templates, 
20        'Mobile-Design-Templates-master/common/
21            js/main.min.js')}"/>
22      
23      <style>
24          /* Default S1 color styles */
25          .list-view-header, .data-capture-buttons a {
26                background: -webkit-linear-gradient(
27                    #2a93d5,#107abb);
28                background: linear-gradient(#2a93d5,#107abb);
29                box-shadow: 0 1px 3px rgba(0,0,0,.2),
30                    inset 0 1px 0 rgba(255,255,255,.21);
31                color: white;
32                font-weight: bold;
33          }
34          
35          #resultPage, #searchPage {
36              padding-bottom: 50px;
37          }
38      </style>

The QuickOrderPage also calls the Force.com Canvas SDK to enable the publisher Submit button and close the publisher window.

First, it includes a reference to the SDK:
1<!-- This needs to be included so the publisher can be used 
2    to submit the action -->
3<script type='text/javascript' src='/canvas/sdk/js/publisher.js'></script>
Then it calls the setValidForSubmit method to enable the publisher Submit button:
1//This method will activate the publish button 
2//so the form can be submitted 
3Sfdc.canvas.publisher.publish({
4    name: "publisher.setValidForSubmit", 
5    payload:"true"});
After the setValidForSubmit is called and the user clicks Submit, this subscribe method fires. This method invokes the final JavaScript function which uses JavaScript remoting to insert the line item (thus completing the quick order) and then post a feed item to the account:
1<script type='text/javascript'>  
2    Sfdc.canvas.publisher.subscribe({name: "publisher.post", 
3        onData:function(e) {
4    //This subscribe fires when the user hits 
5    //Submit in the publisher
6    insertQuickOrder();
7    }});
8</script>
Finally, after the callback from the remoting method returns successfully, this method closes the publisher window:
1// Success - close the publisher and refresh the feed
2Sfdc.canvas.publisher.publish({name: "publisher.close", 
3    payload:{ refresh:"true"}});