122. 对话框dialog

       对话框dialog是页面中悬浮的一个用来显示内容的框,有标题栏、内容区域、控制按钮等,可以被移动,改变尺寸,当内容太长会出现滚动条,键盘导航也只能在框里面进行,看起来像覆盖在父窗体上的子窗体,通常是用来显示一个单独的源的内容,可以在不离开父窗体的情况下做一些互动操作或提供信息等。

modal称为模态对话框,是dialog的一种,通常对话框dialog在打开时,其底下的元素允许交互,而modal将禁止交互,底下的元素全部为禁用状态,直到对话框关闭为止。

基础:

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>点击打开Dialog</h2>
<p>
    <button id="dialog-link" class="ui-button ui-corner-all ui-widget">
        <span class="ui-icon ui-icon-newwin"></span>Open Dialog
    </button>
</p>

<div id="dialog" title="这里是Dialog对话框标题">
    <p>对话框内容:drupal并不将UI下载到一个js文件中,而是按组件分别下载,然后形成不同的核心库</p>
</div>

<br><br><br><br><br>
<a href="http://www.baidu.com" target="_blank">点击打开百度</a>

<script src="jqueryui/external/jquery/jquery.js"></script>
<script src="jqueryui/jquery-ui.js"></script>
<script>
    //初始化对话框
    $("#dialog").dialog({
        autoOpen: false,
        modal: true, //为true时将使对话框仅有模态行为,底层元素被禁用
        width: 400,
        buttons: [
            {
                text: "确定",
                click: function () {
                    $(this).dialog("close");
                }
            },
            {
                text: "取消",
                click: function () {
                    $(this).dialog("close");
                }
            }
        ]
    });

    //初始化打开对话框事件
    $("#dialog-link").click(function (event) {
        $("#dialog").dialog("open");
        event.preventDefault();
    });

</script>
</body>
</html>

示例说明:

该例引用了三个关键文件: UI样式、jquery库、jquery-uijquery库必须在jquery-ui库之前引用,后者扩展了前者,如你所见,简单的使用UI库提供的方法即可实现对话框功能。
关于jquery-ui库的对话框用法不属于本系列范围,如何使用请详见官方文档:
  
https://api.jqueryui.com/dialog/
在继续阅读前强烈建议你花几十分钟学习该API,这里仅列出常被drupal使用的选项。

 

对话框选项dialogOptions

jquery-ui原生选项及含义如下:

appendTo:选择器字符串,指示对话框应该追加到哪个元素上,默认为body

autoOpen:布尔值,指示对话框初始化后是否自动打开,默认为true,否则将保持隐藏,等待调用打开方法

buttons:设置对话框中的按钮,对象或数组值,如果是对象,那么属性名为按钮文本,属性值为点击事件的回调,如果是数组,那么个元素必须是一个对象,其属性名text指定按钮文本,click指定点击回调,icon指定按钮图标类

classes:指定额外的类名到对话框各控件元素

closeOnEscape:布尔值,默认为true,在对话框有焦点时,指示能否通过按压ESC关闭

closeText:关闭按钮的文本字符串,默认为“close”,默认情况下该文本是隐藏的,当鼠标在其上时才显示

dialogClass:为整体对话框添加一个类名,已弃用,用classes项的ui-dialog属性代替

draggable:布尔值,指示是否可以通过标题栏拖动对话框

height:对话框的高度,像素为单位的整数,或“auto”,表示依据内容调整,这是默认值

hide:对话框关闭动画,为true时表示立即关闭(无动画),true时将慢慢淡出,也可以指定动画名等

maxHeight:调整尺寸时的对话框最大高度,像素为单位

maxWidth:调整尺寸时的最大宽度,像素为单位

minHeight:调整尺寸时的对话框最小高度,像素为单位,默认150

minWidth:调整尺寸时的最小宽度,像素为单位,默认150

modal:布尔值,是否将对话框指定为模态框(禁用底层元素)

position:对话框打开时的位置,默认为{ my: "center", at: "center", of: window }

resizable:布尔值,默认true,指示是否可以拖拉调整对话框尺寸

show:指示打开对话框的动画,false为无动画,立即打开,true时将慢慢淡出,也可以指定动画名等

title:对话框标题,字符串值,如为null,将用对话框原元素的title属性代替

width:对话框宽度,默认为300,像素为单位

 

 

Drupal前端dialog库:

核心库:core/drupal.dialog

提供对话框dialog操作功能

依赖以下库:

    - core/jquery
    - core/drupal
    - core/drupalSettings(提供对话框默认选项)
    - core/drupal.debounce(防抖库限制大小、拖动调整时的回调执行频率)
    - core/drupal.displace(边距库,在自动位置时确定对话框位置)
    - core/jquery.ui.dialog(原生jquery.ui.dialog库)

文件:

core/misc/dialog/dialog.es6.js
core/misc/dialog/dialog.position.es6.js
core/misc/dialog/dialog.jquery-ui.es6.js

在加载该库时,你依然可以使用原生jquery-ui提供的方法去打开、操作、关闭一个对话框,但是你不应该这么做,该库的目的是将原生jquery-ui库集成到drupal系统中,提供更高层次的封装,这让对话框处理和drupal其他组件产生关联,比如和位置组件的关联可以很好的处理对话框的默认位置、如果其他组件在对话框上有事件绑定可以获得通知,执行解绑、重绑等,如果单独使用原生jquery-ui库将不会和其他组件互动,是一个孤立的操作,因此在drupal中打开一个对话框应该使用以下方法:

Drupal.dialog(element, options)

参数element用于指示被当做对话框的元素,可以是一个jquery选择器,也可以是元素的dom对象,参数 options是对话框选项,在全局变量drupalSettings.dialog中储存着默认选项

该方法返回一个dialog对象,须进一步调用该对象上的以下三个方法才能完成对话框操作:

show():打开对话框

showModal():打开模态对话框

close(value):关闭对话框,参数将作为返回值保存在dialog.returnValue

注意:dialog对象的open属性仅指示在dialog对象上执行的打开关闭操作,并不能真正表明对话框的真实状态,后者仍需要jquery-ui原生提供的方法去判断

页面其他组件可以监听相关事件感知对话框,执行相关操作时的事件触发如下:

$(window).trigger('dialog:beforecreate', [dialog, $element, settings]);
$(window).trigger('dialog:aftercreate', [dialog, $element, settings]);
$(window).trigger('dialog:beforeclose', [dialog, $element]);
$(window).trigger('dialog:afterclose', [dialog, $element]);

还可以通过以下方法监听对话框的变化:

resize.dialogResize”、“dialogContentResize

 

前端dialog.ajax

核心库:core/drupal.dialog.ajax

文件:/core/misc/dialog/dialog.ajax.es6.js

依赖以下库:

    - core/jquery
    - core/drupal
    - core/drupalSettings
    - core/drupal.ajax
    - core/drupal.dialog

该库用于在AJAX时通过AJAX命令打开、关闭、修改对话框,当服务器返回这类命令时必须附带执行加载该库的动作,确保该库被加载,示例详见:

\Drupal\Core\Render\MainContent\DialogRenderer::renderResponse

命令需要指定一个对话框元素,当该元素尚不存在时会新建一个,并通过AJAX插入命令以html方法将内容放入,内容中以下jquery选择器对应的按钮会被移动到对话框的按钮栏:

'.form-actions input[type=submit], .form-actions a.button'

也就是表单提交按钮和链接标签按钮

 

AJAX打开Dialog命令

后端php类:\Drupal\Core\Ajax\OpenDialogCommand

构造函数按序接收以下参数:

$selector:必须,一个id选择器以指示前端对话框元素,以#做前缀,在前端如果不存在将新建该ID元素

$title:必须,对话框标题,字符串值,不能传递渲染数组

$content:必须,对话框内容,既可为字符串值的html内容,也可为渲染数组,如是渲染数组可在数组中加载前文所述的dialog.ajax

$dialog_options:可选,对话框选项数组,默认值保存在前端全局变量drupalSettings.dialog

$settings:设置数组

前端方法(位于dialog.ajax库中):

Drupal.AjaxCommands.prototype.openDialog(ajax, response, status);

 

AJAX关闭Dialog命令:

后端php类:\Drupal\Core\Ajax\CloseDialogCommand

构造函数按序接收以下参数:

$selector:一个id选择器以指示前端对话框元素

$persist:布尔值,在对话框关闭后,是否让其继续保留在DOM中,不保留将调用jqueryremove()方法

前端方法(位于dialog.ajax库中):

Drupal.AjaxCommands.prototype.closeDialog(ajax, response, status);

 

AJAX设置Dialog选项命令:

后端php类:\Drupal\Core\Ajax\SetDialogOptionCommand

构造函数按序接收以下参数:

$selector:一个id选择器以指示前端对话框元素

$option_name:选项名,可使用任意原生jquery-ui提供的选项

$option_value:选项值

前端方法(位于dialog.ajax库中):

Drupal.AjaxCommands.prototype.setDialogOption(ajax, response, status);

 

其他对话框相关命令:

打开Modal命令:

类:\Drupal\Core\Ajax\OpenModalDialogCommand

模态框是对话框的一种,该命令很简单,继承自打开Dialog命令,仅设置选项参数modal为真,该命令完全可用对话框相关命令代替

关闭Modal命令:

类:\Drupal\Core\Ajax\CloseModalDialogCommand

设置对话框标题命令:

类:\Drupal\Core\Ajax\SetDialogTitleCommand

完全可用选项命令代替

 

主内容渲染器:

dialog主内容渲染器

容器idmain_content_renderer.dialog

类:Drupal\Core\Render\MainContent\DialogRenderer

modal主内容渲染器:

容器idmain_content_renderer.modal

类:Drupal\Core\Render\MainContent\ModalRenderer

继承自dialog主内容渲染器

 

这两个主内容渲染器都很简单,着重注意加载dialog.ajax库的方法即可,加载的库会在AJAX响应附属处理服务(idajax_response.attachments_processor)中以追加命令添加到页面中:

加载dialog<script src="/core/misc/dialog/dialog.ajax.js?v=8.7.1"></script>

详见本系列AJAX后端流程

 

应用示例一:基础

为了完整演示对话框的用法,这里提供一个示例,需要一个运行示例的模块,这里以“yunke_help”模块为例:

第一步:

在控制器:\Drupal\yunke_help\Controller\Test::test中放入以下示例代码:


        $elements['#title'] = '触发AJAX并打开dialog显示内容完整示例';
        $url = \Drupal\Core\Url::fromUri('internal:/yunke-help/test-1');//AJAX目的地
        $elements['span'] = [//这里以span元素为列,但不限于该元素
            '#type'       => 'html_tag',
            '#tag'        => 'span',
            '#value'      => '点击这里将在dialog中呈现ajax内容',
            '#attributes' => ['id' => 'yunkeID'],

        ];
        $elements['link']['#attached']['drupalSettings']['ajax']['yunkeID'] = [ //以触发元素ID做键名
            'event'    => 'click',
            'url'      => $url->toString(),
            'progress' => [
                'type'    => 'throbber',
                'message' => '正在进行ajax...',
            ],
        ];
        $elements['link']['#attached']['drupalSettings']['ajaxTrustedUrl'][$url->toString()] = TRUE;
        $elements['link']['#attached']['library'][] = 'core/jquery.form';
        $elements['link']['#attached']['library'][] = 'core/drupal.ajax';
        return $elements;

第二步:

在控制器:\Drupal\yunke_help\Controller\Test::test_1中放入以下代码:


        $main_content = array(
            '#markup' => '<div>我是ajax获取的内容' . time() . '</div>',
        );
        //$main_content是要渲染的任意渲染数组
        $main_content['#attached']['library'][] = 'core/drupal.dialog.ajax';
        //必须加载该库
        $response = new \Drupal\Core\Ajax\AjaxResponse();
        $dialogSelector = "#yunke_dialog"; //dialog元素的选择器
        $title = '对话框标题';
        $options = ['minWidth' => 500]; //选项
        $OpenDialogCommand = new \Drupal\Core\Ajax\OpenDialogCommand($dialogSelector, $title, $main_content, $options);
        $response->addCommand($OpenDialogCommand);
        return $response;

说明:

然后访问: http://www.你的域名.com/yunke-help/test

或点击“yunke_help”模块主页的测试按钮

依据页面提示点击查看效果

该例在页面中设置了一个AJAX事件,通过返回命令的方式来打开对话框,在第二步中直接返回了AJAX响应对象,因此不会执行主内容渲染器,但通常可以通过添加GET参数“_wrapper_format”的方式指定对话框主内容渲染器,从而第二步仅返回渲染数组即可,但与之相比,本列更加灵活可控。

 

应用示例二:采用对话框提交表单

仍以“yunke_help”模块为例:

第一步:建立示例访问界面

在控制器:\Drupal\yunke_help\Controller\Test::test中放入以下示例代码:


        $elements['#title'] = '采用dialog进行表单提交示例';
        $url = \Drupal\Core\Url::fromUri('internal:/yunke-help/test-1');//AJAX目的地
        $elements['span'] = [//这里以span元素为列,但不限于该元素
            '#type'       => 'html_tag',
            '#tag'        => 'span',
            '#value'      => '点击打开dialog',
            '#attributes' => ['id' => 'yunkeID'],

        ];
        $elements['link']['#attached']['drupalSettings']['ajax']['yunkeID'] = [ //以触发元素ID做键名
            'event'    => 'click',
            'url'      => $url->toString(),
            'progress' => [
                'type'    => 'throbber',
                'message' => '正在进行ajax...',
            ],
        ];
        $elements['link']['#attached']['drupalSettings']['ajaxTrustedUrl'][$url->toString()] = TRUE;
        $elements['link']['#attached']['library'][] = 'core/jquery.form';
        $elements['link']['#attached']['library'][] = 'core/drupal.ajax';
        return $elements;

第二步:打开对话框并加载表单

在控制器:\Drupal\yunke_help\Controller\Test::test_1中放入以下代码:


        $main_content = \Drupal::formBuilder()->getForm("\Drupal\yunke_help\Form\YunkeForm");
        //$main_content是要渲染的任意渲染数组
        $main_content['#attached']['library'][] = 'core/drupal.dialog.ajax';
        //必须加载该库
        $response = new \Drupal\Core\Ajax\AjaxResponse();
        $dialogSelector = "#yunke_dialog"; //dialog元素的选择器,在前端尚不存在
        $title = isset($main_content['#title']) ? $main_content['#title'] : '提交表单';
        $options = []; //选项
        $OpenDialogCommand = new \Drupal\Core\Ajax\OpenDialogCommand($dialogSelector, $title, $main_content, $options);
        $response->addCommand($OpenDialogCommand);
        return $response;

第三步:建立表单

yunke_help/src/Form/YunkeForm.php文件中放入以下代码:


<?php
/**
 * 演示表单AJAX操作
 */

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['#title'] = '对话框表单提交';
        $form['input'] = [
            '#type'  => 'textfield',
            '#title' => '任意输入字符:',
            '#size'  => '60',
        ];

        $form['actions']['#type'] = 'actions';
        $form['actions']['submit'] = array(
            '#type'        => 'submit',
            '#value'       => $this->t('Submit'),
            '#button_type' => 'primary',
            '#ajax'        => [
                'callback'        => '::yunke',
                'event'           => 'click',
                'disable-refocus' => true,
                'progress'        => [
                    'type'    => 'throbber',
                    'message' => '表单正在提交中...',
                ],
            ],

        );
        $form['result'] = array( //用于显示提交错误
            '#type'       => 'html_tag',
            '#tag'        => 'div',
            '#attributes' => ['id' => 'result',],
        );
        return $form;
    }

    public function yunke(array &$form, FormStateInterface $form_state)
    {
        $response = new \Drupal\Core\Ajax\AjaxResponse();
        if ($form_state->get('submitOK')) {//处理提交成功的情况
            $main_content = ['#markup' => '你的输入为:' . $form_state->getValue('input')];
            $main_content['#attached']['library'][] = 'core/drupal.dialog.ajax';
            //必须加载该库,不用担心重复,系统会自动处理
            $dialogSelector = "#yunke_dialog"; //dialog元素的选择器
            $title = '提交成功';
            $options = []; //选项
            $OpenDialogCommand = new \Drupal\Core\Ajax\OpenDialogCommand($dialogSelector, $title, $main_content, $options);
            $response->addCommand($OpenDialogCommand);
            return $response;
        }
        $errors = $form_state->getErrors();
        $message = ['提交失败,请检查错误:'];
        foreach ($errors as $error) {
            $message[] = $error;
        }

        //错误会沉积在消息系统中,体验不好故删除,但不要删除非表单错误的消息
        $errorMessages = \Drupal::messenger()->deleteByType('error');
        foreach ($errors as $key => $error) {
            $errors[$key] = (string)$error;//可能是翻译对象
        }
        foreach ($errorMessages as $key => $error) {
            if (!in_array((string)$error, $errors)) {
                \Drupal::messenger()->addMessage($error, 'error');
            }
        }
        $html = ['#markup' => implode('<br>', $message)];
        $command = new \Drupal\Core\Ajax\HtmlCommand("#result", $html);
        $response->addCommand($command);
        $response->addCommand(new \Drupal\Core\Ajax\CssCommand("#result", ['color' => 'red']));
        return $response;
    }

    public function validateForm(array & $form, FormStateInterface $form_state)
    {
        if (empty($form_state->getValue('input'))) {
            $form_state->setErrorByName('input', '输入不能为空');
        }
    }

    public function submitForm(array & $form, FormStateInterface $form_state)
    {
        $form_state->cleanValues();
        //some code
        //在这里执行正常提交操作
        $form_state->set('submitOK', TRUE);
        //标识提交已执行成功
    }
}

说明:

该例是对话框、AJAX、表单的综合运用,如不明白请阅读本系列AJAX后端原理等主题

 

 

添加新评论

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。
请输入以上问题的答案,换一个问题请刷新,不区分大小写