JavaScript
Salesforce
Visualforce

jsforce-metadata-toolsを使って静的リソースをらくらくデプロイ

画面上からぽちぽち静的リソースアップロードするのつらすぎ :innocent:

やること

  • CLIから静的リソースのアップロード

はじめに


jsforceという神ライブラリ使います:point_up:
いつも大変お世話になっております :sob:
jsforce-metadata-tools
まだの人はコマンド使えるようにインストールしといてください

1. デプロイ環境整備 & リソース用意


用意する環境はこんな感じ:finnadie:
$ tree
.
├── assets
│   └── js
│       └── hoge.js
└── package
    ├── package.xml
    └── staticresources
        ├── hoge.resource
        └── hoge.resource-meta.xml
前回のやつ のJSを切り出す

hoge.js
function sayHello(helloTo) {
  new Promise((resolve, reject) => {
    Visualforce.remoting.Manager.invokeAction(
      '{!$RemoteAction.SR_Controller.sayHello}',
      helloTo,
      function(result, event) {
        if(event.status) {
          resolve(result);
        }
      }
    );
  })
  .then(function(data) {
    document.getElementById("main").innerHTML = data;
  });
  return false;
}
圧縮

$ zip package/staticresources/hoge.resource assets/js/hoge.js

「hoge」の部分がNameになるのであまり適当なものをつけないほうがいいかも
拡張子は.resourceじゃないとデプロイした時に怒られる :angry:

最小限のpackage.xml用意 :package:

package.xml
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>hoge</members>
        <name>StaticResource</name>
    </types>
    <version>40.0</version>
</Package>

リソース(hoge.resource)に付随するxmlが無いと怒られるので作成

hoge.resource-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<StaticResource xmlns="http://soap.sforce.com/2006/04/metadata">
    <cacheControl>Public</cacheControl>
    <contentType>application/zip</contentType>
</StaticResource>

XMLの書き方はここ :point_down:
メタデータ API 開発者ガイド - StaticResource

2. デプロイ


下記コマンドで一発デプロイ。神ライブラリすぎます!!!!!!

$ jsforce-deploy -u {username} -p {password} -D ./package/
Logged in as: ***********
Deploying to server...

Deploy Succeeded.

sandboxならlオプションでログインURL指定できます。

3. 確認


前回のやつ とおんなじ感じでファイルを用意 :tokyo_tower:

VF
<apex:page showHeader="false" sidebar="false" controller="SR_Controller">

  <div id="main">
    <input type="button" onClick="sayHello('Marc')" value="Salesforceへのリンク"/>
  </div>

  <script type="text/javascript" src="{!URLFOR($Resource.hoge, '/assets/js/hoge.js')}"></script>
</apex:page>
APEX
public with sharing class SR_Controller {
    @RemoteAction
    public static String sayHello(String helloTo) {
        return 'Hello ' + helloTo;
    }
}

で前回と同じようにRemoteActionできるはず、ができない。。。

余談:RemoteAction周りでちょっと詰まった話


コンソールにエラーが吐かれてるので確認すると、

VFRemote.js:121 Uncaught (in promise) TypeError: Cannot read property 'SR_Controller' of undefined
    at Object.getObject (VFRemote.js:121)
    at constructor.getController (VFRemote.js:137)
    at constructor.getAction (VFRemote.js:137)
    at constructor.invokeAction (VFRemote.js:138)
    at Promise (hoge.js:3)
    at new Promise (<anonymous>)
    at sayHello (hoge.js:2)
    at HTMLInputElement.onclick (sr:8)

で、VFRemote.jsのgetObjectの引数みてみたら

VFRemote.js:122 {!$RemoteAction.SR_Controller
VFRemote.js:123 undefined 

:grey_question::yum::grey_question:

VFRemote.jsを少し読み進めると

VFRemote.js
    getAction: function(a) {
        if (!a || !$VFRM.Util.isString(a) || -1 == a.indexOf(".")) return null;
        var b = this.getController(a.substring(0, a.lastIndexOf("."))), 

:eyes:

a.substring(0, a.lastIndexOf("."))),  

:weary: :weary: :weary:

ここにきてVFの記法を思い出した、外部リソースはコンパイルされないのね

hoge.js
function sayHello(helloTo) {
  new Promise((resolve, reject) => {
    Visualforce.remoting.Manager.invokeAction(
      '{!$RemoteAction.SR_Controller.sayHello}',  // ←ココ
      helloTo,
      function(result, event) {
        if(event.status) {
          resolve(result);
        }
      }
    );
  })
  .then(function(data) {
    document.getElementById("main").innerHTML = data;
  });
  return false;
}

:x::'{!$RemoteAction.SR_Controller.sayHello}'
:o::'SR_Controller.sayHello'

ちゃんと動きました :ok_woman:
外部JSからのRemoteActionの参照はそのままテキストで渡せば(おそらく)OK

なにが入ってんのかなーと思い{!$RemoteAction}をVFのページに直書きしてみると

:alien: 無効なグローバル変数「ApexPagesELAdapterContext.$RemoteAction」を使用しています。期待される項目セレクタがありません。:alien:

見たい、中身知ってる人(見方分かる人)いたら教えてください。

公式docっぽいの見てみたけど、VFに直接記述しない場合は自動で名前解決されないのでコントローラとメソッドだけでOKってことで良いのかな。
名前空間および JavaScript Remoting

おわり。

ドリームフォース行く人うらやましい。俺もミリオンドルマンになりたい。