apex:actionXX タグについて一気に攻略

今日はちょっと、内容多めです。
VisualForceのタグの中で、actionXXというタグがいくつかありますが、ちょっと多くてどれがどういう機能かが分からなくなりますね。
それぞれどういう機能なのかこのページで整理しました。

スポンサーリンク

apex:actionタグたち

VisualForceタグの中でactionXXとついているタグは以下の5つです。

  • apex:actionPoller
  • apex:actionFunction
  • apex:actionSupport
  • apex:actionStatus
  • apex:actionRegion

それぞれ役割が違いますが、共通して言えることは、AJAX要求をする時に使用されるコンポーネントになります。
VisualForceにおけるAJAX要求とは何かというと、ボタンを押した時や時間経過などのイベントによって、
画面全体ではなく、一部のコンポーネントが更新されるような動作となります。

それぞれの解説

それぞれ、どういう動きをするか、実際にコードを書いて動きを確かめてみました。
それぞれ、VisualForceとApexを使用しますが、Apexは面倒だったので共通のものを使用しています。

まずはApexクラスから。

ActionMoverController.cls

public class ActionMoverController {
    // ページを開いてからの秒数
    public Integer timer{get; private set;}

    // 更新対象レコード
    public Account acc{get; set;}

    // ページを開いた日時
    private Datetime startTime;

    public ActionMoverController(){
        // 開始時間を記録
        startTime = Datetime.now();
        timer = 0;
    }

    // 初期処理
    public void init(){
        acc = [Select Id, Name, Industry From Account Limit 1];
    }

    // ページを開いた時間からの秒数を算出するアクション
    public void timeConuter(){
        Long mSec = Datetime.now().getTime() - startTime.getTime();
        timer = (Integer)(mSec / 1000);
    }
}

apex:actionPoller

apex:actionPollerタグは、時間経過によりイベントを起こす仕組みとなります。

VisualForceページで5秒間隔でイベントを起こすようにします。
仕組みとしては、intervalで指定した秒数毎にactionが起動され、reRenderで指定したコンポーネントのみを更新するという動きになっています。
ActionPollerMover.page

<apex:page controller="ActionMoverController">
    <apex:form>
        <apex:outputText value="{!timer}秒経過" id="counter"/>
        <apex:actionPoller action="{!timeConuter}" reRender="counter" interval="5"/>
    </apex:form>
</apex:page>

ページを開いた直後はこのような状態。
actionPoller起動前

何もしないで5秒待つと秒数が切り替わります。
actionPoller起動後

ユーザ側で何か操作することはなく、時間経過でイベントが走るので、
非同期のジョブを実行させている時の監視手段として使うことができます。

apex:actionFunction

apex:actionFunctionタグは、JavaScriptからApexコントローラーのアクションを呼び出すための手段として用いられます。
サーバ処理を実施する前に、クライアントサイド(主にJavaScript内)で必要な処理を実施してから呼び出すことができます。

ボタンを押したら秒数が更新されるようにします。
仕組みとしてはボタンを押した後、onclickによってJavaScriptが実行され、JavaScript内でactionFunctionで定義したメソッド(name)を呼び出し、コントローラのアクションを実行して、rerenderで指定したコンポーネントを更新するという感じです。
ActionFunctionMover.page

<apex:page controller="ActionMoverController">
    <script type="text/javascript">
        function timeCounterJS() {
            // actionFunctionで定義したメソッド名からApexコントローラのアクションを呼び出す
            getTime();
            return false;
    }
    </script>
    <apex:form >
        <apex:outputText value="{!timer}秒経過" id="counter"/>
        <apex:commandButton value="ボタンを押すと秒数表示" onclick="return timeCounterJS();" />
        <apex:actionFunction name="getTime" action="{!timeConuter}" rerender="counter" />
    </apex:form>
</apex:page>

ボタンを押す前の状態。
actionFunction実行前

ボタンを押したら、押した時点での秒数が更新されます。
actionFunction実行後

最終的にはJavaScript経由でApexコントローラのメソッドを呼び出すことになるので、イベント受信後にクライアント側で処理を実施する必要がある場合に使用されます。

apex:actionSupport

apex:actionSupportタグはVisualForceとのコンポーネントとのセットで使用され、コンポーネント側で起こったイベント(onclickやonchangeなど)を受けて、アクションを実施します。

今後はボタンではなくテキスト「ここを押すと秒数が更新されるよ」にクリックをするとイベントが走るようにします。
ActionSupportMover.page

<apex:page controller="ActionMoverController">
    <apex:form >
        <apex:outputText value="{!timer}秒経過" id="counter"/>
        <apex:outputpanel id="msg" layout="block">
            <apex:outputText value="ここを押すと秒数が更新されるよ" />
            <apex:actionSupport event="onclick" action="{!timeConuter}" rerender="counter" />
        </apex:outputpanel>
    </apex:form>
</apex:page>

開始前。
actionSupportイベント処理前

テキストの部分をクリックすると秒数が更新。
actionSupportイベント処理後

コンポーネント毎にイベントを拾うことができるので、一部コンポーネントで特別な処理を実施したい場合に使用します。

apex:actionStatus

apex:actionStatusは単独で使用されることはありません。
上記タグとセットで使用され、サーバ側で処理中の際にアクションの進捗状態を表示するものになります。

apex:actionSupportで使用したイベントを流用して、apex:actionStatusタグを追加します。
もちろん、進捗状態を更新するために、apex:actionSupport側にstatus=”counterStatus”を入れるのを忘れないように。

ActionStatusMover.page

<apex:page controller="ActionMoverController">
    <apex:form >
        <apex:outputText value="{!timer}秒経過" id="counter"/>
        <apex:outputpanel id="msg" layout="block">
            <apex:outputText value="ここを押すと秒数が更新されるよ" />
            <apex:actionSupport event="onclick" action="{!timeConuter}" rerender="counter" status="counterStatus"/>
        </apex:outputpanel>
        <apex:actionStatus id="counterStatus" startText="カウント中・・・" stopText=""/>
    </apex:form>
</apex:page>

開始前の状態。
actionStatusの進捗開始前

テキスト部分をクリック(キャプチャを取るタイミングがシビアだったのでちょっとインチキしました。。。)
actionStatusの進捗中

サーバ処理が終わると、ステータスのところにはstopTextが表示(今回はstopTextがブランクなので、進捗が消える)。
actionStatusの進捗終了状態

AJAX要求でサーバ側の処理に時間がかかる場合に、ユーザ側へ進捗を知らせる手段として使用されます。

apex:actionRegion

apex:actionRegionはちょっと毛色が違います。
Apexコントローラ側でアクションを実施する場合、通常は入力可能なコンポーネント全部をサーバ側に送信しますが、apex:actionRegionを指定することによって、その中で括られたコンポーネントのみがサーバ側へ渡すことができます。

まずは、actionRegionを使用しないとどうなるでしょうか。
業種を切り替えた時に、テキストに更新されるように作ります。
ActionRegionMover.page(actionRegion未使用)

<apex:page controller="ActionMoverController" action="{!init}">
    <apex:form >
        <apex:pageBlock >
        <apex:inputField value="{!acc.Name}" required="true" label="取引先名" />
        <!-- <apex:actionRegion > -->
        <apex:inputField value="{!acc.Industry}">
          <apex:actionSupport event="onchange" rerender="industry"/>
          </apex:inputField>
            <apex:outputText id="industry" value="この会社の業種は{!acc.Industry}です" />
        <!-- </apex:actionRegion> -->
        </apex:pageBlock>
    </apex:form>
</apex:page>

開始前の状態はこう。
actionRegionを使用しない状態での選択リスト更新前

業種を更新すると、テキストの方にも反映されます。
actionRegionを使用しない状態での選択リスト更新後

一見、動作上問題なさそうですが、必須項目が絡むとうまく更新されなくなります。
会社名をブランクにした状態で業種を変更すると。
actionRegionを使用しない状態での必須項目未入力時の選択リスト更新前

業種が更新されなくなります。
Nameは必須項目なので、入力がない状態でアクションを実行しようとすると、アクション実施前のシステムチェックでNGとなって、Apexコントローラまで到達しないんですね。
actionRegionを使用しない状態で必須項目未入力だとアクション動作しない

では、ここでコメントアウトしていたapex:actionRegionを解放させましょう。
ActionRegionMover.page(actionRegion使用)

<apex:page controller="ActionMoverController" action="{!init}">
    <apex:form >
        <apex:pageBlock >
        <apex:inputField value="{!acc.Name}" required="true" label="取引先名" />
        <apex:actionRegion >
        <apex:inputField value="{!acc.Industry}">
          <apex:actionSupport event="onchange" rerender="industry"/>
          </apex:inputField>
            <apex:outputText id="industry" value="この会社の業種は{!acc.Industry}です" />
        </apex:actionRegion>
        </apex:pageBlock>
    </apex:form>
</apex:page>

このように、取引先名に入力がなくても。
actionRegionを使用した際の選択リスト更新前

業種変更で見事に更新されます。
apex:actionRegionをつける事によって、業種だけサーバ側に送られる仕組みとなります。
actionRegionを使用することで必須項目未入力でもアクションが動作

上記例にあった通り、必須項目があるとサーバ側のアクションが実施できないことがあるため、
必須項目を必要としない時にサーバ処理を実施したいときに有効です。

まとめ

今日は内容多めでしたね。
それぞれのactionタグについての使い方、使い道をまとめると以下のようになります。
動作確認などで色々書きましたが、下の表を押さえておけばテスト対策にはなると思います。

apex:actionPoller
  • 時間経過によりイベントが起動
  • 非同期で実行されたジョブの監視に向いている
apex:actionFunction
  • JavaScript経由でサーバ側アクションを要求する
  • クライアントサイドで事前処理がある場合に有効
apex:actionSupport
  • コンポーネントのイベントを受けてAJAX要求を行う
apex:actionStatus
  • AJAX要求の進捗状態確認で使用
  • サーバ処理中および処理終了時の画面表示を指定できる
  • 単独では使用できず、他のAJAX要求からのstatus指定として使用する
apex:actionRegion
  • 一部のコンポーネントのみサーバへ送信する
  • 必須項目を含む項目入力でのAJAX要求がある場合に使用する

コメント