“中国要复兴、富强,必须在开源软件领域起到主导作用,为了国家安全和人类发展,责无旁贷,我们须为此而奋斗”——By:云客
自动完成是指用户在表单上进行输入时,系统依据已输入内容给出一个相关的备选列表(或叫备选菜单),该列表随输入变化而自动更新,用户可从列表中直接选择,从而代替继续输入,输入越多相关程度越高,相关性由匹配逻辑决定,通常是字符串包含,但相关性逻辑是可以自定义的,因此有时这也用于推荐信息;列表里面的数据来源能以变量方式存储在前端js中(称为本地数据),如果量较大也可以储存在后端,通过ajax远程取回;自动完成让用户输入更快、更准确、更方便,可进行自动完成的元素有input、textarea或具备contenteditable属性的元素
基础:
drupal前端系统的自动完成功能是建立在jquery UI基础之上的,明白了jquery UI自动完成后你将很容易理解drupal自动完成的实现,jquery UI组件的官方下载地址是:
https://jqueryui.com/download/
在该页面你可以选择jQuery UI的版本、下载的库中包含的组件、UI使用的主题样式,然后点击下载,这里假设你选择了全部组件,并下载解压到“jqueryui”目录,这里提供一个自动完成的使用示例:
自动完成示例:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery UI 自动完成示范页</title>
<link href="jqueryui/jquery-ui.css" rel="stylesheet">
</head>
<body>
<h2>自动完成展示</h2>
<div>
<input id="autocomplete">
</div>
<br><br><br><br><br><br><br>
<div id="yunke"></div>
<script src="jqueryui/external/jquery/jquery.js"></script>
<script src="jqueryui/jquery-ui.js"></script>
<script>
var availableTags = [
"ActionScript",
"AppleScript",
"Asp",
"BASIC",
"C",
"C++",
"Clojure",
"COBOL",
"ColdFusion",
"Erlang",
"Fortran",
"Groovy",
"Haskell",
"Java",
"JavaScript",
"Lisp",
"Perl",
"PHP",
"Python",
"Ruby",
"Scala",
"Scheme"
];
$( "#autocomplete" ).autocomplete({
source: availableTags,
appendTo: '#yunke',
autoFocus: true,
delay: 100,
disabled: false,
minLength: 1,
position: { my: "left top", at: "left bottom", collision: "none" }
});
</script>
</body>
</html>
示例说明:
该例引用了三个关键文件: UI样式、jquery库、jquery-ui库,jquery库必须在jquery-ui库之前引用,后者扩展了前者,如你所见,简单的使用UI库提供的方法即可实现自动完成功能。
关于jquery-ui库的自动完成用法不属于本系列范围,如何使用请详见官方文档:
https://api.jqueryui.com/autocomplete/
在继续阅读前强烈建议你花几十分钟学习该API,本篇仅列出自动完成相关的选项。
jquery-ui原生自动完成选项及含义:
如下:
appendTo:一个jquery选择器,用于指示用来放置备选列表的元素(注意该选项只是指示DOM放置位置,并不影响显示位置,显示位置见position选项),默认为NULL,此时将查找输入元素的第一个有“ui-front”类属性的父元素,如果存在即用,而不管值为何,如果不存在将使用body元素
autoFocus:布尔值,默认为false,指示在备选列表显示时是否选中第一个元素
classes:用于给控件元素添加额外的类,这样便于自定义控制,值为一个对象,在控件主题化中用到的任意类名可以作为属性名,属性值为要添加的自定义类,这使得在原类出现的地方额外添加了自定义类
delay:一个毫秒为单位的整数,默认为300,指定击键开始到执行数据搜索之间的时间间隔,用于控制备选列表的延迟显示,总延迟还包括搜索时间、网络延迟等
disabled:布尔值,是否禁用自动完成功能,默认为false
minLength:一个整数,默认为1,指示执行数据搜索前用户需要键入的最少字符数,作用相当于频率限制,当有少量本地数据时可以设置为0,当有巨量数据时,应该提高该值
position:一个对象,默认为:{ my: "left top", at: "left bottom", collision: "none" },指示备选列表的显示位置,默认相对于输入元素,但可用该对象的of属性指定相对于其他元素,值为选择器,详见jquery位置组件
source:指定用于自动完成的数据来源,可以是数组、字符串或函数,分别如下:
当为数组时,该数组代表本地尚未过滤过的完整数据,系统会依据输入进行匹配并过滤(匹配即是过滤),然后进行显示,数组有字符串构成和对象构成两种格式(不可混用),对象构成时,对象的“label”属性的值用于在备选列表中显示,“value”属性作为输入元素的值,以label进行输入匹配(“包含”匹配方式),对象的label和value属性可只存在一个,此时另一个将采用相同值;
当为字符串时,应该是一个url,会被附加term查询参数(值为用户输入)后执行ajax请求,应该返回json,解码后格式同数组,但不会再被过滤,因此远程服务器应自行过滤,相关性由服务器决定;
当为函数时,其接收两个参数source: function (request,responseFun) { },第一个参数是一个对象,称为请求对象,其仅有一个属性“term”,值为用户输入,第二个参数是一个回调函数,称为响应回调,响应回调仅有一个参数用于接收源数据,不论是否发生错误,在回调中都必须调用响应回调,接收的数据应是已被过滤过的(相关性已确定),格式同前文讲的数组情况,当发生错误时可以不传递参数,或为空数组、null、空对象
除以上选项外,还有以下事件选项:
change、close、create、focus、open、response、search、select
对应的值为事件处理函数
drupal自动完成前端库:
库名:core/drupal.autocomplete
文件:core/misc/autocomplete.es6.js
依赖以下库:
- core/jquery
- core/drupal
- core/drupalSettings
- core/drupal.ajax
- core/jquery.ui.autocomplete
示例:
这里以本系列配套模块“yunke_help”来做演示
第一步:
建立一个表单yunke_help/src/Form/YunkeForm.php内容如下:
<?php
/**
* 演示表单自动完成操作
*/
namespace Drupal\yunke_help\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
class YunkeForm extends FormBase
{
public function getFormId()
{
return 'yunke_help_form';
}
public function buildForm(array $form, FormStateInterface $form_state)
{
$form['value'] = array(
'#type' => 'textfield',
'#attributes' => ['data-autocomplete-first-character-blacklist' => '/#?',],
'#autocomplete_route_name' => 'yunke_help.test1',
'#autocomplete_route_parameters' => [],
'#title' => '输入值',
'#description' => "自动完成演示示例",
);
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('submit'),
'#button_type' => 'primary',
);
return $form;
}
public function validateForm(array & $form, FormStateInterface $form_state)
{
//演示起见,无需验证
}
public function submitForm(array & $form, FormStateInterface $form_state)
{
$form_state->cleanValues();
print_r($form_state->getValues());
die;
}
}
第二步:
在\Drupal\yunke_help\Controller\Test::test方法中放入以下代码:
return \Drupal::formBuilder()->getForm("\Drupal\yunke_help\Form\YunkeForm");
这用于执行表单
第三步:
在\Drupal\yunke_help\Controller\Test::test_1方法中放入以下代码:
public function test_1(Request $request)
{
$input=$request->query->get('q');
$data=[];
for($i=0;$i<5;$i++){
//$data[]=$i."($input)";
$data[]=['label'=>$i."($input)",'value'=>$i];
}
$response = new \Symfony\Component\HttpFoundation\JsonResponse($data);
return $response;
}
然后访问“http://www.你的域名.com/yunke-help/test”,或点击模块主面板的“测试”按钮,即可体验drupal的自动完成功能
Drupal自动完成概述:
在讲述原理前,需要明白drupal的自动完成是通过AJAX从后端获取数据的,默认仅支持input表单元素,允许在一个input控件中输入多个条目,可针对每个条目单独进行自动完成功能,条目间用英文逗号分隔,发送到后端的数据并不是整个input控件的值,而是将其以英文逗号“,”分隔后的最后一段值(英文双引号“””中的逗号不算,这作为值而不是分隔符),数据通过GET查询参数“q”传递到后端。
后端返回json响应,通常是以下类对象:
\Symfony\Component\HttpFoundation\JsonResponse
json格式按前文所述的原生jquery UI数组格式,如果返回的数组不是对象构成,换句话说是字符串构成,那么已经输入的条目会从建议列表中删除,仅显示有效值,反之如果是对象构成则不会有删除动作。
页面中可以有多个自动完成元素,每个元素都需要有ID属性,为了提高性能和节约资源,每个自动完成元素的每次自动完成数据请求会被前端js缓存,以便相同请求不会再次发送,且设置了首字符黑名单,当条目首字符位于黑名单中时,不会触发自动完成功能
原理:
自动完成前端js首先运行的是:
Drupal.behaviors.autocomplete.attach(context)
该方法进行自动完成初始化,使用全局变量Drupal.autocomplete.options作为选项,各选项解释如下:
focus:设置聚焦事件处理回调,该事件是在菜单条目获得焦点后派发,默认动作是用条目的值替换表单的值,返回false表示取消该事件,即在菜单出现后,通过键盘方向键进行选择的过程中不要自动输入值,需要等待按压回车后才输入
search:设置搜索事件处理回调,搜索事件派发时机是在“最小长度”和“延迟时间”判断满足后,搜索执行前,如果返回false将取消本次自动完成功能执行(没有请求发起,没有建议给出),drupal通过该事件实现首字符黑名单功能:首字符在黑名单中或字符数小于最小限制将不执行搜索,不显示备选列表
select:设置选择事件处理回调,选择事件派发时机是在自动完成菜单中有条目被选中之后(回车键被按压后),默认动作是用选择的条目值替换表单值,如果返回false将阻止默认动作,但不会阻止菜单关闭,drupal利用该事件实现多条目输入功能,以允许英文逗号分隔多个值,使得之前的自动完成结果值得到保留
renderItem:在原生的jquery-ui中,并不支持该选项,这里被用来传递额外的数据,保存一个函数,用于通过jquery Ui的“_renderItem”API进行原生扩展,使得在构建菜单条目时,lable可以是任意html内容,包含标签,且lable被<a>”标签包裹
minLength:触发自动完成功能所需输入字符串最小长度,默认设置为1,以字符串的length属性来比较,意味着中文汉字并非按字节计算,通常一个字就是一个长度(但并非全部,如汉字“𠮷”长度为2)
firstCharacterBlacklist:首字符黑名单,单个条目如果以黑名单中的任意字符开始,将不会触发自动完成功能,默认通过第一个自动完成元素的“data-autocomplete-first-character-blacklist”属性设置,所有自动完成元素共享相同首字母黑名单,如果需要不同,可以在获得焦点时通过以下全局对象:
Drupal.autocomplete.options.firstCharacterBlacklist
设置,然后在失去焦点时恢复之前的共享值,注意不支持首词组,仅首字符
部分函数解释:
showSuggestions(suggestions)
从菜单中删除已经输入过的值,仅在后端返回索引数组时起作用
Drupal.autocomplete.splitValues(value)
输入一个字符串值,返回由字符串中的逗号“,”分隔后的一个数组,注意是英文逗号,且英文的双引号“””中的逗号不算
Drupal.autocomplete.extractLastTerm(value)
返回由Drupal.autocomplete.splitValues(value)分隔后的数组的最后一个元素
在前端:
要应用自动完成的元素仅支持input,且还需要具备以下条件:
类属性form-autocomplete:必须值,用于表明元素需要自动完成
数据属性data-autocomplete-path:必须值,用于指定自动完成的ajax请求地址
数据属性data-autocomplete-first-character-blacklist:可选的,指定首字符黑名单
id属性:必须值,用做缓存标识符
在后端:
自动完成是用于表单的,因此在表单API中实现,通过“#process”回调添加相关条件,详见Textfield是如何指定自动完成处理的:
\Drupal\Core\Render\Element\Textfield
实际处理方法为:
\Drupal\Core\Render\Element\FormElement::processAutocomplete
该方法自动添加类属性“.form-autocomplete”,加载自动完成库,进行权限检查,前文示例中的表单元素即通过该方法处理
以上是drupal自动完成的一般性、通用的基础实现,称为自动完成API,在开发时直接使用即可,但如果是针对实体进行自动完成,开发者还需要做许多工作,因此drupal在本篇所讲API基础之上为实体提供了专门的自动完成,开箱即用,将在下一节中讲解
补充:
1、自动完成以编程方式改变元素的值,因此元素上的“change”事件可能不被触发,稳妥的办法是采用自动完成功能派发的change事件:
$( ".selector" ).autocomplete({
change: function( event, ui ) {}
});
2、drupal自动完成条目间的分隔符默认为英文逗号,如需其他分隔符可覆写以下方法:
Drupal.autocomplete.splitValues(value)
3、自动完成时,服务器返回值如果是索引数组,不应该有重复值
4、被初始化的自动完成元素有一个数据属性“ui-autocomplete”(获取:data('ui-autocomplete')),储存着该元素的自动完成对象,这涉及更加底层的原生jquery ui实现,这里仅提示,不做深入讲解
反馈互动