65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 AngularJS 和 ui-bootstrap 的可移动弹出模态框

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2022年2月15日

MIT

9分钟阅读

viewsIcon

10621

downloadIcon

100

教程,讨论使用 AngularJS 和 ui-bootstrap 设计可移动的弹出模态框。

引言

我想用一个非常有用且简单的设计来开启2022年的系列教程。想象这样一个场景:你需要在屏幕上创建一个弹出式模态框。这非常简单。然后客户回来要求让这个弹出框可以移动,允许用户将弹出式模态框拖动到屏幕上的任何地方。使用 AngularJS 和 ui-bootstrap(也称为 Angular-UI),显示弹出式模态框很容易,但要使其可移动则需要一些额外的组件和一些组装工作。在本教程中,我将讨论这些组件以及如何将它们组装起来,以在网页上制作一个可移动的弹出式模态框。而且我可以告诉你,这一点也不难。

架构概述

通常,本节将描述整体架构。但这次,我将解释我用来实现这个设计所使用的 JavaScript 库。显而易见的是 JQuery、Bootstrap(是的,它有 js 文件)和 AngularJS。我还用到了 Angular-UI(也称为 ui-bootstrap)和 JQuery UI。这里最不明显的是 JQuery UI 的使用。这是一个鲜为人知的 JQuery 扩展库,它提供了许多可以轻松添加到 Web 应用程序中的强大功能。这是本教程的关键组件。我需要这个库的可拖动功能。它使我的实现比原本需要的简单得多。

接下来,我想描述一下如何实现这个想法。Angular-UI(或 ui-bootstrap)是与 AngularJS 配合使用的最佳组件,能够发挥 Bootstrap 的最佳功能。它提供了显示 Bootstrap 模态弹出框的最简单方法。这个弹出框是不可移动的。“ui-bootstrap”确实提供了使之成为可能的配置选项,但并未完全实现。我猜 ui-bootstrap 的开发者知道这是可能的,但决定保持原样,这样如果有人想让弹出框可移动,他们可以毫不费力地实现这一飞跃。

现在,我可以描述这个应用程序的架构了。其实并不复杂。这个 Web 应用程序使用 Spring Boot 向 Web 浏览器提供一个单页面。该网页上运行着一个 AngularJS 应用程序。页面上只有一个按钮。当用户点击该按钮时,可移动的模态弹出框就会显示出来。用户可以将弹出框拖动到屏幕上几乎任何地方。

为了实现这一点,我需要在弹出框显示时去掉背景遮罩,另一件我必须做的事情是让弹出框可以移动。移除背景遮罩非常简单。让它可移动则需要一些编码。这会很有趣,让我们开始深入研究吧。

 

AngularJS 应用程序概述

我使用 ES6 脚本来编写这个应用程序。主入口文件名为 app.js,它定义了模块依赖的设置。为了让示例应用程序正常工作,我需要一个应用程序控制器、一个弹出框控制器,以及一个可以使弹出框移动的指令(directive)。我还定义了一个服务来处理弹出框的配置和实际显示。我是这样设置它们的:

import { appRouting } from '/assets/app/js/app-routing.js';
import { AppController } from '/assets/app/js/AppController.js';
import { MoveablePopupController, MoveablePopupService } 
         from '/assets/app/js/MoveablePopupService.js';
import { uiMoveableDirective } from '/assets/app/js/UIMoveableDirective.js';

let app = angular.module('startup', ["ngRoute", "ui.bootstrap" ]);
app.config(appRouting);
app.factory("MoveablePopupService", ["$uibModal", MoveablePopupService]);
app.directive("uiMoveable", [ uiMoveableDirective ]);
app.controller("MoveablePopupController", [ "$scope",  MoveablePopupController ]);
app.controller("AppController", 
              [ "$rootScope", "$scope", "MoveablePopupService", AppController ]);

import 语句引入了 AngularJS 应用程序设置所需的所有类和函数。然后我声明了一个名为 “startup” 的模块。这个模块将由 ngApp 使用。对于这个模块,我将设置所有的依赖项,像这样:

app.config(appRouting);
app.factory("MoveablePopupService", ["$uibModal", MoveablePopupService]);
app.directive("uiMoveable", [ uiMoveableDirective ]);
app.controller("MoveablePopupController", [ "$scope",  MoveablePopupController ]);
app.controller("AppController", 
              [ "$rootScope", "$scope", "MoveablePopupService", AppController ]);

第一行是应用程序的 URL 路由设置。第二行定义了用于显示模态弹出框的服务。第三行是我为了使弹出框可移动而必须创建的指令。这是本教程至关重要的一部分。第四行是模态弹出框的控制器。最后一行是应用程序控制器。

应用程序路由配置

应用程序的 URL 路由由一个函数提供,它所做的只是将显示重定向到应用程序的索引页面,别无他用。

export function appRouting($routeProvider) {
  $routeProvider.when("/", {
    templateUrl : "/assets/app/pages/index.html",
    controller: "AppController",
    controllerAs: "vm"
  });
}

这个函数没什么特别的,除了 AngularJS 会知道要显示的页面,以及与该页面关联的控制器。

主页面控制器

接下来,我想展示主页面的控制器。同样,它非常简单。

export class AppController {
   constructor($rootScope, $scope, moveablePopupService) {
      this._rootScope = $rootScope;
      this._scope = $scope;
      this._moveablePopupService = moveablePopupService
   }

   handleOpenPopup() {
      this._moveablePopupService
          .openMovablePopup()
          .then(function () {
             console.log("Popup closed by user.");
          }, function () {
             console.log("Popup cancelled.");
          });
   }
}

该控制器被定义为一个类,其构造函数用于通过注入传递外部依赖。我在这里唯一需要的依赖是显示弹出框的服务。它被称为 moveablePopupService。我将在下一节中展示它。

这个类中只有一个函数,名为 handleOpenPopup。这个方法是主页面的事件处理程序。页面上有一个按钮,按钮的点击事件由这个方法处理。它调用服务来打开弹出框。then() 方法什么也不做,只是在控制台上显示一行信息,表明用户通过弹出框上的关闭按钮或键盘上的 Escape 键关闭了弹出框。

打开弹出框的服务

服务函数和弹出框控制器都打包在一个 js 文件中,名为“MoveablePopupService.js”。服务函数本身非常简单,但为了使弹出框可移动,我在打开它时必须对弹出框的配置做一些处理。这些部分已高亮显示。

export function MoveablePopupService ($uibModal) {
   let retVal = {};

   retVal.openMovablePopup = function () {
      let moveablePopup = $uibModal.open({
         templateUrl: "/assets/app/pages/moveablePopup.html",
         controller: "MoveablePopupController",
         controllerAs: "popup",
         backdrop: false,
         size: "md",
         resolve: { }
      });

      return moveablePopup.result;
   };

   return retVal;
}

该函数中只有一个方法,名为 openMovablePopup。它使用 Angular-UI 的 $uibModal 来打开弹出框。$uibModal 会使用某个模板添加模态弹出框,并在显示前设置配置。高亮显示的行是为了移除灰色的遮罩背景。这样做的想法是,当你使弹出框可移动时,你希望能够看到弹出框下面的内容。如果保留灰色遮罩,可能会成为一个可用性问题。幸运的是,有一个配置属性 backdrop,我们将其设置为布尔值 false,这样灰色遮罩(即 backdrop)就不会显示了。

弹出框控制器

接下来,我将向您展示弹出对话框使用的控制器。它也非常简单。

export class MoveablePopupController {
   constructor($scope) {
      this._scope = $scope;
   }

   handleClickClose() {
      this._scope.$close();
   }
}

这是一个简单的类,其构造函数几乎什么都不做。它还有一个成员方法用于处理弹出框的关闭。Angular-UI 的弹出模态框有自己的独立作用域,其中有两个关闭弹出框的方法,一个叫 close(),另一个叫 dismiss()。我使用 close() 方法来明确地关闭弹出框。它还可以返回一个结果对象,表示在弹出框中完成的任何活动都已成功完成。

在这个示例应用程序中,弹出框没有有意义的操作。我们所做的只是显示弹出框,以便可以四处移动它。并且可以通过按钮或点击“Escape”键来关闭它。

使弹出模态框可移动

这是本教程最重要的部分,我将在这里解释如何使弹出框可移动。我必须为我的弹出窗口模板创建一个指令(directive)。然后我将在我的弹出模态框模板上使用这个指令。这个指令将使弹出框变得可移动。

它的工作原理是,该指令找到弹出模态框的父级 div。然后它会向这个 div 添加可拖动属性。“draggable” 属性是 JQueryUI 库的一部分。一旦通过 JQueryUI 提供的 API 添加了该属性,弹出框就立即可以移动了。

这是我对该指令的定义:

export function uiMoveableDirective() {
   return {
      restrict: "EA",
      link: function ($scope, element, attrs) {
         var popupDiv = element.parent();
         if (popupDiv) {
            popupDiv.draggable();
         }
      }
   }
}

我高亮了重要的部分。第一个是找到父级弹出框 div 的那一行。然后下一行将可拖动属性添加到弹出框中。以下是我如何将此指令应用于我的弹出模态框模板:

<div ui-moveable>
   <div class="modal-header">
      <h4>Moveable Popup</h4>
   </div>
   <div class="modal-body">
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
      Nam pulvinar enim eu nibh aliquam, ac laoreet sem lobortis.
      Etiam vitae sodales ipsum. Donec tempus turpis eget nibh euismod,
      a mollis ipsum rhoncus. Aliquam nulla augue, dapibus et nisl vitae,
      imperdiet bibendum tellus. Mauris vitae ante sodales, eleifend arcu quis,
      interdum magna. Sed vestibulum eu sapien id eleifend.
      Maecenas quis imperdiet lacus. Mauris vehicula turpis nisi,
      nec dapibus velit pharetra vitae. Orci varius natoque penatibus et
      magnis dis parturient montes, nascetur ridiculus mus.</p>
      <div class="row">
         <div class="col-xs-12 text-center">
            <button class="btn btn-danger" ng-click="popup.handleClickClose()">Close</button>
         </div>
      </div>
   </div>
</div>

该指令用在最外层的 div 中。它包裹了内部内容,这样弹出框的框架就会被修改以具有可拖动属性。然后整个弹出框就变得可以移动了。

为了使应用程序更具可用性,还需要再做一个操作。当模态弹出框显示时,背景将变得不可滚动。这造成了一个两难的境地:即使你可以拖动弹出框,你也无法看到背景上的太多内容。我需要覆盖这个限制,以便用户可以上下滚动背景内容,并且在拖动弹出框时,用户仍然可以看到下面的内容。

我所要做的就是在页面的 CSS 文件(见 index.css)中添加一个 CSS 样式,像这样:

.modal-open {
	overflow: auto;
}

这就是制作一个可用的、可拖动的弹出模态框所需要的一切。本教程到此结束。

如何测试

下载教程示例代码后,解压缩文件,然后将所有 *.sj 文件重命名为 *.js 文件。

接下来,在示例代码的基础目录(你可以找到 pom.xml 的地方),运行以下命令:

mvn clean install 

此命令将构建应用程序。下一个命令将启动 Web 应用程序:

java -jar target/hanbo-angular-movable-popup-1.0.1.jar

假设构建成功且应用程序正常启动,您可以使用浏览器通过输入以下 URL 来访问应用程序:

https://:8080/

一旦网页正确显示,顶部中央位置会有一个按钮。点击它,你就会看到弹出框。然后用户可以点击弹出框的任何位置并拖动它。

这是应用程序启动后的截图:

这是应用程序弹出框被移动到页面右下角的截图:

摘要

与我过去的教程相比,这是一个简单的教程。而且我在本教程中描述的是一个非常有效的技巧。当我第一次听到这个需求时,我很困惑它是否能实现。最终,这是我想出的解决方案。这个解决方案有一个问题。用户可以拖动弹出模态框,但无法使用鼠标在弹出模态框上高亮选中文本。

在本教程中,我解释了如何创建指令来使 angular-ui (ui-bootstrap) 弹出模态框在页面上可拖动和移动。我还解释了为了使背景页面可滚动,从而使整个设置更具可用性,需要进行的覆盖。JQueryUI 的可拖动功能还有一个问题。如果用户将弹出框拖得离页面底部太远,弹出框可能会消失。这可能是 JQueryUI 库的一个问题。

我今年开局有点慢,刚刚完成了这篇教程。它很简单,但我认为会非常有用。我希望你也觉得这篇教程有用。祝你好运。

历史

  • 2022年2月13日 - 初稿
© . All rights reserved.