Skip to main content

Drupal 建立互動式表單 create Ajax interactive form

Submitted by admin on Wed, 02/19/2020 - 03:19

截至目前為止我們已經建立過了兩份表單,分別是

Drupal 簡單的自製表單

以及

Drupal 建立模組的設定頁面 create setting page for custom module

上面兩個表單頁面都屬於靜態頁面,每一次要更新內容都要重載整個網頁。

這次我們要介紹不需要重載整個頁面便可以變更表單內容的Ajax表單,這在當我們需要某些低延遲的互動式設計時非常有用。

我們可以先到這裡看看本次範例的結果。

範例提供了一個下拉式選單,並在選擇的時候刷新下方的勾選內容。

 

1.建立info以及routing檔案

這次這兩個檔案其實跟簡單的自製表單那篇提到的差不多,不過這次的模組名稱是SimpleForm2,也記得改一下namespace。

name: SimpleForm2
description: ajax interactive form.
package: Simple Examples
type: module
core: 8.x
SimpleForm2.MySimpleForm:
  path: '/simple_form2'
  defaults:
    _form: '\Drupal\SimpleForm2\Form\SimpleExampleForm'
    _title: 'Drupal Ajax interactive form - select list with checkbox'
  requirements:
    _permission: 'access content'

 

2.建立所呼叫的表單物件

本次的重頭戲。

跟前幾次比起來buildForm多了長長一串,但其實重點是在vocabulary_select這個下拉選單多了#ajax這個鍵值,以及一個checkboxes_fieldset提供了勾選框跳出的區塊而已。下半部分是在搜尋所有的tag標籤名稱。

勾選框跳出的區塊會由<div id="checkboxes-div"></div>包裹,覺得外觀差強人意可以參考Drupal前端設計 - 基本CSS修改教學看看怎麼動手拉皮。

<?php

namespace Drupal\SimpleForm2\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

class SimpleExampleForm extends FormBase {

  public function getFormId() {
    return 'SimpleForm2_SimpleExample_Form';
  }

 public function buildForm(array $form, FormStateInterface $form_state){
     
    //definition of select list with ajax callback
    $form['vocabulary_select'] = [
        '#type' => 'select',
        '#title' => $this->t('Taxonomy select'),
        '#options' => [
            'impossible name of vocabulary' => $this->t('--Select--'),
            'tags' => $this->t('Tag'),
        ],
        '#weight' => -20,
        '#ajax' => [
            'callback' => '::myAjaxCallback', //:: for calling a class method.
            'disable-refocus' => FALSE, // Or TRUE to prevent re-focusing on the triggering element.
            'event' => 'change',
            'wrapper' => 'checkboxes-div', // defines the element updated with this AJAX callback.
            'progress' => [
                'type' => 'throbber',
                'message' => $this->t('loading...'),
            ],
        ]
    ];
    
    //definition of checkbox section
    $form['checkboxes_fieldset'] = array(
        '#prefix' => '<div id="checkboxes-div">',
        '#suffix' => '</div>',
        '#type' => 'fieldset',
        '#weight' => -15
    );
    
    //load term under selected vocabulary, returns null if vocabulary not exist
    $vid = $form_state->getValue('vocabulary_select');
    $terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid);
    foreach ($terms as $term) {
        $term_data[] = $term->name;
    }
    
    //build checkbox named by terms found in previous step
    foreach ($term_data as $term) {
        $form['checkboxes_fieldset']["checkbox_{$term}"] = array(
            '#type' => 'checkbox',
            '#title' => $term,
        );
    }
    

    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Apply'),
    ];

    return $form;

  }

  public function submitForm(array &$form, FormStateInterface $form_state){
  }
  
  public function myAjaxCallback(array &$form, FormStateInterface $form_state)
  {
      return $form['checkboxes_fieldset'];
      
  }

}

#ajax鍵當中比較重要的值就兩個:

  • callback function會回傳表單當中需要被更新的區塊,更精確的說法是回傳一個render array。
  • wrapper裡面記錄了我們希望換掉的HTML區塊的ID,可以注意到這裡的值跟我們的checkbox_fieldset裡面的#prefix一樣。

 

換言之,整份Ajax表單的運作模式如下。

  1. 使用者進入網頁後執行第一次build form,因為下拉選單的預設值是impossible name of vocabulary,所以checkboxes_fieldset內容為空白,但prefix中仍帶有<div id="checkboxes-div">來標記區塊。
  2. 使用者更動下拉選單的值,重新執行build form,checkboxes_fieldset內容有值且經由callback function回傳,取代掉上一份內容空白的<div id="checkboxes-div">區塊,但此時新生成的區塊由於#prefix設定,仍帶有相同的<div id="checkboxes-div">標記。
  3. 使用者再次更動下拉選單,再次執行build form產生新的checkboxes_fieldset取代掉標籤內容,以此類推...

 

enjoy~