表單(Form)的使用(3)——窗口構造器

當創建HTML的表單時,我們經常發現,寫了一大堆的關於這個表單視圖的代碼,卻很難被其他工程所複用。例如,每個輸入框,我們必須獲取他的標籤信息,還要提示錯誤信息。爲了改進這些代碼的可重用性,我們可以用表單構造器這玩意兒。

1.1 基礎概念

Yii表單構造器用CForm對象來表示特定需求的一個HTML表單,包括這個表單設計到什麼數據,表單中有哪些輸入區域,以及如何整體渲染這個表單。開發人員通常中需要創建,配置這個CForm的對象,然後調用他的渲染方法來呈現就可以了。

表單的輸入是有一堆的元素組建起來的。在這個結構的頂層,就是CForm這個對象。這個跟元素有兩個分支:CForm::buttons 和 CForm::elements. 前一個分支包含了按鈕的元素(例如提交,重置),後者包含了輸入元素,靜態文本,子表單。子表單就是CForm這個對象所包含的另外表單的元素集。他可以有他自己的模型,包括這兩個分支集合。

當用戶提交一個表單,數據輸入到輸入框之後,整個表單的表單被提交,包括子表單的那些輸入區。CForm提供了便捷的方法自動把輸入的數據賦值到相對應的模型屬性中,並會調用校驗規則校驗之。

1.2 創建一個簡單的表單

接下來的例子,我們展示一下如何用表單器構造器來創建一個登錄表單。

首先,我們寫好登錄的動作:

public function actionLogin()
{
        $model = new LoginForm;
        $form = new CForm('application.views.site.loginForm', $model);
        if($form->submitted('login') && $form->validate())
                $this->redirect(array('site/index'));
        else
                $this->render('login', array('form'=>$form));
}

在上面的代碼中,我們創建了一個CForm的對象,指向application.views.site.loginForm。這對象關聯到LoginModel的模型中。

表單提交之後,如果所有的驗證都能通過,用戶會被重定向到site/idnex的頁面。否則,就渲染登錄表單。

關於路徑別名alias application.views.site.loginForm, 實際上是對應到protected/
views/site/loginForm.php. 這個文件返回一個包含配置信息的PHP數組:

return array(
        'title'=>'Please provide your login credential',
        'elements'=>array(
                'username'=>array(
                        'type'=>'text',
                        'maxlength'=>32,
                ),
                'password'=>array(
                        'type'=>'password',
                        'maxlength'=>32,
                ),
                'rememberMe'=>array(
                        'type'=>'checkbox',
                )
        ),
        'buttons'=>array(
                'login'=>array(
                        'type'=>'submit',
                        'label'=>'Login',
                ),
        ),
);
 

這個配置文件是用來初始化CForm的屬性的,格式是ame-value 這樣的配對模式。最重要的兩個屬性,就是我們之前提到過的,'elements'以及'buttons'。 他們都包含了一個自己的元素列表,後續我們會更加詳細的演示如何配置這些元素。

現在,我們終於可以來寫登錄視圖的腳本了,可以簡單到如此的地方,歎爲觀止:

<h1>Login</h1>
<div class="form">
<?php echo $form; ?>
</div>

1.3 指定表單元素

採用了表單構造器之後,我們的主要工作就從編寫視圖腳本變成指定表單元素了。在本章節中,我們來演示如何設定表單元素的屬性。下面我們將描述一下如何使用elements,buttons就不說了,幾乎都一樣的。

CForm::elements 這個屬性用一個數組作爲他的屬性值。裏面的成員,是一個可以單獨輸入的元素,例如一個靜態文本框或者是一個子表單。

指定輸入元素

一個輸入元素,一般來說由標籤,輸入框,提示文本,錯誤顯示這4個組成。該元素必須與模型的某個屬性關聯。指定這個元素,就好比給輸入元素實例化。下面展示一下單個元素的定義。

'username'=>array(
        'type'=>'text',
        'maxlength'=>32,
),

以上代碼表明,模型的屬性是username, 用戶輸入區域類型是test, 輸入的長度最長爲32.

對於CFormInputElement來說,任何可以改寫的屬性,都可以用上面的形式來配置。例如我們可以指定“hint” 這個選項,來顯示該輸入的提示信息。如果元素是下拉框,複選框,單選框之類的,我們也可以用items這個選項。如果某個選項不是CFormInputElement的屬性,就會被認爲是HTML的輸入元素屬性。上例中,'maxlength'就不是CFormInputElement的屬性,他會作爲HTML輸入框的屬性被呈現。CFormInputElement內置的元素有:

text, hidden, password, textarea, file, radio, checkbox, listbox, dropdownlist, checkboxlist, radiolist

在上述的內置類型中,我們重點闡述一下那些列表類型,下拉框列表, 複選框列表,單選框列表。這3種類型要設置相對應的輸入元素的items屬性。可以按照下面的做:

'gender'=>array(
        'type'=>'dropdownlist',
        'items'=>User::model()->getGenderOptions(),
        'prompt'=>'Please select:',
        ),
...
class User extends CActiveRecord
{
        public function getGenderOptions()
        {
                return array(
                        0 => 'Male',
                        1 => 'Female',
                );
        }
}

上述中,選擇器包含了兩個選項:Male和Female。

除了這些內置類型,type選項也可以是widget的別名,或是某個路徑的別名。如果是Widget的別名,那麼這個widget必須是CInputWidget 或 CJuiInputWidget.的擴展。在渲染這個元素的時候,被指定的widget類會被創建,並渲染。

指定靜態文本

很多情況下,一個表單,除了有用戶輸入的元素,還有很多裝飾性的HTML代碼。例如,表單的不同部分,需要水平線來劃分;某張圖片爲了美觀,必須放置在表單的特定位置。我們把這些HTML代碼作爲靜態文本,放在CForm::elements 的元素集裏。我們把靜態文本字符串作爲一個元素數組放置在CForm::elements。例如:

return array(
        'elements'=>array(
                ......
                'password'=>array(
                        'type'=>'password',
                        'maxlength'=>32,
                ),
                '<hr />',
                'rememberMe'=>array(
                        'type'=>'checkbox',
                )
        ),
        ......
);

上面的代碼中,我們在密碼與“記住我”之間,加了一行水平的分割線。

靜態文本最好是在他的文本內容以及位置不規則的時候使用。如果每個輸入元素都要被裝飾的一致,我們可以定製表單的渲染方法。

指定子表單

子表單是用來把一個很長的表單,切割成邏輯上連接的幾個表單。例如,我們會把用戶註冊分爲兩個子表單:登錄信息和配置信息。不同子表單之間,可能是同一個模型,也可能不是。例如在用戶註冊的時候,我們把用戶登錄信息跟配置信息分別存儲於數據庫中的兩個不同的表,也就是2個模型,那麼每個子表單都會有自己所對應的模型;如果我們把所有的用戶信息配置信息存儲在一個數據庫表中,那麼那兩個子表單都沒有數據模型了,因爲他們共享他們的父表單的數據模型。

一個子表單也是關聯一個CForm的對象,爲了指定一個子表單,我們必須配置CForm::elements的一個元素,他的類型是form:

  1. return array(  
  2.     'elements'=>array(  
  3.         ......  
  4.         'user'=>array(  
  5.             'type'=>'form',  
  6.             'title'=>'Login Credential',  
  7.             'elements'=>array(  
  8.                 'username'=>array(  
  9.                     'type'=>'text',  
  10.                 ),  
  11.                 'password'=>array(  
  12.                     'type'=>'password',  
  13.                 ),  
  14.                 'email'=>array(  
  15.                     'type'=>'text',  
  16.                 ),  
  17.             ),  
  18.         ),  
  19.         'profile'=>array(  
  20.             'type'=>'form',  
  21.             ......  
  22.         ),  
  23.         ......  
  24.     ),  
  25.     ......  
  26. ); 

就像配置跟表單一樣,我們也需要爲子表單進行屬性設置。如果子表單需要關聯某個數據模型,我們可以跟配置跟表單一樣的配置這個子表單。

很多情況下,我們想用一個表單關聯一個其他的類,而並非CForm。例如,我們會從CForm中擴展出來,定製他的渲染邏輯。通過對這個表單的元素設定,子表單會自動關聯父表單的對象。

1.4 訪問表單元素

訪問表單元素,就如訪問數組元素一樣的簡單。CForm::elements的屬性返回一個CFormElementCollection對象,這個對象是從Cmap繼承而來,並可以被當做普通數組訪問。例如,想要訪問登錄表單重的username這個元素,我們可以這樣做:

$username = $form->elements['username'];

如果要訪問用戶註冊的email元素,可以:

$email = $form->elements['user']->elements['email'];

上述的代碼還可以簡化成:

$username = $form['username'];
$email = $form['user']['email'];

1.5 創建嵌套表單

前面已經介紹了子表單,我們將包含子表單的表單稱之爲嵌套表單。本節中,我們將會把用戶註冊的表單,作爲演示例子。我們假設用戶的登錄信息存放在Uuser模型中,用戶的配置信息存儲在Profile的模型中。

一如往常,我們先創建一個用戶註冊的動作:

  1. public function actionRegister()  
  2. {  
  3.     $form = new CForm     ('application.views.user.registerForm');  
  4.     $form['user']->model = new User;  
  5.     $form['profile']->model = new Profile;  
  6.     if($form->submitted('register') && $form->validate())  
  7.     {  
  8.         $user = $form['user']->model;  
  9.         $profile = $form['profile']->model;  
  10.         if($user->save(false))  
  11.         {  
  12.             $profile->userID = $user->id;  
  13.             $profile->save(false);  
  14.             $this->redirect(array('site/index'));  
  15.         }  
  16.     }  
  17.     $this->render('register'array('form'=>$form));  

我們創建了一個application.views.user.registerForm的表單。在這個表單提交併驗證通過以後,我們保存用戶信息以及用戶配置信息。我們通過當前的子表單對象的屬性來訪問用戶信息以及配置信息。因爲已經驗證過了輸入數據,所以直接調用$user->save(false)。對於配置信息,如出一轍。

接下來,我們來編寫註冊表單的配置文件/views/user/registerForm.php:

  1. return array(  
  2.     'elements'=>array(  
  3.         'user'=>array(  
  4.             'type'=>'form',  
  5.             'title'=>'Login information',  
  6.             'elements'=>array(  
  7.                 'username'=>array(  
  8.                     'type'=>'text',  
  9.                 ),  
  10.                 'password'=>array(  
  11.                     'type'=>'password',  
  12.                 ),  
  13.                 'email'=>array(  
  14.                     'type'=>'text',  
  15.                 )  
  16.             ),  
  17.         ),  
  18.         'profile'=>array(  
  19.             'type'=>'form',  
  20.             'title'=>'Profile information',  
  21.             'elements'=>array(  
  22.                 'firstName'=>array(  
  23.                     'type'=>'text',  
  24.                 ),  
  25.                 'lastName'=>array(  
  26.                     'type'=>'text',  
  27.                 ),  
  28.             ),  
  29.         ),  
  30.     ),  
  31.     'buttons'=>array(  
  32.         'register'=>array(  
  33.             'type'=>'submit',  
  34.             'label'=>'Register',  
  35.         ),  
  36.     ),  
  37. ); 

上例中,指定子表單時,我們一樣的爲這子表單指定了titile的屬性。默認的表單渲染時,會將這些標題也括進去。

最終,我們編寫用戶註冊的腳本語句,一如的簡潔:

 

  1. <h1>Register</h1>  
  2. <div class="form">  
  3. <?php echo $form; ?>  
  4. </div> 

1.6 自定義表單顯示

使用表單構造器的最大好處,就是分離表單元素的邏輯與表現。所以,我們可以通過重載CForm::render的方法,或者是提供一個局部視圖,來定製自己的表單渲染。這兩種方法都可以保證表單數據的完整性,便於複用。

重載CForm::render 時, 主要遍歷CForm::elements 和CForm::buttons, 然後調用每個元素的CFormElement::render。 例如:

  1. class MyForm extends CForm  
  2. {  
  3.     public function render()  
  4.     {  
  5.         $output = $this->renderBegin();  
  6.         foreach($this->getElements() as $element)  
  7.             $output .= $element->render();  
  8.         $output .= $this->renderEnd();  
  9.         return $output;  
  10.     }  

我們也可以寫一個視圖腳本文件_form來渲染這個表單:

  1. <?php  
  2. echo $form->renderBegin();  
  3. foreach($form->getElements() as $element)  
  4.     echo $element->render();  
  5. echo $form->renderEnd(); 

要調用上面的腳本,我們只要簡單的調用:

  1. <div class="form">  
  2. $this->renderPartial(' form'array('form'=>$form));  
  3. </div> 

如果通用的渲染方法無法渲染一些特殊的表單(例如某些表單的元素需要特殊裝飾),我們可以按照下面的方法寫視圖腳本:

  1. some complex UI elements here  
  2. <?php echo $form['username']; ?>  
  3. some complex UI elements here  
  4. <?php echo $form['password']; ?>  
  5. some complex UI elements here 

最後的這種方法,表單構造器看起來對我們沒有你太多的幫忙,我們還是要寫很多的視圖腳本代碼。但是始終是有好處的,表單被分成了獨立配置文件,有助於開發人員專注於邏輯即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章