JavaScript Remoting と静的 HTML
この方法で設計された Visualforce ページは、要求-応答サイクルに対する制御が強化され、ページの再読み込みの代わりに JavaScript を使用したページの更新が優先されるため、標準の Visualforce の簡略化された自動機能���多くが使用されません。このアプローチはページのパフォーマンスを大幅に高めることができます。帯域幅が狭く、遅延の大きいワイヤレスネットワーク接続でモバイルデバイスを文字通りモバイルとして使用する場合は特に有効です。この方法の欠点は記述するコードが増えることで、Apex と Visualforce のほか、JavaScript、JavaScript Remoting、HTML5、ご使用のモバイルツールキット、CSS などの専門知識が必要です。一方、利点は、モバイル開発の最新鋭のツールを使用して作業できることと、この手法で構築したページにより、アプリケーションと完全統合するカスタム機能を最も適切で最も完璧な方法で組み込めることです。
デスクトップ用の Visualforce ページも、Salesforce アプリケーション用のページもこのアプローチで構築できます。さらに、このアプローチによるページは、スタイル設定をカスタマイズすれば 2 つの環境で共有することも可能です。ただし、Salesforce フルサイトのデザインに近づけることは容易なことではありません。最も重要なことは、設計するページの応答性を最大限に高められることと、幅広いデバイスやフォームファクタに適合させられることです。
このアプローチの Visualforce ページへの適用
Salesforce アプリケーションのページの作成にこのアプローチを採用する場合は、次の一般的なプロセスに従います。
- 任意の Salesforce Mobile パック (Salesforce で入手可) を静的リソースとして組織にインストールします。
- ページの docType を html-5.0 に設定します。標準のスタイルシートとヘッダーを無効にすることを強くお勧めします。次に例を示します。
1<apex:page standardController="Warehouse__c" 2 extensions="WarehouseEditor" 3 showHeader="false" standardStylesheets="false" 4 docType="html-5.0"> - Visualforce リソースタグを使用して、選択したモバイルツールキットのスクリプトとスタイルをページに追加します。次に例を示します。
1<apex:includeScript 2 value="{!URLFOR( 3 $Resource.Mobile_Design_Templates, 4 'Mobile-Design-Templates-master/common/js/ 5 jQuery2.0.2.min.js' 6 )}"/> - HTML5 とモバイルツールキットのタグおよび属性を使用して、ページのスケルトンを作成します。
- Javascript 関数を、ユーザ操作に応答するハンドラとしてページに追加します。JavaScript Remoting を使用して、Apex @RemoteAction メソッドをコールします。このメソッドは、レコードの取得、DML の実行などを行います。
- ユーザアクションやページ更新を処理する Javascript 関数を追加します。HTML 要素を JavaScript で構築してから、ページのスケルトンに追加して、ページ更新を実行します。
JavaScript Remoting と静的 HTML ページの例
1<apex:page standardController="Warehouse__c" extensions="WarehouseEditor"
2 showHeader="false" standardStylesheets="false"
3 docType="html-5.0" applyHtmlTag="false" applyBodyTag="false">
4
5 <!-- Include Mobile Toolkit styles and JavaScript -->
6 <apex:stylesheet
7 value="{!URLFOR($Resource.Mobile_Design_Templates,
8 'Mobile-Design-Templates-master/common/css/app.min.css')}"/>
9 <apex:includeScript
10 value="{!URLFOR($Resource.Mobile_Design_Templates,
11 'Mobile-Design-Templates-master/common/js/jQuery2.0.2.min.js')}"/>
12 <apex:includeScript
13 value="{!URLFOR($Resource.Mobile_Design_Templates,
14 'Mobile-Design-Templates-master/common/js/jquery.touchwipe.min.js')}"/>
15 <apex:includeScript
16 value="{!URLFOR($Resource.Mobile_Design_Templates,
17 'Mobile-Design-Templates-master/common/js/main.min.js')}"/>
18
19<head>
20<style>
21 html, body, p { font-family: sans-serif; }
22 input { display: block; }
23</style>
24
25<script>
26 $(document).ready(function(){
27 // Load the record
28 loadWarehouse();
29 });
30
31 // Utility; parse out parameter by name from URL query string
32 $.urlParam = function(name){
33 var results = new RegExp('[\\?&]' + name + '=([^&#]*)')
34 .exec(window.location.href);
35 return results[1] || 0;
36 }
37
38 function loadWarehouse() {
39 // Get the record Id from the GET query string
40 warehouseId = $.urlParam('id');
41
42 // Call the remote action to retrieve the record data
43 Visualforce.remoting.Manager.invokeAction(
44 '{!$RemoteAction.WarehouseEditor.getWarehouse}',
45 warehouseId,
46 function(result, event){;
47 if(event.status){
48 console.log(warehouseId);
49 $('#warehouse_name').text(result.Name);
50 $('#warehouse_address').val(
51 result.Street_Address__c);
52 $('#warehouse_city').val(result.City__c);
53 $('#warehouse_phone').val(result.Phone__c);
54 } else if (event.type === 'exception'){
55 console.log(result);
56 } else {
57 // unexpected problem...
58 }
59 });
60 }
61
62 function updateWarehouse() {
63 // Get the record Id from the GET query string
64 warehouseId = $.urlParam('id');
65
66 // Call the remote action to save the record data
67 Visualforce.remoting.Manager.invokeAction(
68 '{!$RemoteAction.WarehouseEditor.setWarehouse}',
69 warehouseId, $('#warehouse_address').val(),
70 $('#warehouse_city').val(),
71 $('#warehouse_phone').val(),
72 function(result, event){;
73 if(event.status){
74 console.log(warehouseId);
75 $('#action_status').text('Record updated.');
76 } else if (event.type === 'exception'){
77 console.log(result);
78 $('#action_status').text(
79 'Problem saving record.');
80 } else {
81 // unexpected problem...
82 }
83 });
84 }
85
86</script>
87</head>
88
89<body>
90
91<div id="detailPage">
92 <div class="list-view-header" id="warehouse_name"></div>
93 <div id="action_status"></div>
94
95 <section>
96 <div class="content">
97 <h3>Warehouse Details</h3>
98 <div class="form-control-group">
99 <div class="form-control form-control-text">
100 <label for="warehouse_address">
101 Street Address</label>
102 <input type="text" id="warehouse_address" />
103 </div>
104 <div class="form-control form-control-text">
105 <label for="warehouse_city">City</label>
106 <input type="text" id="warehouse_city" />
107 </div>
108 <div class="form-control form-control-text">
109 <label for="warehouse_phone">Phone</label>
110 <input type="text" id="warehouse_phone" />
111 </div>
112 </div>
113 </div>
114 </section>
115
116 <section class="data-capture-buttons one-buttons">
117 <div class="content">
118 <section class="data-capture-buttons one-buttons">
119 <a href="#" id="updateWarehouse"
120 onClick="updateWarehouse();">save</a>
121 </section>
122 </div>
123 </section>
124</div> <!-- end detail page -->
125
126</body>
127
128</apex:page>このページは、Salesforce フルサイトでも使用できますが、Salesforce アプリケーションページとして設計されているため、通常の Visualforce ページとはデザインがかなり異なります。
JavaScript Remoting と静的 HTML コントローラの例
1global with sharing class WarehouseEditor {
2
3 // Stub controller
4 // We're only using RemoteActions, so this never runs
5 public WarehouseEditor(ApexPages.StandardController ctl){ }
6
7 @RemoteAction
8 global static Warehouse__c getWarehouse(String warehouseId) {
9
10 // Clean up the Id parameter, in case there are spaces
11 warehouseId = warehouseId.trim();
12
13 // Simple SOQL query to get the warehouse data we need
14 Warehouse__c wh = [
15 SELECT Id, Name, Street_Address__c, City__c, Phone__c
16 FROM Warehouse__c
17 WHERE Id = :warehouseId];
18
19 return(wh);
20 }
21
22 @RemoteAction
23 global static Boolean setWarehouse(
24 String whId, String street, String city, String phone) {
25
26 // Get the warehouse record for the Id
27 Warehouse__c wh = WarehouseEditor.getWarehouse(whId);
28
29 // Update fields
30 // Note that we're not validating / sanitizing, for simplicity
31 wh.Street_Address__c = street.trim();
32 wh.City__c = city.trim();
33 wh.Phone__c = phone.trim();
34
35 // Save the updated record
36 // This should be wrapped in an exception handler
37 update wh;
38
39 return true;
40 }
41}