【问题标题】:yii2: dropdown select field with likeyii2:带有like的下拉选择字段
【发布时间】:2015-07-11 17:35:39
【问题描述】:

我有一个下拉选择字段,其中包含很长的值列表,我想像搜索一样从列表中选择值,以便更轻松地选择值,例如在我的列表中:

我有

DR X
DR Y
Dr Z

因此,如果键入 Z,则值 Dr Z 将突出显示以供选择。现在我必须输入 Dr Z 以突出显示该值。

我的下拉列表代码如下:

<?= $form->field($model, 'doctor_name')->DropDownList(ArrayHelper::map(\app\models\Doctor::find()->all(), 'id', 'doctor_name' ),
[ 'prompt' => 'Please Select' ]) ?>

寻找一些提示。

【问题讨论】:

  • 看起来你正在寻找类似 jquery-ui 组合框的东西:jqueryui.com/autocomplete/#combobox
  • 我认为您的建议是正确的,但是如何实现。我正在从数据库中获取值
  • 我可以建议您重命名您的问题标题以包含单词“combobox”吗?

标签: php jquery mysql yii2


【解决方案1】:

您要查找的内容类似于 jquery Ui 的组合框。如何在您的软件中实现这一点可以通过多种方式完成,但这里有一个:

制作组合框小部件

Yii2 带有多个 jquery-ui 小部件。这第一步是install JUI for Yii2

完成此操作后,您将可以访问所有这些小部件。但是没有组合框小部件(只有一个标准的自动完成)。没关系,我们仍然可以使用此插件中的资源。

为小部件制作 AssetBundle

创建一个ComboboxAssetBundle.php 文件,将其放入assets 文件夹。应该如下(这是advanced template 和frontend 入口点的示例):

namespace frontend\assets;

use yii\web\AssetBundle;

class ComboboxAssetBundle extends AssetBundle
{
    public $sourcePath = '@frontend/assets';
    public $css = [
    ];
    public $js = [
        'js/combobox.js', // Lets load your custom JS
    ];
    public $depends = [
        'yii\web\JqueryAsset', // requires Jquery
        'yii\jui\JuiAsset', // requires JUI
    ];
}

您的 @frontend/assets/js/combobox.js 文件应包含组合框 js 代码:

(function( $ ) {
    $.widget( "custom.combobox", {
      _create: function() {
        this.wrapper = $( "<span>" )
          .addClass( "custom-combobox" )
          .insertAfter( this.element );

        this.element.hide();
        this._createAutocomplete();
        this._createShowAllButton();
      },

      _createAutocomplete: function() {
        var selected = this.element.children( ":selected" ),
          value = selected.val() ? selected.text() : "";

        this.input = $( "<input>" )
          .appendTo( this.wrapper )
          .val( value )
          .attr( "title", "" )
          .addClass( "custom-combobox-input ui-widget ui-widget-content ui-state-default ui-corner-left" )
          .autocomplete({
            delay: 0,
            minLength: 0,
            source: $.proxy( this, "_source" )
          })
          .tooltip({
            tooltipClass: "ui-state-highlight"
          });

        this._on( this.input, {
          autocompleteselect: function( event, ui ) {
            ui.item.option.selected = true;
            this._trigger( "select", event, {
              item: ui.item.option
            });
          },

          autocompletechange: "_removeIfInvalid"
        });
      },

      _createShowAllButton: function() {
        var input = this.input,
          wasOpen = false;

        $( "<a>" )
          .attr( "tabIndex", -1 )
          .attr( "title", "Show All Items" )
          .tooltip()
          .appendTo( this.wrapper )
          .button({
            icons: {
              primary: "ui-icon-triangle-1-s"
            },
            text: false
          })
          .removeClass( "ui-corner-all" )
          .addClass( "custom-combobox-toggle ui-corner-right" )
          .mousedown(function() {
            wasOpen = input.autocomplete( "widget" ).is( ":visible" );
          })
          .click(function() {
            input.focus();

            // Close if already visible
            if ( wasOpen ) {
              return;
            }

            // Pass empty string as value to search for, displaying all results
            input.autocomplete( "search", "" );
          });
      },

      _source: function( request, response ) {
        var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
        response( this.element.children( "option" ).map(function() {
          var text = $( this ).text();
          if ( this.value && ( !request.term || matcher.test(text) ) )
            return {
              label: text,
              value: text,
              option: this
            };
        }) );
      },

      _removeIfInvalid: function( event, ui ) {

        // Selected an item, nothing to do
        if ( ui.item ) {
          return;
        }

        // Search for a match (case-insensitive)
        var value = this.input.val(),
          valueLowerCase = value.toLowerCase(),
          valid = false;
        this.element.children( "option" ).each(function() {
          if ( $( this ).text().toLowerCase() === valueLowerCase ) {
            this.selected = valid = true;
            return false;
          }
        });

        // Found a match, nothing to do
        if ( valid ) {
          return;
        }

        // Remove invalid value
        this.input
          .val( "" )
          .attr( "title", value + " didn't match any item" )
          .tooltip( "open" );
        this.element.val( "" );
        this._delay(function() {
          this.input.tooltip( "close" ).attr( "title", "" );
        }, 2500 );
        this.input.autocomplete( "instance" ).term = "";
      },

      _destroy: function() {
        this.wrapper.remove();
        this.element.show();
      }
    });
  })( jQuery );

制作小部件

现在您需要创建小部件。为了与上述保持一致,让我们使用以下内容创建一个@frontend/widgets/Combobox.php 文件:

namespace frontend\widgets;

use yii\base\Widget;
use frontend\assets\ComboboxAssetBundle;
use yii\base\InvalidConfigException;
use yii\helpers\Html;
use yii\helpers\ArrayHelper;
use Yii;

class Combobox extends Widget
{
    /**
     * @var form object if desired
     */
    public $form;

    /**
     * @var model object tu use
     */
    public $model;

    /**
     * @var model attribute this dropdown is for.
     */
    public $attribute;

    /**
     * @var options for the dropdown.
     */
    public $options = [];

    /**
     * @var items for the dropdown list.
     */
    public $items = [];

    public function init()
    {
        if(!isset($this->attribute))
        {
            throw new InvalidConfigException("an attribute is required");
        }
    }

    public function run()
    {
        //register your asset:
        ComboboxAssetBundle::register(Yii::$app->view);
        Yii::$app->view->registerJs('$( ".combobox" ).combobox();');

        //add a class to the options
        $options = ArrayHelper::merge($this->options, ['class'=>'combobox']);

        if(isset($this->form))
        {
            return $this->form->field($this->model, $this->attribute)->dropDownList($this->items, $options);
        }
        elseif(isset($this->model))
        {
            return Html::activeDropDownList($this->model, $this->attribute, $this->items, $options);
        }
        else
        {
            return Html::dropDownList($this->attribute, NULL, $this->items, $options);
        }
    }
}

调用小部件

有了这个,您应该能够使用Combobox::widget([]) 调用小部件,如下所示:

<?= Combobox::widget([
    'form' => $form,
    'model' => $model,
    'attribute' => 'doctor_name',
    'items' => ArrayHelper::map(\app\models\Doctor::find()->all(), 'id', 'doctor_name' ),
    'options' => [ 'prompt' => 'Please Select' ]) ?>

我没有对此进行测试,但应该有足够的信息让您轻松地让它工作。这样做的好处是您可以在应用程序的任何地方重复使用这种类型的下拉菜单。

如果您需要任何额外信息,请告诉我。

编辑:

有时您需要让您的应用强制复制您的资产,否则您将看不到任何更改。有很多方法可以执行/配置此操作,但在您的开发环境中本地执行我发现最简单的方法是将以下内容添加到您的 local 配置文件中:

'components' => [
    //...
    'assetManager' => [
        'forceCopy' => TRUE,
    ]
],

【讨论】:

  • 嗨 D.Mill - 感谢您提供如此详尽的答案。我正在使用基本模板。我已经按照基本模板修改了代码。但它按原来的方式工作,但不像预期的那样工作。
  • 糟糕,我忘记了最重要的。我已经用以下内容更正了我的答案:文件底部的 combobox.js 将 #combobox 更改为 .combobox 。在小部件中,我在顶部添加了use yii\helpers\ArrayHelper,并将组合框类添加到run() 的下拉列表中。希望它现在有效。
  • 嗨 - D.Mill - 仍然没有运气。下拉菜单显示正常,但没有按需要工作。
  • @Pawan 这很奇怪,它似乎对我来说很好用。我刚测试过。我做了一个改动,删除了combobox.js 的底部并将其放在小部件中。然而,这不应该是必需的。您确定所有脚本都已正确加载吗?你能检查一下你的导航器吗? (萤火虫或 chrome 开发工具)
  • 哦,我刚刚有了一个想法并编辑了我的答案。请在进行任何其他更改之前更改它:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-30
相关资源
最近更新 更多