メインコンテンツに移動

メインナビゲーション

  • ホーム
  • サイトマップ
  • ビデオ
  • ご連絡

パンくず

  • ホーム
  • Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理

Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理

drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
ctools
form

Drupalでダイナミックフォーム作成方法:Ctoolsのdependent/FromAPIのstates/FormAPIのajaxがあります

  • Drupalでダイナミックフォーム作成するには大体3っつの方法があります

    • Ctoolsのdependent: Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
    • FormAPIのstates:Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
    • FormAPIのajax:Drupalのajaxの仕組みおよびプログラム上での扱う
  • この三つの方法の中にCtoolsのdependentの利用はお勧めはしません

制御条件が単純的の場合にFormAPIのstatesを利用すべきです

  • 制御条件は単純的な、条件数は少ない場合はFormAPIのstatesを利用すべき
    • 例:ラジオボタン/チェックボックスが選択されたかどうか
  • 制御される要素の表示/非表示など単純の場合はFormAPIのstatesを利用すべき
  • 上記条件では、Ctoolsのdependentを利用する結果は同じですが、Ctoolsのdependentの設定に特殊性があり、利用しないほうが良いでしょう
  • また、CtoolsのdependentよりFormAPIのstatesのほうが定義ルール、コーティングルール、柔軟性などが優れています

サーバーでデータやり取り必要な場合にFormAPIのajaxを利用します

  • 制御条件は複雑、データの検索が必要などの場合にFormAPIのajaxを利用するしかないです
    • 例:キーワードの検索より、そのキーワードの編集フォームを開くなど
    • サーバーへのデータ保存、保存後に詳細のデータを表示します
  • この場合に、FormAPIのstates/Ctoolsのdependentが実現できないです
drupal
video
form

DrupalのFormAPIの"#states"プロパティの設定でダイナミックフォーム要素を制御します

  • Drupalのフォームに関する開発/カスタマイズの場合に、FormAPIがほぼ必須になる状態です
  • FormAPIにある様々のプロパティに"#states"が初心者にあまり気づかないかもしれないですが、ダイナミックフォームの要素制御に使用されます
  • 基本として、制御要素と制御される要素となります
    • 制御要素は:入力系要素(チェックボックス/ラジオボタン/セレクトボックス/テキストフィールドなど)
    • 制御される要素は:任意の要素の表示/非表示/有効化/無効化/Collapseなどの制御
  • "#states"プロパティの定義に、制御要素の識別はjQueryのセレクター(:input)が利用されています
    // "#"states" 定義の基本
    "#"states" => array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:チェックボックスがチェックされたときに要素が表示される
    "#"states" => array(
        'visible' => array(
            ':input[name="remote_checkbox"]' => array('checked' => TRUE),
        ),
    )

    FormAPIのstatesプロパティの使用

  • 今回のカスタムモジュール(formapi_states_example)は、前回のCtoolsのdependentの使用と同じようなフォーム要素を作成しました

制御要素の動き(empty/filled/chechedなど)を利用します

  • 制御要素がラジオボタン/チェックボックス/セレクトボックスがよく利用されますが、テキストフィールドなども利用することができます
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
                'uncollapse'=>t('次の要素を開く'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#states' => array(
                // セレクトが「旅行」の場合にこのエレメントが表示される
                'visible' => array(':input[id="edit-restrict-by"]' => array('value'=>'trip')),
    
            ),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#states' => array(
                // セレクトが「カレンダー」の場合にこのエレメントが表示される
                'visible' => array(':input[name="restrict_by"]' => array('value'=>'month')),
            ),
        );
  • 使用可能な要素のプロパティ:
    • empty
    • filled
    • checked
    • unchecked
    • expanded
    • collapsed
    • value

制御される要素の動き(enabled/disabled/visible/invisibleなど)を定義することができます

  • 制御される要素の動きは表示/非表示がよく利用されます
  • ほかに様々の動き(collapse/extendedなど)の制御は可能です
     // フィールドセットの畳む/開く制御例
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#states' => array(
                'expanded' => array(':input[name="restrict_by"]' => array('value'=>'uncollapse')),
            ),
        );
    
  • 制御される要素の動き一覧は
    • enabled
    • disabled
    • required
    • optional
    • visible
    • invisible
    • checked
    • unchecked
    • expanded
    • collapsed

複数の制御条件(AND/OR)の設定が可能

  • 制御条件は複数にすることができます
  • 制御条件はANDの場合に以下のような構文
    // 二つのAND制御条件に
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
    // 例:ニッカ所にチェックされた場合、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );
  • 制御条件はORの場合に以下のような構文
    // ORの複数条件で表示/非表示制御
    array(
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
      'visible' => array(
        JQUERY_SELECTOR => REMOTE_CONDITIONS,
        ...
      ),
    )
    
     // 例:ニッカ所のところに、一か所がチェックされたら、要素が表示される
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
            '#states' => array(
                'visible' => array(
                    ':input[name="radio_restrict_by"]' => array('value' => 'enable_next'),
                ),
                'visible' => array(
                    ':input[id="edit-restrict-by"]' => array('value'=>'uncollapse')
                ),
            ),
        );

結論・感想:FormAPIの"#states"プロパティ制御条件の設定は分かりやすく、統一性があり、柔軟性が高いものです

  • 要素のプロパティ"#states"の定義/コーティングルールは比較的にシンプルで、わかりやすいです
  • 要素特、定などにはjQueryのセレクター(:input)が利用しているので、コーティングルールの統一感があります
  • 要素の動き制御は表示/非表示だけではなく、有効/無効/折りたたむなども制御可能で、より柔軟性があります
添付 サイズ
formapi_states_test.zip (1.45 KB) 1.45 KB
Embedded thumbnail for Drupalのダイナミックフォーム2:FormAPIのStatesプロパティよりエレメントの表示/非表示などの処理
drupal
ctools
form

Drupalのモジュール(Ctools)がAPIモジュールであり、幅広く使用されている

  • Drupalのモジュール(Ctools)がAPIモジュールなので、直接に利用するのではなく、いろいろなモジュールに使用されています
    • 例:Views、PanelsなどがCtoolsを利用しています
  • 主な機能は以下のようです(Drupalの中核な存在です)
    • Plugins
    • Exportables
    • AJAX responder
    • Form tools
    • Object caching
    • Contexts
    • Modal dialog
    • Dependent
    • Content
    • Form wizard
    • CSS tools

CtoolsのDependent(依存)がフォーム要素を表示/非表示されるダイナミックな変化に使用されます

  • ダイナミックフォーム: フォーム要素がある条件で表示、非表示を行います
    • 例:セレクトボックス:カレンダー、旅行 二つあります
    • 別の要素(例:セレクト/ラジオボタン/チェックボックス)で上記要素(カレンダー/旅行)の表示/非表示を制御します(ビデオを参考)CtoolsのDependent Systemでフォーム要素の表示/非表示を制御する
  • CtoolsがFormAPIにプロパティ("#dependency")をプラグインしています
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    • フォームの構成はDrupalのFormAPIに従っています
    • 制御される要素は"#dependency"プロパティで記述されています

Ctoolsの"#depencency"の要素識別方法にラジオボタンが特別に扱われています

  • 制御要素が先に定義します(例:セレクトボックス:(restrict_by)
  • 制御される要素が二つ:trip、month セレクトボックス
  • 制御される要素の"#dependency"設定より制御要素の状態で要素の表示/非表示がjavascriptで操作されます
  • "#dependency"の定義は配列で行います
    ”#dependency” => array( "依存先要素のID" => "依存先要素の状態" )
    • 例:依存先は「セレクトボックス」(restrict_by)とします
    • 依存先(restrict_by)のid(htmlソースにある要素id):edit-restrict-by
    • 依存先の状態: none/trip/month 三種類です。
    • trip、またはmonthが選ばれたら、旅行、またはカレンダーのセレクトボックスを表示させる(選ばれなかったら、表示させない)
  • 但し、制御要素がラジオボタン("radios")の場合に"依存先要素のID"書き方が変わります
    '#dependency' => array('radio:restrict_by' => array('trip')),
    // radio: 決まり文言
    // restrict_by: 要素ID
    • 特に、ラジオボタンの要素IDはhtml上の要素IDではありません。FormAPIで定義する要素のIDです
  • "依存先要素の状態"は要素の性質より変わります
    • ラジオボタン: FormAPIの"radios"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('month')),
    • セレクトボックス: FormAPIの"select"での"#opetions"に選択された要素のキー:none/trip/month の一つ
      '#dependency' => array('edit-restrict-by' => array('trip')),
    • チェックボックス: html上に各チェックボックスIDごとに、チェックされた(true:1)かチェックされていない(false:0)の一つ
      '#dependency' => array('edit-restrict-by-month--2' => array(true)),
  • 今回テスト(セレクトボックス/チェックボックス/ラジオボタン)したソースは以下のとなります
    function example_page($form, &$form_state){
    
        // 1. Include CTools Dependent helper
        ctools_include('dependent');
    
        $form['select_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
    
        $form['select_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'select',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => array('none'),
    
        );
        $form['select_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            // セレクトが「旅行」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('trip')),
        );
        $form['select_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            // セレクトが「カレンダー」の場合にこのエレメントが表示される
            '#dependency' => array('edit-restrict-by' => array('month')),
        );
    
    
        $form['radio_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Selectbox Set'),
        );
        $form['radio_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'radios',
            '#options' => array (
                'none'=>t('Select one'),
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
            '#default_value' => 'none',
    
        );
        $form['radio_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
            '#dependency' => array('radio:restrict_by' => array('trip')),
        );
    
        $form['radio_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('radio:restrict_by' => array('month')),
        );
        $form['Checkbox_set'] = array(
            '#type' => 'fieldset',
            '#title' => t('Checkbox Set'),
        );
    
        $form['Checkbox_set']['restrict_by'] = array (
            '#title' => t('選択してください'),
            '#type' => 'checkboxes',
            '#options' => array (
                'trip'=>t('旅行'),
                'month'=>t('カレンダー'),
            ),
    
        );
    
        $form['Checkbox_set']['trips'] = array (
            '#type' => 'select',
            '#title' => t('旅行'),
            '#options' => array (
                'trip1' => t('地中海クルーズ'),
                'trip2' => t('南極旅行'),
            ),
    
            // チェックボックスのIDが変わったので注意が必要
            '#dependency' => array('edit-restrict-by-trip--2' => array(true)),
        );
    
        $form['Checkbox_set']['months'] = array (
            '#type' => 'select',
            '#title' => t('カレンダー'),
            '#options' => array (
                '1' => t('一月'),
                '2' => t('二月'),
                //...and so on
            ),
            '#dependency' => array('edit-restrict-by-month--2' => array(true)),
        );
    
    
        return $form;
    }
    

Ctoolsの依存(dependent)が表示/非表示制御のみ、FormAPIのstatesより制御範囲が狭いです

  • FormAPIに"#dependency"プロパティをプラグインは、表示/非表示のみとなります
  • FormAPIのstatesプロパティの制御と比較すると、制御できる範囲は狭いです
    • FormAPIのstatesプロパティは、表示/非表示/collapse/extended/enabled/disabledなどの制御ができます

感想と結論:Ctoolsの依存(dependent)の設定、コーティングルールに統一性が足りなく、わかりずらいです

  • 依存性の設定定義にラジオボタンの特別扱いを認識しなければならない(最初は戸惑いがあるだろう)
  • これらの特別なコーティングルールを覚える必要があります
  • 要素特定にはjQueryを利用していない
  • 比較してみると、FormAPIの”#states”プロパティの使用で、より統一性があり、柔軟性のダイナミックフォーム作成ができます
添付 サイズ
ctools_dependent_test.zip (1.4 KB) 1.4 KB
Embedded thumbnail for Drupalのダイナミックフォーム1:モジュール(Ctools)のDependent Systemよりエレメントの表示/非表示
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
drupal
development
system_management
ajax

DrupalのFormAPIでフォーム内でAJAXを扱うのは基本です

  • DrupalのFormAPIがAJAXを実装しています
  • FormAPIでのAJAXの扱う:「Drupalのフォーム(FromAPI)でのajaxの実装」を参考してください
  • FormAPIでのAJAXでの扱う利点:
    • フォームを作成しているときに簡単にAJAXの実装が可能
    • AJAXの仕組みを知らなくても、手順通りに実装すればよいです
  • 大半のケースでFormAPIで解決できるので、AJAXをFormAPIで実装するのは一般的となります

FormAPI以外にAJAXの実装はDrupal.ajaxオブジェクトで行います

  • DrupalのAJAXのフレームワークは「misc/ajax.js」にあります
  • AJAXを実装する場合、このjsファイルを必ずインポートする必要があります
    drupal_add_js("/misc/ajax.js") ;  // ajaxのjsファイルをインポート
  • ここで、あるフォーム(text_form)のボタンを押してajaxを通じてサーバーとの交信を行う例とします
    Drupalのajaxの仕組みと扱い方法
  • フォームはDrupalのFormAPIで生成、または別の方法で生成します
    // 例: DrupalのAPIでフォームを作成
    //     送信ボタンを押して、"test message"の文字列でフォームを置換します
      $form['card_last']=array(
          "#type" => "markup",
          "#markup" => "",
          "#prefix" => "<div id='card-last-result-div'>",  // ajaxで置換したいエリア
          "#suffix" => "</div>",
      );
    
      $form['card_last']['submit'] = array(
          '#type' => 'submit',
          '#value' => '送信',
          '#attributes' => array( 'id'=>'test-form-submit' ),  // ajaxのIDを指定します
          "#ajax" => array(
            "callback" => "_ajax_callback_form",  // ajaxのコールバック
            'wrapper' => 'card-last-result-div',
            'method' => 'replace',
            'effect' => 'fade',
          ),
      );
    
    // カスタムJSファイルをフォームと一緒に添付
      $form['#attached']['js'] = array( "file_path/custom_ajax.js" );

DrupalのAJAXオブジェクトを生成してサーバーとの交信を用意します

  • 一つカスタムjsファイルを用意して(例:custom_ajax.js)、フォーム生成と同時にクライアント側に渡します
  • Drupal.ajaxオブジェクトを生成します
    (function($){
      Drupal.behaviors.custom_module_name={
        attach:function(cotext,settings){
          $("#test-form-submit").once("test-form-submit",function(){ //指定したフォームのみ
            var base = $(this).attr('id');  //フォームIDの使用
            var element_settings = {
              url: window.location.protocol + "//"+ window.location.hostname + settings.basePath + settings.pathPrefix + "system/ajax",
              event: 'click',
              progress: { type: 'throbber', },
            };
        // Drupalのajaxオブジェクトを生成します
            Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
    
        // 送信ボタンにListenerを追加
            $("#test-form-submit").on( 'click',function(){
              $(this).click();  // ajaxを作動させます
            });
          });
        }
      };
    })(jQuery)
    
    • base(必須): ajaxの識別、ここでフォームID(test-form-submit)で指定
    • url(必須): サイトのパス/system/ajax
    • event: ajaxを作動させるイベントの指定
    • progress: ajaxでデータ交信中にロードの動画アイコンの表示
  • サーバーサイトでajaxに呼び出されるファンクションを用意します、"test message"をajaxに返還します
    function _ajax_callback_card_last_form($form, &$form_state){
      $commands[] = ajax_command_replace('#card-last-result-div', "test message"); 
      return array('#type' => 'ajax', '#commands' => $commands);
    }
  • ここで、ajax_commandを利用しなくても、ajax_deliverでメッセージの返還もできます
ホーム

古松

検索

Article Category

  • apache(7)
  • css(19)
  • drupal(295)
  • Electron(4)
  • html(34)
  • javascript(27)
  • laravel(4)
  • linux(5)
  • macOS(2)
  • mysql(13)
  • php(19)
  • python(4)
  • SEO(12)
  • video(72)
  • Visual Studio Code(4)
  • windows(13)
  • wordpress(32)