依赖AJAX的下拉列表

通常,你会需要一个带有两个下拉列表的表单,一个表单的值依赖于另外一个。使用Yii内置的AJAX功能,你可以创建这样一个下拉列表。

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. 创建@app/model/Product.php
  1. <?php
  2. namespace app\models;
  3. use yii\db\ActiveRecord;
  4. class Product extends ActiveRecord
  5. {
  6. public function rules()
  7. {
  8. return [
  9. ['title', 'string'],
  10. [['title', 'category_id', 'sub_category_id'],
  11. 'required'],
  12. ['category_id', 'exist', 'targetAttribute' => 'id',
  13. 'targetClass' => 'app\models\Category'],
  14. ['sub_category_id', 'exist', 'targetAttribute' =>
  15. 'id', 'targetClass' => 'app\models\Category'],
  16. ];
  17. }
  18. public function attributeLabels()
  19. {
  20. return [
  21. 'category_id' => 'Category',
  22. 'sub_category_id' => 'Sub category',
  23. ]; }
  24. }
  1. 创建@app/models/Category.php模型:
  1. <?php
  2. namespace app\models;
  3. use yii\db\ActiveRecord;
  4. class Category extends ActiveRecord
  5. {
  6. public function rules()
  7. {
  8. return [
  9. ['title', 'string'],
  10. ];
  11. }
  12. /**
  13. * @return array
  14. */
  15. public static function getSubCategories($categoryId)
  16. {
  17. $subCategories = [];
  18. if ($categoryId) {
  19. $subCategories = self::find()
  20. ->where(['category_id' => $categoryId])
  21. ->asArray()
  22. ->all();
  23. }
  24. return $subCategories;
  25. }
  26. }
  1. 创建create_category_and_product_tables migration:
  1. ./yii migrate/create create_category_and_product_tables
  1. 更新刚刚创建的migration方法:
  1. <?php
  2. use yii\db\Schema;
  3. use yii\db\Migration;
  4. class m150813_005030_create_categories extends Migration
  5. {
  6. public function up()
  7. {
  8. $tableOptions = null;
  9. $this->createTable('{{%product}}', [
  10. 'id' => Schema::TYPE_PK,
  11. 'category_id' => Schema::TYPE_INTEGER . ' NOT NULL',
  12. 'sub_category_id' => Schema::TYPE_INTEGER . ' NOT NULL',
  13. 'title' => Schema::TYPE_STRING . ' NOT NULL',
  14. ], $tableOptions);
  15. $this->createTable('{{%category}}', [
  16. 'id' => Schema::TYPE_PK,
  17. 'category_id' => Schema::TYPE_INTEGER,
  18. 'title' => Schema::TYPE_STRING . ' NOT NULL',
  19. ], $tableOptions);
  20. $this->addForeignKey('fk_product_category_id',
  21. '{{%product}}', 'category_id', '{{%category}}', 'id');
  22. $this->addForeignKey('fk_product_sub_category_id','{{%product}}', 'category_id', '{{%category}}', 'id');
  23. $this->batchInsert('{{%category}}', ['id', 'title'], [
  24. [1, 'TV, Audio/Video'],
  25. [2, 'Photo'],
  26. [3, 'Video']
  27. ]);
  28. $this->batchInsert('{{%category}}', ['category_id', 'title'], [
  29. [1, 'TV'],
  30. [1, 'Acoustic System'],
  31. [2, 'Cameras'],
  32. [2, 'Flashes and Lenses '],
  33. [3, 'Video Cams'],
  34. [3, 'Action Cams'],
  35. [3, 'Accessories']
  36. ]);
  37. }
  38. public function down()
  39. {
  40. $this->dropTable('{{%product}}');
  41. $this->dropTable('{{%category}}');
  42. }
  43. }

如何做…

  1. 创建控制器文件,@app/controllers/DropdownController.php
  1. <?php
  2. namespace app\controllers;
  3. use app\models\Product;
  4. use app\models\Category;
  5. use app\models\SubCategory;
  6. use Yii;
  7. use yii\helpers\ArrayHelper;
  8. use yii\helpers\Json;
  9. use yii\web\Controller;
  10. use yii\web\HttpException;
  11. class DropdownController extends Controller
  12. {
  13. public function actionGetSubCategories($id)
  14. {
  15. if (!Yii::$app->request->isAjax) {
  16. throw new HttpException(400, 'Only ajax request is allowed.');
  17. }
  18. return Json::encode(Category::getSubCategories($id));
  19. }
  20. public function actionIndex()
  21. {
  22. $model = new Product();
  23. if ($model->load(Yii::$app->request->post()) &&
  24. $model->validate()) {
  25. Yii::$app->session->setFlash('success',
  26. 'Model was successfully saved'
  27. );
  28. }
  29. return $this->render('index', [
  30. 'model' => $model,
  31. ]);
  32. }
  33. }
  1. 创建视图文件@app/views/dropdown/index.php
  1. <?php
  2. use yii\bootstrap\ActiveForm;
  3. use yii\helpers\Html;
  4. use yii\helpers\Url;
  5. use app\models\Category;
  6. use yii\helpers\ArrayHelper;
  7. use yii\web\View;
  8. $url = Url::toRoute(['dropdown/get-sub-categories']);
  9. $this->registerJs("
  10. (function(){
  11. var select = $('#product-sub_category_id');
  12. var buildOptions = function(options) {
  13. if (typeof options === 'object') {
  14. select.children('option').remove();
  15. $('<option />')
  16. .appendTo(select)
  17. .html('Select a sub category')
  18. $.each(options, function(index, option) {
  19. $('<option />', {value:option.id})
  20. .appendTo(select)
  21. .html(option.title);
  22. });
  23. }
  24. };
  25. var categoryOnChange = function(category_id){
  26. $.ajax({
  27. dataType: 'json',
  28. url: '" . $url . "&id=' + category_id ,
  29. success: buildOptions});
  30. };
  31. window.buildOptions = buildOptions;
  32. window.categoryOnChange = categoryOnChange;
  33. })();
  34. ", View::POS_READY);
  35. ?>
  36. <h1>Product</h1>
  37. <?php if (Yii::$app->session->hasFlash('success')): ?>
  38. <div class="alert alert-success"><?=
  39. Yii::$app->session->getFlash('success'); ?></div>
  40. <?php endif; ?>
  41. <?php $form = ActiveForm::begin(); ?>
  42. <?= $form->field($model, 'title')->textInput() ?>
  43. <?= $form->field($model,
  44. 'category_id')->dropDownList(ArrayHelper::map(
  45. Category::find()->where('category_id IS NULL')->asArray()->all(),'id', 'title'), [
  46. 'prompt' => 'Select a category',
  47. 'onChange' => 'categoryOnChange($(this).val());',
  48. ]) ?>
  49. <?= $form->field($model, 'sub_category_id')->dropDownList(
  50. ArrayHelper::map(Category::getSubCategories($model->sub_category_id), 'id' ,'title'), [
  51. 'prompt' => 'Select a sub category',
  52. ]) ?>
  53. <div class="form-group">
  54. <?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>
  55. </div>
  56. <?php ActiveForm::end(); ?>
  1. 打开index.php?r=dropdown运行dropdown控制器,然后添加一个新的产品,Canon - EOS Rebel T6i DSLR

依赖AJAX的下拉列表 - 图1

  1. 正如你所见到的,Category输入框有三个选项。选择Photo选项,然后第二个输入选择将会有两个更多的选项:

依赖AJAX的下拉列表 - 图2

  1. 如果你选择了另外一个分类。你将会得到这个分类的子分类。

工作原理…

在这个例子中,我们有两个依赖的列表,分类和子分类,以及一个模型Category。主要的思想比较简单:我们将JQuery的onChange事件绑定到表单的category_id字段上。每次当用户修改字段时,我们的应用会发送一个AJAX请求到get-sub-categories动作上。这个动作返回一个JSON格式的子分类列表,然后在客户端,将子列表进行渲染。