BLOG

【Salesforce研修】まるの記録10

こんにちは。まるです。
10回目のブログ更新ですね。
マメな性格じゃないので続けられるか不安だったのですか、何とか目標の10記事達成です。
偉いぞ!自分!

今回は、前回作成した画面を更に改良していきます。
より使いやすい画面を目指して頑張りましょう。

項番を付与する

一括編集画面には複数の取引先責任者の情報が表示される訳ですが、これが何十件もあると中々見づらいですね。
少しでも見やすい画面にするために、各レコードの左側に項番を付与していきます。

<apex:page standardController="Contact" recordSetVar="contacts" extensions="ContactsEdit_Controller" LightningStylesheets="true">
    <apex:form >
        <apex:pageBlock title="取引先責任者一括編集画面">
            <apex:variable value="{!1}" var="rowNumber"/>
            <apex:pageBlockTable title="Contacts" value="{!contactList}" var="con">
                <apex:column headerValue="No." style="width:20px;">
					<apex:outputText value="{!rowNumber}"/>
                    <apex:variable value="{!rowNumber+1}" var="rowNumber"/>
				</apex:column>
                <apex:column headerValue="姓" >
                    <apex:inputField value="{!con.LastName}"/>
                </apex:column>
                <apex:column headerValue="名" >
                    <apex:inputField value="{!con.FirstName}"/>
                </apex:column>
                <apex:column headerValue="役職" >
                    <apex:inputField value="{!con.Title}"/>
                </apex:column>
                <apex:column headerValue="部署" >
                    <apex:inputField value="{!con.Department}"/>
                </apex:column>
            </apex:pageBlockTable>
            <apex:pageBlockButtons location="bottom">
				<apex:commandButton value="保存" action="{!save}"/>
				<apex:commandButton value="キャンセル" action="{!cancel}"/>
			</apex:pageBlockButtons>   
        </apex:pageBlock>
    </apex:form>
</apex:page>

4行目で使用している<apex:variable>コンポーネントでは、このVisualforce内で使える変数を設定できます。今回は”rowNumber”という名前で初期値が”1″の変数を作成しました。
6行目で<apex:column>コンポーネントのheaderValue属性でその列の名前を設定しているのは他の項目と同様です。項番欄には数字しか表示されず、あまり幅を取っても勿体ないのでstyle属性で幅を狭めに指定してみました。
7行目の<apex:outputText>コンポーネントは、このVisualforce画面に表示される内容を設定します。value属性が {!rowNumber} なので、先ほど<apex:variable>コンポーネントで指定した変数の初期値”1″が表示されます。
8行目で、変数”rowNumber”の値に1を足しているので、次の行では”2″が表示されるという仕組みです。

想定通り項番を設定することが出来ました。

新規作成処理の追加

複数の取引先責任者を追加することになった場合、この画面で一気に追加出来たら便利じゃないですか?
ということで、実装してみましょう。
新規作成ということは、新しいレコードの情報を入力する行が必要です。なので、新規で行追加を行うボタンを作成します。
まずは、コントローラ側にボタンを押した際のアクションを追加します。

public class ContactsEdit_Controller {
    public Id accountId;    //親取引先のID
    public List < Contact > contactList {get;set;}  //対象の取引先責任者リスト
    
    //コンストラクタ
    public ContactsEdit_Controller(ApexPages.StandardSetController controller) {
        accountId = ApexPages.currentPage().getParameters().get('id');
        contactList = [Select id, firstName, LastName, Title, Department from Contact where AccountId = :accountId];
    }
    
    //保存処理
	public PageReference save() {
		upsert contactList;
		String prevURL = '/' + accountId;
		PageReference pageRef = new PageReference(prevURL);
		return pageRef;
	}
    
    //行追加処理
    public void addRow() {
		contactList.add(new Contact(AccountId = accountId));
	}
}

追加箇所は19~22行目です。
この画面に表示する取引先責任者のリストである”contactList”に親取引先のIDに遷移元の取引先IDが設定された取引先責任者レコードを追加しています。
あとはVisualforce側にこの”addRow”アクションを呼び出すボタンを追加するだけです。
保存・キャンセルボタンの上に以下の一行を追加します。

<apex:commandButton action="{!addRow}" value="新規"/>

では、実画面で挙動確認です。
新規ボタンが表示されましたね。押してみます。

新規ボタンを押すと、値が入っていない列が一列追加されました。項番も問題なく付与されています。

折角なので、追加された行に値を設定して保存してみます。

新規レコードが追加されました!

削除処理の追加

最後に、一括編集画面上で任意のレコードの削除も行えるようにしたいと思います。
今回は先にVisualforce画面の方を編集します。一括画面の右端にアクション列を追加しましょう。

<apex:column headerValue="アクション">
	<apex:commandButton value="削除" action="{!deleteRow}" reRender="pageblock">
		<apex:param name="rowIndex" value="{!rowNumber-1}"/>
	</apex:commandButton>
	<apex:variable value="{!rowNumber+1}" var="rowNumber"/>
</apex:column>

削除という名前のボタンを押すと、deleteRowアクションが起動する仕様です。(まだこのアクションは設定していないので、現時点で上記のコードを記述するとエラーになります)
また、<apex:param>コンポーネントではそのレコードの項番(rowNumber)から-1した値に”rowIndex”という名前を付けています。<apex:param>コンポーネントで指定されたデータはパラメータとして扱うことができ、コントローラ側で使用することが出来ます。
つまりコントローラ側で削除ボタンが押されたレコードの項番-1の値を参照し、その値と同様の添え字番号のレコードを削除ことが出来るという訳です。
※項番は1からスタートなのに対し、添え字番号は0からスタートのため、ここで1つ数を減らすことで値を揃えています。

また、5行目の<apex:variable>コンポーネントは元々No.列の<apex:column>コンポーネントの中に記述されていたもの(「項番を付与する」という単元のコードの8行目)を移動してきました。
当初の位置の場合、項番を表示後に変数”rowNumber”がすぐにインクリメントされてしまうため、<apex:param>コンポーネントでパラメータに設定する値が想定とは異なる値となってしまうからです。

それでは、コントローラ側にdeleteRowアクションを追加しましょう。

public class ContactsEdit_Controller {
	public Id accountId;    //親取引先のID
	public List < Contact > contactList {get;set;}  //対象の取引先責任者リスト
	public Integer rowIndex {get;set;}	//行番号
	public Contact del;		//削除する取引先責任者
    
	//コンストラクタ
	public ContactsEdit_Controller(ApexPages.StandardSetController controller) {
		accountId = ApexPages.currentPage().getParameters().get('id');
		contactList = [Select id, firstName, LastName, Title, Department from Contact where AccountId = :accountId];
	}
    
	//保存処理
	public PageReference save() {
		upsert contactList;
		String prevURL = '/' + accountId;
		PageReference pageRef = new PageReference(prevURL);
		return pageRef;
	}
    
	//行追加処理
	public void addRow() {
		contactList.add(new Contact(AccountId = accountId));
	}
    
	//行削除処理
	public void deleteRow() {
		rowIndex = Integer.valueOf(ApexPages.currentPage().getParameters().get('rowIndex'));
		del = contactList.remove(rowIndex);
		delete del;
	}  
}

4行目で削除する行の番号をセットする変数を、5行目で削除する取引先責任者レコードををセットする変数を宣言しています。
数値型はJavaではint型を利用できますが、ApexではInteger型と記述しないといけないのは紛らわしいところですね…。

deleteRowアクションも見ていきます。
28行目で変数”rowIndex”に、先ほどVisualforce側で設定したパラメータの”rowIndex”の値を代入します。
29行目では、contactListの要素の中で、添え字番号が”rowIndex”と一致するものを削除しています。そして、removeでは戻り値が削除された要素のデータとなるので、それを変数”del”に代入しています。
これだけでは、あくまでもcontactListの中から対象のレコードを削除したにすぎないので、30行目でSalesforce上からも対象レコードをdeleteしています。

deleteRowアクションも作成できたので、Visualforce画面も保存に成功します。挙動を見てみましょう。

あら~~エラーでした。
調べてみたところ、問題は削除処理実行後に画面を再読み込み出来ていない点にあるみたいです。
という訳で、修正版が以下です。

<apex:page standardController="Contact" recordSetVar="contacts" extensions="ContactsEdit_Controller" LightningStylesheets="true">
    <apex:form >
        <apex:pageBlock title="取引先責任者一括編集画面" id="pageblock">
            <apex:variable value="{!1}" var="rowNumber"/>
            <apex:pageBlockTable title="Contacts" value="{!contactList}" var="con">
                <apex:column headerValue="No." style="width:20px;">
					<apex:outputText value="{!rowNumber}"/>
				</apex:column>
                <apex:column headerValue="姓" >
                    <apex:inputField value="{!con.LastName}"/>
                </apex:column>
                <apex:column headerValue="名" >
                    <apex:inputField value="{!con.FirstName}"/>
                </apex:column>
                <apex:column headerValue="役職" >
                    <apex:inputField value="{!con.Title}"/>
                </apex:column>
                <apex:column headerValue="部署" >
                    <apex:inputField value="{!con.Department}"/>
                </apex:column>
                <apex:column headerValue="アクション">
					<apex:commandButton value="削除" action="{!deleteRow}" reRender="pageblock">
						<apex:param name="rowIndex" value="{!rowNumber-1}"/>
					</apex:commandButton>
					<apex:variable value="{!rowNumber+1}" var="rowNumber"/>
				</apex:column>
            </apex:pageBlockTable>
            <apex:commandButton action="{!addRow}" value="新規"/> 
            <apex:pageBlockButtons location="bottom">
				<apex:commandButton value="保存" action="{!save}"/>
				<apex:commandButton value="キャンセル" action="{!cancel}"/>
			</apex:pageBlockButtons>   
        </apex:pageBlock>
    </apex:form>
</apex:page>

ここでは、ボタンによって部分更新出来るようにしてみました。
まず、3行目の<apex:pageBlock>コンポーネントで、ID属性を”pageblock”として指定しています。
そして22行目の<apex:commandButton>コンポーネントのreRender属性に同じ値を設定しています。
reRender属性に設定された箇所が部分更新されるので、これで削除ボタンを押した際にIDが”pageblock”、つまり取引先責任者一括編集画面ブロック全体が再読み込みされるようになりました。

今度こそ上手くいくはず…。

無事レコードが消えました!

キャンセルボタンを押して元画面に戻ると、ちゃんとレコード自体が削除されていることも確認できます。

完成コード

これで完成です。
念のため完成版のコードを下記に残しておきます。
【Visualforce】

<apex:page standardController="Contact" recordSetVar="contacts" extensions="ContactsEdit_Controller" LightningStylesheets="true">
    <apex:form >
        <apex:pageBlock title="取引先責任者一括編集画面" id="pageblock">
            <apex:variable value="{!1}" var="rowNumber"/>
            <apex:pageBlockTable title="Contacts" value="{!contactList}" var="con">
                <apex:column headerValue="No." style="width:20px;">
					<apex:outputText value="{!rowNumber}"/>
				</apex:column>
                <apex:column headerValue="姓" >
                    <apex:inputField value="{!con.LastName}"/>
                </apex:column>
                <apex:column headerValue="名" >
                    <apex:inputField value="{!con.FirstName}"/>
                </apex:column>
                <apex:column headerValue="役職" >
                    <apex:inputField value="{!con.Title}"/>
                </apex:column>
                <apex:column headerValue="部署" >
                    <apex:inputField value="{!con.Department}"/>
                </apex:column>
                <apex:column headerValue="アクション">
					<apex:commandButton value="削除" action="{!deleteRow}" reRender="pageblock">
						<apex:param name="rowIndex" value="{!rowNumber-1}"/>
					</apex:commandButton>
					<apex:variable value="{!rowNumber+1}" var="rowNumber"/>
				</apex:column>
            </apex:pageBlockTable>
            <apex:commandButton action="{!addRow}" value="新規"/> 
            <apex:pageBlockButtons location="bottom">
				<apex:commandButton value="保存" action="{!save}"/>
				<apex:commandButton value="キャンセル" action="{!cancel}"/>
			</apex:pageBlockButtons>   
        </apex:pageBlock>
    </apex:form>
</apex:page>

【コントローラ(Apex)】

public class ContactsEdit_Controller {
	public Id accountId;    //親取引先のID
	public List < Contact > contactList {get;set;}  //対象の取引先責任者リスト
	public Integer rowIndex {get;set;}	//行番号
	public Contact del;		//削除する取引先責任者
    
	//コンストラクタ
	public ContactsEdit_Controller(ApexPages.StandardSetController controller) {
		accountId = ApexPages.currentPage().getParameters().get('id');
		contactList = [Select id, firstName, LastName, Title, Department from Contact where AccountId = :accountId];
	}
    
	//保存処理
	public PageReference save() {
		upsert contactList;
		String prevURL = '/' + accountId;
		PageReference pageRef = new PageReference(prevURL);
		return pageRef;
	}
    
	//行追加処理
	public void addRow() {
		contactList.add(new Contact(AccountId = accountId));
	}
    
	//行削除処理
	public void deleteRow() {
		rowIndex = Integer.valueOf(ApexPages.currentPage().getParameters().get('rowIndex'));
		del = contactList.remove(rowIndex);
		delete del;
	}  
}

所感

初心者が画面+コントローラクラスを1から作成するというのはなかなか大変でしたが、Salesforce公式ドキュメントやコミュニティ上に投稿された質問・回答に助けられながらなんとか完成まで漕ぎ着けました。
今回作成した画面にはまだまだ改善の余地があると思いますので、引き続き改修を重ねてスキルアップに繋げたいと思います。

ここまでお付き合い頂き有難うございました。

BLOGトップへ戻る