Yii2学习笔记系列16——Application Structure-Widgets(应用结构之小部件)

日 15 五月 2016

小部件

小部件是视图中可以重复使用的构建单元,用于以面向对象的方式创建复杂的并且可以配置的用户接口元素。例如,一个日期小部件会生成一个允许用户选择他们想要输入的日期的漂亮组件,而我们要做的只是把如下的代码插入到视图中即可:

<?php
use yii\jui\DatePicker;
?>
<?= DatePicker::widget(['name' => 'date']) ?>

Yii提供了许多优秀的小部件,比如activeFormmenujQuery UI widgetsTwitter Bootstrap widgets。下面我们会讲解关于小部件的基础只是,如果你想了解某个小部件请参考对应的API文档。

使用小部件

小部件主要在视图中使用。我们可以通过调用yii\base\Widget::widget()方法在视图中使用小部件,该方法接收一个配置数组用于初始化小部件并且返回小部件的渲染结果。例如,如下示例代码插入一个配置使用俄语并且输入的值会作为$model模型的from_date属性的日期小部件:

<?php
use yii\jui\DatePicker;
?>
<?= DatePicker::widget([
    'model' => $model,
    'attribute' => 'from_date',
    'language' => 'ru',
    'clientOptions' => [
        'dateFormat' => 'yy-mm-dd',
    ],
]) ?>

有些小部件可以在yii\base\Widget::begin()方法和yii\base\Widget::end()方法之间调用内容块。例如,下面的代码使用yii\widgets\ActiveForm小部件生成了一个登录的Form表单,小部件会生成在begin()方法和end()方法被调用的地方分别生成开始和结束的<form>标签,位于开始和结束标签中间的任何数据也都会被渲染。

<?php
use yii\widgets\ActiveForm;
use yii\helpers\Html;
?>

<?php $form = ActiveForm::begin(['id' => 'login-form']); ?>

    <?= $form->field($model, 'username') ?>

    <?= $form->field($model, 'password')->passwordInput() ?>

    <div class="form-group">
        <?= Html::submitButton('Login') ?>
    </div>

<?php ActiveForm::end(); ?>

需要注意的是,与yii\base\Widget::widget()方法(该方法返回渲染结果)不同的是,yii\base\Widget::begin()方法返回一个我们可以用来构建小部件内容的小部件实例。

配置全局默认值

一个小部件类型的全局默认值可以通过DI容器进行配置:

\Yii::$container->set('yii\widgets\LinkPager', ['maxButtonCount' => 5]);

更多详情请参阅DI容器指南中“实际使用”一节。

创建小部件

为了创建小部件,我们需要继承yii\base\Widget类,并且重写yii\base\Widget::init()方法 和/或 yii\base\Widget::run()方法。通常来讲,init()方法主要包含处理小部件属性的代码,run()方法主要包含生成小部件渲染结果的代码。run()方法可以将渲染结果直接输出也可以以字符串形式返回。

下面的示例中,HelloWidget 将赋值给它的message属性进行HTML编码并且显示,如果message属性没有设置,则默认会显示“Hello World”。

namespace app\components;

use yii\base\Widget;
use yii\helpers\Html;

class HelloWidget extends Widget
{
    public $message;

    public function init()
    {
        parent::init();
        if ($this->message === null) {
            $this->message = 'Hello World';
        }
    }

    public function run()
    {
        return Html::encode($this->message);
    }
}

要想使用这个小部件,只需要在视图中简单的插入如下代码:

<?php
use app\components\HelloWidget;
?>
<?= HelloWidget::widget(['message' => 'Good morning']) ?>

下面是一个HelloWidget的变种版本,它主要获取begin()方法和end()方法中间的内容,然后进行HTML编码并且进行展示:

namespace app\components;

use yii\base\Widget;
use yii\helpers\Html;

class HelloWidget extends Widget
{
    public function init()
    {
        parent::init();
        ob_start();
    }

    public function run()
    {
        $content = ob_get_clean();
        return Html::encode($content);
    }
}

如你所见,PHP的输入缓冲在init()方法中启动,所以init()方法和run()方法中间的任意输出都会被捕获、处理并且在run()方法中被返回。

注意:当你调用yii\base\Widget::begin()方法时,会创建一个小部件的实例,并且init()方法会在小部件的构造方法结尾处被调用。当你调用yii\base\Widget::end()方法时,run()方法会在被调用,并且它的返回结果会被end()方法输出。

下面的代码展示了HelloWidget的变种的使用方法:

<?php
use app\components\HelloWidget;
?>
<?php HelloWidget::begin(); ?>

    content that may contain <tag>'s

<?php HelloWidget::end(); ?>

有时候,一个小部件可以能需要渲染一大段内容,我们除了可以将内容嵌入到run()方法之外,更好的办法就是将其放入一个视图中并且调用yii\base\Widget::render()方法去渲染它。例如:

public function run()
{
    return $this->render('hello');
}

默认情况下,小部件的视图文件存储在WidgetPath/views目录下,其中WidgetPath表示包含小部件类文件的路径。因此,上述示例中,假设小部件类位于@app/components目录下,则渲染@app/components/views/hello.php文件。我们可以通过重写yii\base\Widget::getViewPath()方法来自定义包含小部件视图代码的目录。

最佳实践

小部件是通过面向对象来实现视图代码重用的一种方式。

当创建小部件时,仍然需要遵循MVC模式,通常我们需要将逻辑代码保存在小部件类中,将展示代码放在视图中。

小部件应该被设计成独立的,也就是说,当我们使用一个小部件的时候,只需要把它丢进我们的视图中即可,不需要做任何其它多余的工作。但是当小部件需要应用外部的资源比如CSS、JS或者图片时会比较棘手,幸运的是,Yii提供了资源包的管理,能够充分解决这个问题。

当一个小部件只包含视图代码的时候跟视图是很相似的,事实上,在这种情况下,小部件与视图的唯一区别就是小部件是重用类,而视图只是应用中使用的普通PHP脚本。

Category: PHP Develop

Comments