Memos About SalesForce

Salesforceにハマってたこと!

SFDC Component commandButton reRender not working

こんにちは、管理人の@Salesforce.Zです。

apex:commandButtonタグのreRender属性に問題があった。。。

f:id:jude2016:20190805144057j:plain
悔しい、ッチ

問題概要を言いますと、ボタンをクリックによって、画面の部分更新をしたくて、reRender属性で対応しようと思い、

しかし、Not working。。。。。。、なんだと。。。と思ったけど、

読んだら得ること

★ apex:commandButtonのreRenderが動作しない対策

目次

画面再描画機能

sfdcのVFを構成するタグが多数あり、ほとんど、reRender属性があります

画面の一部を更新するには非情報に便利な属性である

普段のパタン

コントロールクラスを用意し、画面表に応じて、DTOクラスをクラス内部で宣言し充分、

この例では、画面にテーブルを表示し、テーブルの上にある「行追加」ボタンを押下することによって

一行がテーブルに追加され、テーブルの領域のみ再描画する

f:id:jude2016:20190805150642p:plain
イメージ図

次のサンプルで、対応し、十分に実現可能だが、コンポーネントでいろいろ表示しまくる時に動作しない部分がある

サンプルコントロールApexクラス

public with sharing class YourController {
    public List<displayDTO> dtoList {get;set;}
    public YourController(){
        dtoList = new List<displayDTO>();
    }

    pulic Pagereference addRow(){
        displayDTO row = new displayDTO();
        row.rowNo = 1;
        if(!dtoList.isEmpty()){
            row.rowNo = dtoList.size() + 1;
        }
        return null;
    }
    /**
     * 編集リンク
     */
    public Pagereference EditLink(){
        return null;
    }

    /**
     * 削除リンク
     */
    public Pagereference DeleteLink(){
        return null;
    }

    public class displayDTO {
        public Account acc {get;set;}
        public Integer rowNo {get;set;}
        // コンストラクタ
        public displayDTO() {
            this.acc = new Account();
            this.rowNo = 0;
        }
    }
}

サンプルコントロールApexクラスがコントロールするVF画面

<apex:page controller="YourController" tabStyle="Account">
    <apex:form id="pageid">
        <apex:pageBlock>
            <apex:pageBlockSection columns="1">
                <apex:commandButton value="行追加" action="{!addRow}" reRender="edit_tbl"/>
                <apex:pageBlockTable value="{!dtoList}" var="dto" id="edit_tbl">
                    <apex:column id="col_link_btn">
                        <apex:commandLink action="{!EditLink}" value="編集" id="row_edit_id" reRender="edit_tbl">
                            <apex:param name="editIdx" value="{!dto.rowNo}" />
                        </apex:commandLink>
                        <apex:outputText value=" | "/>
                        <apex:commandLink action="{!DeleteLink}" value="削除" id="row_del_id" reRender="edit_tbl">
                            <apex:param name="delIdx" value="{!dto.rowNo}" />
                        </apex:commandLink>
                    </apex:column>
                    <apex:column value="{!dto.acc.Name}" />
                    <apex:column value="{!dto.acc.好きな列}" />
                </apex:pageBlockTable>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form
</apex:page>

テーブルの部分をコンポーネントにし、親ページに入れ子として表示、問題点と対策

f:id:jude2016:20190805151608p:plain
イメージ図

なぜ、コンポーネントにするかというと、コンポーネントの部分を繰り返し、画面の違う部分に表示する必要がある場合とか、コードをページ上で、コピペも良いが、格好悪いね?効率も悪いし

ほんで、コンポーネントにしたわけ

すると、ボタンが再描画は動作しません

理由は分かりません🙇🙇🙇 💦💦💦

問題点

問題点は2つあった

・ボタンの再描画機能しません

・内部クラスDTOは使えません

対策

・ボタンのアクションをコンポーネントにパスして、コンポーネント内にアクションを受け取って、再描画する

DTOをクラスの内部から、外部にする

コントローラークラス

public with sharing class YourController {
    public List<displayDTO> dtoList {get;set;}
    public YourController(){
        dtoList = new List<displayDTO>();
    }

    pulic Pagereference addRow(){
        displayDTO row = new displayDTO();
        row.rowNo = 1;
        if(!dtoList.isEmpty()){
            row.rowNo = dtoList.size() + 1;
        }
        return null;
    }

    /**
     * 編集リンク
     */
    public Pagereference EditLink(){
        return null;
    }

    /**
     * 削除リンク
     */
    public Pagereference DeleteLink(){
        return null;
    }
}

クラスの内部DTOクラスを外部にする

public with sharing class displayDTO {
    public Account acc {get;set;}
    public Integer rowNo {get;set;}
    // コンストラクタ
    public displayDTO() {
        this.acc = new Account();
        this.rowNo = 0;
    }
}

最終的なページ側抜粋

<apex:page controller="YourController" tabStyle="Account">
    <apex:form id="pageid">
        <apex:pageBlock>
            <apex:pageBlockSection columns="1">
                <c:your_componentname dtoList="{!dtoList}" func="{!addRow}"/>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form
</apex:page>

コンポーネント

<apex:component controller="YourController" selfClosing="true">
    <apex:attribute name="dtoList" description="一覧情報" type="displayDTO[]" />
    <apex:attribute name="func" description="テスト" type="ApexPages.Action" />
    <apex:pageBlockSection columns="1">
        <apex:commandButton value="行追加" action="{!func}" reRender="edit_tbl"/>
        <apex:pageBlockTable value="{!dtoList}" var="dto" id="edit_tbl">
            <apex:column id="col_link_btn">
                <apex:commandLink action="{!EditLink}" value="編集" id="row_edit_id" reRender="edit_tbl">
                    <apex:param name="editIdx" value="{!dto.rowNo}" />
                </apex:commandLink>
                <apex:outputText value=" | "/>
                <apex:commandLink action="{!DeleteLink}" value="削除" id="row_del_id" reRender="edit_tbl">
                    <apex:param name="delIdx" value="{!dto.rowNo}" />
                </apex:commandLink>
            </apex:column>
            <apex:column value="{!dto.acc.Name}" />
            <apex:column value="{!dto.acc.好きな列}" />
        </apex:pageBlockTable>
    </apex:pageBlockSection>
</apex:component>

終わりに

コンポーネント内で、直接、クラスのアクション(行追加)にする場合、再描画機能が動作しません、メソッド自体はコールされるだけ

あくまでも今回、自分があった問題です。ご参考までに

ほかのサイトでは、actionFunctionを使い、ってような進め方もある

または再描画したい時にコールするメソッドのreturn ApexPages.currentPage()をreturn nullにするとか

いろんなケースで動作しない可能性がある

<apex:actionRegion>タグで対象タグを囲んで、対応するとか、で解決するパターンもある