Salesforce Platform Developer I：Visualforceの基礎

# Visualforce
Visualforceは、Lightning Platformでホストできるモバイルおよびデスクトップアプリケーション用の高度なカスタムユーザインターフェースを、開発者が作成できるようにするWeb開発フレームワークです。
Visualforceページは標準Webページに似ていますが、組織のデータのアクセス、表示、更新を行うための強力な機能が含まれています。
## Lightning ExperienceにVisualforceを使用できる場所
**・アプリケーションランチャーから Visualforce ページを開く**
　　カスタムタブ作成
**・Visualforce ページをナビゲーションバーに追加する**
　　カスタムタブ作成、応用対象アプリケーション選択
**・標準ページレイアウト内に Visualforce ページを表示する**
　　標準ページレイアウト編集画面で、Visualforceページ追加可能
**・Lightning アプリケーションビルダーで Visualforce ページをコンポーネントとして追加する**
　　Lightningアプリケーションビルダーに設定、指定箇所にドラッグ
**・Visualforce ページをクイックアクションとして起動する**
　　クイックアクション作成して、ページレイアウト編集画面に指定箇所にドラッグ
**・標準ボタンまたはリンクを上書きして Visualforce ページを表示する**
　　オブジェクトマネージャーから「ボタン、リンク、およびアクション」右の[▼］の編集から設定する
**・カスタムボタンまたはリンクを使用して Visualforce ページを表示する**
　　Lightning Experience では JavaScript のボタンやリンクがサポートされていませんが、Visualforce (および URL) の項目はサポートされています。

※Lightning Experienceに利用したい場合以下の設定がチェック必要
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/189c85f9-c7a6-ad1d-a9f3-5e0d95ef23e6.png)

## Visualforce ページの作成と編集
・開発者コンソールで作成
・「設定」の「Visualforceページ」の新規ボタンまた、各VFPの編集リンクから

 ```HelloWorld.vfp
<apex:page>
    <h1>Hello World</h1>
    <apex:pageBlock title="A Block Title">
        <apex:pageBlockSection title="A Section Title">
            I'm three components deep!
        </apex:pageBlockSection>
        <apex:pageBlockSection title="A New Section">
            This is another section.
        </apex:pageBlockSection>
    </apex:pageBlock>
</apex:page>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/70cb33ff-5558-1387-45a3-dbc0feee89b2.png)
<font color="red">※<<apex:pageBlockSection>> タグは、親 <<apex:pageBlock>> タグ内に配置しない限り使用できません。</font>


ブラウザの JavaScript コンソール入力して以下のエラーが出る場合、対応方法
<font color="red">Uncaught ReferenceError: $A is not defined
    at <anonymous>:1:1</font>
①Visualforceページの編集画面で、Lightning Experience利用可能チェック入れる
②Lightning Experience環境に該当VFPのタブ作成
③該当タブ移動
④[Ctrl]+[shift]+[J]でブラウザのJSコンソール呼び出し
⑤以下のコード実行

```
$A.get("e.force:navigateToURL").setParams(
    {"url": "/apex/AccountSummary?id=0015h000008rgKJAAY"}).fire();
```
⑥遷移可能になる確認

#### CHALLENGE

```DisplayImage.vfp
<apex:page showHeader="false">
    <apex:image url="https://developer.salesforce.com/files/salesforce-developer-network-logo.png"/>
</apex:page>
```

## グローバル変数、数式と条件式
{! $GlobalName.fieldName } という形式の式を使用します
よく使うグローバル変数
```$ Label```
カスタムラベルを参照するときに使用するグローバルマージフィールドタイプ。
```$ Permission```
現在のユーザーのカスタムアクセス許可アクセスに関する情報を参照するときに使用するグローバルマージフィールドタイプ。権限マージフィールドを使用して、組織のカスタム権限へのユーザーの現在のアクセスに関する情報を参照します。
```$ Profile```
現在のユーザーのプロファイルに関する情報を参照するときに使用するグローバルマージフィールドタイプ。プロファイルマージフィールドを使用して、ライセンスタイプや名前などのユーザーのプロファイルに関する情報を参照します。
```$ User```
現在のユーザーに関する情報を参照するときに使用するグローバルマージフィールドタイプ。ユーザーマージフィールドは、エイリアス、タイトル、IDなどのユーザーに関する情報を参照できます。User標準オブジェクトで使用できるフィールドのほとんどは、$ Userでも使用できます。
```$ UserRole```
現在のユーザーの役割に関する情報を参照するときに使用するグローバルマージフィールドタイプ。ロールマージフィールドは、ロール名、説明、IDなどの情報を参照できます。

そして、組織の詳細 (```$Organization```)、設定 (```$Setup```)、カスタムオブジェクトの詳細 (```$ObjectType```)、それらのオブジェクトで使用可能なアクション (```$Action```) などの取得にも役立ちます。
<font color="red">※Visualforce 式では大文字小文字は区別されず、{! ...} 内のスペースも無視されます。</font>

```UserStatus.vfp
<apex:page>
    <apex:pageBlock title="User Status">
        <apex:pageBlockSection columns="1" title="ユーザ情報">
            {! $User.FirstName & ' ' & $User.LastName }
           ({! $User.Username })
        </apex:pageBlockSection>
        <apex:pageBlockSection columns="1" title="数式">
            <p> Today's Date is {! TODAY() } </p>
            <p> Next week it will be {! TODAY() + 7 } </p>
            <p>The year today is {! YEAR(TODAY()) }</p>
            <p>Tomorrow will be day number  {! DAY(TODAY() + 1) }</p>
            <p>Let's find a maximum: {! MAX(1,2,3,4,5,6,5,4,3,2,1) } </p>
            <p>The square root of 49 is {! SQRT(49) }</p>
            <p>Is it true?  {! CONTAINS('salesforce.com', 'force.com') }</p>
        </apex:pageBlockSection>
        <apex:pageBlockSection columns="1" title="条件式">
            <p>{! IF( CONTAINS('salesforce.com','force.com'), 'Yep', 'Nope') }</p>
            <p>{! IF( DAY(TODAY()) < 15, 'Before the 15th', 'The 15th or after') }</p>
        </apex:pageBlockSection>
    </apex:pageBlock>
</apex:page>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/8d013dcf-6c39-f49d-2024-ff2d521db756.png)


## 単一レコードのデータとリレーションの項目を表示する

```AccountSummary.vfp
<apex:page standardController="Account">
    <apex:pageBlock title="Account Summary">
        <apex:pageBlockSection>
            Account owner: {! Account.Owner.Name } <br/>
            Name: {! Account.Name } <br/>
            Phone: {! Account.Phone } <br/>
            Industry: {! Account.Industry } <br/>
            Revenue: {! Account.AnnualRevenue } <br/>
        </apex:pageBlockSection>
    </apex:pageBlock>
</apex:page>
```
<font color="red">standardControllerにオブジェクトの「API参照名」指定、
その後は「{! Account.XXX }」で項目表示可能、そして「{! Account.Owner.XXX }」でリレーション関係の関連オブジェクト項目表示可能</font>

## レコード詳細と関連リストを表示する
### レコード詳細表示(関連リスト付け)

```
<apex:page standardController="Account">
<apex:detail />
</apex:page>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/507ccc6a-6ece-a948-0661-f9caa868db07.png)

### レコード詳細表示(関連リスト付いていない)
取引先レコードの詳細はそのまま表示されますが、関連リストは表示されません。

```
<apex:page standardController="Account">
<apex:detail relatedList="false"/>
</apex:page>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/21d1e62f-8202-deeb-4250-272f689bfd03.png)

### レコード詳細表示(関連リスト自定義表示)

```
<apex:page standardController="Account">
<apex:detail relatedList="false"/>
<apex:relatedList list="Contacts"/>
<apex:relatedList list="Opportunities" pageSize="5"/>
</apex:page>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/5dab61e6-5731-9b25-7bfd-ac8c73c4007d.png)

### レコード詳細個別項目表示

```
<apex:page standardController="Account">
<apex:outputField value="{! Account.Name }"/><br/>
<apex:outputField value="{! Account.Phone }"/><br/>
<apex:outputField value="{! Account.Industry }"/><br/>
<apex:outputField value="{! Account.AnnualRevenue }"/><br/>
</apex:page>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/0ec672fd-afb9-0209-cc30-ee73c3501276.png)

### テーブルを表示する
・「apex:pageBlock」：表示枠
・「apex:pageBlockTable」テーブル設定
　　「value」：データ元
　　「var」：列のカラムに設定レコードデータの略称
・「apex:column」：列
　　「value」：レコードの項目

```
<apex:pageBlock title="Contacts">
   <apex:pageBlockTable value="{!Account.contacts}" var="contact">
      <apex:column value="{!contact.Name}"/>
      <apex:column value="{!contact.Title}"/>
      <apex:column value="{!contact.Phone}"/>
   </apex:pageBlockTable>
</apex:pageBlock>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/a50f7a9d-6357-e19e-c76f-41690d740a16.png)

### その他
他のタグが下記のリンク参照

https://developer.salesforce.com/docs/atlas.en-us.224.0.pages.meta/pages/pages_compref_enhancedList.htm

<apex:enhancedList> と <apex:listViews> は、<apex:relatedList> と併用するかこの代わりに使用すると役立つ、別の大まかなコンポーネントです。
<apex:dataTable> と <apex:dataList> は、スタイル設定なしのテーブルとリストを作成する反復コンポーネントです。

## Visualforceフォーム

```AccountEdit.vfp
<apex:page standardController="Account">
    <apex:form> <!-- その中に含まれるすべてをページアクションの一部としてサーバに戻すことができる -->
    <apex:pageBlock title="Edit Account">
		<apex:pageMessages/> <!-- フォーム処理エラー、メッセージを表示 -->
        <apex:pageBlockSection columns="1">
            <apex:inputField value="{! Account.Name }"/> <!-- レコードデータ入力項目 -->
            <apex:inputField value="{! Account.Phone }"/>
            <apex:inputField value="{! Account.Industry }"/>
            <apex:inputField value="{! Account.AnnualRevenue }"/>
        </apex:pageBlockSection>
        <apex:pageBlockButtons>
            <apex:commandButton action="{! save }" value="Save" /> <!-- ボタン、 標準saveアクションメソッド呼び出し -->
        </apex:pageBlockButtons>
    </apex:pageBlock>
    <apex:pageBlock title="Contacts">
        <apex:pageBlockTable value="{!Account.contacts}" var="contact">
            <apex:column>
                <apex:outputLink
                    value="{! URLFOR($Action.Contact.Edit, contact.Id) }"><!-- URLFOR()URL再設定、グローバル変数利用 -->
                    Edit
                </apex:outputLink>
                &nbsp;
                <apex:outputLink
                    value="{! URLFOR($Action.Contact.Delete, contact.Id) }">
                    Del
                </apex:outputLink>
            </apex:column>
            <apex:column value="{!contact.Name}"/>
            <apex:column value="{!contact.Title}"/>
            <apex:column value="{!contact.Phone}"/>
        </apex:pageBlockTable>
    </apex:pageBlock>
    </apex:form>
</apex:page>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/5fdf9f19-8092-ae29-c988-079f818a1c6b.png)

### 標準リストコントローラ

```ContactsList.vfp
<apex:page standardController="Contact" recordSetVar="contacts"><!-- recordSetVar：レコードのコレクションで作成される変数の名前 -->
    <apex:form >
        <apex:pageBlock title="Contacts List" id="contacts_list">
            Filter:
            <apex:selectList value="{! filterId }" size="1"><!-- 検索条件設定 -->
                <apex:selectOptions value="{! listViewOptions }"/>
                <apex:actionSupport event="onchange" reRender="contacts_list"/>
            </apex:selectList>
            <!-- Contacts List表示-->
            <apex:pageBlockTable value="{! contacts }" var="ct">
                <apex:column value="{! ct.FirstName }"/>
                <apex:column value="{! ct.LastName }"/>
                <apex:column value="{! ct.Email }"/>
                <apex:column value="{! ct.Account.Name }"/>
            </apex:pageBlockTable>
            <!-- page数　前後ページ追加　表示件数変更 -->
            <table style="width: 100%"><tr>
            <!-- ページ数/総ページ数　表示追加-->
                <td>
                    Page: <apex:outputText value=" {!PageNumber} of {! CEILING(ResultSize / PageSize) }"/>
                </td>
                <td align="center">
                    <!-- 前のページ-->
                    <!-- active -->
                    <apex:commandLink action="{! Previous }" value="« Previous"
                         rendered="{! HasPrevious }"/>
                    <!-- inactive (no earlier pages) -->
                    <apex:outputText style="color: #ccc;" value="« Previous"
                         rendered="{! NOT(HasPrevious) }"/>
                    &nbsp;&nbsp;
                    <!-- 次のページ-->
                    <!-- active -->
                    <apex:commandLink action="{! Next }" value="Next »"
                         rendered="{! HasNext }"/>
                    <!-- inactive (no more pages) -->
                    <apex:outputText style="color: #ccc;" value="Next »"
                         rendered="{! NOT(HasNext) }"/>
                </td>
                <!-- ページ表示件数設定-->
                <td align="right">
                    Records per page:
                    <apex:selectList value="{! PageSize }" size="1">
                        <apex:selectOption itemValue="5" itemLabel="5"/>
                        <apex:selectOption itemValue="20" itemLabel="20"/>
                        <apex:actionSupport event="onchange" reRender="contacts_list"/>
                    </apex:selectList>
                </td>
            </tr></table>
        </apex:pageBlock>
    </apex:form>
</apex:page>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/6fc7b429-6344-8854-4946-f641dda11a6e.png)

#### CHALLENGE

```AccountList.vfp
<apex:page standardController="Account" recordSetVar="accounts">
    <apex:repeat var="a" value="{!accounts}">
          <li>
              <apex:outputLink value="/{!a.id}">
                  {!a.name}
        </apex:outputLink>
        </li>
    </apex:repeat>
</apex:page>
```
<font color="red">結果：リンク作成、各取引先に遷移可能</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/0d9963ab-876a-872e-2417-418b5f87ae06.png)


### 静的リソースの使用
①下記のリンクから単純なjQueryダウンロード、また、jquerymobileのzipファイルダウンロード
https://jquery.com/download/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/f3d74812-ed52-6983-9951-c271a0a350ae.png)
https://jquerymobile.com/download/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/8a1c58d2-7a26-1370-074f-50afa96d845f.png)

<font color="red">※jquerymobileの場合、zipファイル解凍して、demoフォルダ削除、そしてフォルダ名はjqueryに変更して、再圧縮する必要がある。
</font>

②静的リソース作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/33e12696-3dbd-8a8b-1374-535bc33e448e.png)
また
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/ffaa3072-c78a-bfcd-fe61-1025eb10f6ea.png)

③VFの利用

```jQueryMobileResources.vfp
<apex:page showHeader="false" sidebar="false" standardStylesheets="false">
    <!-- 静的リソースからcss設定 -->
    <apex:stylesheet value="{!
        URLFOR($Resource.jQueryMobile,'jquery/jquery.mobile-1.4.5.css')}"/>
    <!-- Add static resources to page's <head> -->
    <apex:includeScript value="{! $Resource.jQueryMobile }"/>
    <!-- 静的リソースからjs設定 -->
    <apex:includeScript value="{!
        URLFOR($Resource.jQueryMobile,'jquery/jquery.mobile-1.4.5.js')}"/>
    <div style="margin-left: auto; margin-right: auto; width: 50%">
        <!-- Display images directly referenced in a static resource -->
        <h3>Images</h3>
        <p>A hidden message:
            <!-- 静的リソースから表示画像設定 -->
            <apex:image alt="eye" title="eye"
                url="{!URLFOR($Resource.jQueryMobile, 'jquery/images/icons-png/eye-black.png')}"/>
            <apex:image alt="heart" title="heart"
                url="{!URLFOR($Resource.jQueryMobile, 'jquery/images/icons-png/heart-black.png')}"/>
            <apex:image alt="cloud" title="cloud"
                url="{!URLFOR($Resource.jQueryMobile, 'jquery/images/icons-png/cloud-black.png')}"/>
        </p>
    <!-- Display images referenced by CSS styles, all from a static resource. -->
    <h3>Background Images on Buttons</h3>
    <button class="ui-btn ui-shadow ui-corner-all 
        ui-btn-icon-left ui-icon-action">action</button>
    <button class="ui-btn ui-shadow ui-corner-all 
        ui-btn-icon-left ui-icon-star">star</button>
    </div>
</apex:page>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/c6f5a940-98dd-ca38-823d-e92422542af1.png)

### カスタムコントローラの作成と使用
ページタグの<font color="red">controller</font>項目の指定のカスタムコントローラ設定可能

```ContactsListWithController.vfp
<apex:page controller="ContactsListWithController">
    <apex:form>
        <apex:pageBlock title="Contacts List" id="contacts_list">
            <!-- Contacts List -->
            <apex:pageBlockTable value="{! contacts }" var="ct">
                <apex:column value="{! ct.FirstName }">
                    <apex:facet name="header">
                        <apex:commandLink action="{! sortByFirstName }"
                            reRender="contacts_list">
                            <!-- カスタムオブジェクトには独自の翻訳指定 -->
                            <apex:outputText value="{! $ObjectType.Contact.Fields.FirstName.Label }"/>
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>
                <apex:column value="{! ct.LastName }">
                    <apex:facet name="header">
                        <apex:commandLink action="{! sortByLastName }"
                            reRender="contacts_list"><apex:outputText value="{! $ObjectType.Contact.Fields.LastName.Label }"/>
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>
                <apex:column value="{! ct.Title }"/>
                <apex:column value="{! ct.Email }"/>
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</apex:page>
```

```ContactsListWithController.cls
public class ContactsListWithController {
private String sortOrder = 'LastName';
//初期化の検索
public List<Contact> getContacts() {
    List<Contact> results = Database.query(
        'SELECT Id, FirstName, LastName, Title, Email ' +
        'FROM Contact ' +
        'ORDER BY ' + sortOrder + ' ASC ' +
        'LIMIT 10'
    );
    return results;
}
//姓でソート
public void sortByLastName() {
    this.sortOrder = 'LastName';
}
//名でソート
public void sortByFirstName() {
    this.sortOrder = 'FirstName';
}
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424429/1148f75e-82c4-0a41-3ac3-3c757d99bba2.png)

### 標準コントローラとカスタムコントローラ
|  | 呼び出し文言 | Apex |
|:-:|:-:|:-:|
| 標準コントローラ | standardController  | 無 |
| カスタムコントローラ | controller | 必要 |
| 標準コントローラ拡張 | standardControllerとextensions | 拡張の部分必要 |

#### CHALLENGE

```NewCaseList.vfp
<apex:page controller="NewCaseListController">
    <apex:repeat value="{!NewCases}" var="case">
        <li>
            <apex:outputLink value="/{!case.id}" target="_new">
                {!case.CaseNumber}
            </apex:outputLink>
        </li>
    </apex:repeat>
</apex:page>
```

```NewCaseListController.cls
public class NewCaseListController {
    public List<Case> getNewCases(){
        List<Case> results = Database.query('SELECT Id,CaseNumber FROM Case where status=\'New\'');
        return results;
    }
}
```