UI-BootStrap Modal 对话框教程
本文将讨论 ui-bootstrap 与 AngularJS 1.6.x 的集成,我将展示如何弹出模态对话框,如何将数据传递到模态对话框,以及如何将数据传回给调用者。
引言
前段时间,我为 CodeProject 写了一篇文章,内容是如何在 Angular JS 应用程序中使用 Bootstrap 打开模态对话框。这是该文章的链接。正如我在其中解释的那样,那篇文章的动机是我当时无法让 ui-bootstrap 工作。完成那篇文章后,我知道我提出的解决方案不是最好的解决方案,我决心将 ui-bootstrap 集成到我的 Angular JS 应用程序中。我知道如果我再深入研究一下,答案就会揭晓。经过一些实验,我搞清楚了。在本文中,我将讨论所有这些是如何工作的。
首先,我将解释如何正确地将 ui-bootstrap 添加到 Angular JS 应用程序中。这严格限于 Angular JS 1.6.x,而不适用于 Angular 2 到 5 版本。接下来,我将展示如何通过 ui-bootstrap 打开模态对话框。然后,我将解释如何将数据从调用者传递到模态对话框。最后,我将展示如何将结果数据传回给调用者。
整体架构
本文附带了一个示例应用程序。页面上有一个按钮,会触发模态对话框打开。对话框打开后,对话框上的输入字段将显示从调用者传递到对话框的值。当用户点击对话框上的“搜索”时,它将关闭,并将结果(一个包含一些模拟数据的对象)传回给调用者。调用者将在表格中显示结果。
有两个控制器,一个是主应用程序的控制器;另一个是对话框的控制器。我将对话框的控制器和标记分离开来,以便它可以重复使用。这是我的教程与您在谷歌搜索结果前几页中找到的教程之间的主要区别。那些搜索结果对我的设计帮助不大。所以,提供现有解决方案的一些替代方案是很好的。
当点击主应用程序页面上的按钮时,主应用程序控制器会调用 ui-bootstrap 的 $uibModal
来弹出模态对话框。这是可以将输入数据传递到模态对话框控制器的地方。当用户关闭模态对话框时,会发生以下两种操作之一。一种称为 $close
,另一种称为 $dismiss
。在 $close
的情况下,在对话框上执行的操作成功,并且可以返回任何结果数据。在 $dismiss
的情况下,用户可能取消了操作,并且不应返回模态对话框中的任何结果数据。所有这些都将在代码演练中解释。首先,让我向您展示如何将 ui-bootstrap 添加到 Angular JS 应用程序中。
将 ui-bootstrap 添加到 Angular JS 应用程序中
要将 ui-bootstrap 集成到 Angular JS 应用程序中,需要两个 JavaScript 文件
- ui-bootstrap-2.5.0.min.js
- ui-bootstrap-tpls-2.5.0.min.js
我从 ui-bootstrap 官方网站列出的 github 存储库获取了这两个文件:https://github.com/angular-ui/bootstrap/tree/gh-pages。我选择了最新版本。在撰写本文时,最新版本是 2.5.0。在我的“index.html”中,您可以看到以下内容,它将这两个文件合并到 Angular JS 应用程序中
<script type="text/javascript" src="./assets/ui-bootstrap/js/ui-bootstrap-2.5.0.min.js"></script>
<script type="text/javascript" src="./assets/ui-bootstrap/js/ui-bootstrap-tpls-2.5.0.min.js"></script>
这两个文件应该放在您的 Angular JS 应用程序的 index.html 文件中。然后在使用模态对话框的控制器中,也就是调用者控制器,您需要注入 angular 模块 ui.bootstrap
。这是一个例子
var appModule = angular.module("appModule", [ "ui.bootstrap", "dlgModule" ]);
这位于示例应用程序的“app.js”文件中。它定义了一个名为 appModule
的模块。它依赖于两个模块。一个是 ui.bootstrap
模块。另一个是名为 dlgModule
的模块(此时不重要,很快就会用到)。让我总结一下,第一步是将 ui.bootstrap
JavaScript 文件添加到 index.html 页面或任何最终会显示并加载这两个 JavaScript 文件的 html 页面。下一步是将 ui.bootstrap
作为注入模块添加到将使用模态对话框的控制器中。
索引页
索引页将是 Angular JS 应用程序的入口点。对于此示例应用程序,它有一个标题,一个名为“Open Dialog”的按钮,以及一个仅在对话框关闭且结果数据可用时才会显示的隐藏表格。该页面的源代码如下所示
<div class="row" style="margin-top: 65px; margin-bottom: 30px;">
<div class="col-xs-12 col-sm-offset-1 col-sm-10 col-md-offset-2 col-md-8"
ng-app="appModule" ng-controller="appController">
<div class="page-header">
<h1>Search People</h1>
</div>
<button class="btn btn-default" ng-click="openDialog()">Open Dialog</button>
<div class="row" ng-if="result != null">
<div class="col-xs-12">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Profession</th>
<th>Years of Experience</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{result.name}}</td>
<td>{{result.age}}</td>
<td>{{result.profession}}</td>
<td>{{result.yearsOfExp}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
按钮的定义可以在上面的 HTML 源代码中找到,如下所示
<button class="btn btn-default" ng-click="openDialog()">Open Dialog</button>
让我们看看主应用程序的 Angular 控制器。
应用程序控制器
主应用程序的 Angular 控制器只有一个方法,用于处理“打开对话框”的按钮点击。然后还有一个回调用于处理对话框关闭时返回的结果。代码如下
(function () {
"use strict";
var appModule = angular.module("appModule", [ "ui.bootstrap", "dlgModule" ]);
appModule.controller("appController", [ "$scope", "$uibModal", function ($scope, $uibModal) {
$scope.result = null;
$scope.openDialog = function () {
var modalInstance = $uibModal.open({
templateUrl: "assets/app/templates/modalDialog.html",
controller: "dialogController",
size: "sm",
resolve: {
params: function () {
return {
name: "John",
age: 32
};
}
}
});
modalInstance.result.then(function (result) {
$scope.result = result;
}, function () {
console.log("Dialog dismissed");
});
};
}]);
})();
有几件事值得一提。第一个是 JavaScript 闭包,另一个是 IIFE(立即调用函数表达式)。最外层的匿名函数定义创建了一个闭包,一个包含所有函数和变量的作用域。一旦定义,它就会立即被调用。这是对这两个概念的非常简短的解释。它们非常重要(在面试中总是会被问到),所以请有机会时查阅一下。这是关于闭包和 IIFE 的代码
(function () {
...
})();
应用程序模块和控制器的定义如下
"use strict";
var appModule = angular.module("appModule", [ "ui.bootstrap", "dlgModule" ]);
appModule.controller("appController", [ "$scope", "$uibModal", function ($scope, $uibModal) {
...
}]);
在最上面的三行中,中间一行定义了 angular 模块,并添加了 ui.bootstrap
模块的注入。它还注入了我的自定义模块 dlgModule
。最后一行定义了 appController
。请注意,传入了一个字符串和函数的数组,它定义了注入到此控制器中的依赖项。appController
需要 $uibModal
来打开模态对话框,并创建模态实例,该实例稍后可用于接收结果。
打开模态对话框通过以下代码完成
$scope.openDialog = function () { var modalInstance = $uibModal.open({ templateUrl: "assets/app/templates/modalDialog.html", controller: "dialogController", size: "sm", resolve: { params: function () { return { name: "John", age: 32 }; } } .... });
代码段 $scope.openDialog
被设置为一个函数,该函数由 index.html 中的按钮上的 ng-click
使用。在该方法内部,有一个 $uibModal.open(...)
的调用。这就是 ui.bootstrap
工作的地方。open()
方法将弹出一个 Bootstrap 模态对话框。它也是可以初始化对话框和传入输入数据的地方。
您应该注意到以下几点
$uibModal.open(...)
接受一个对象。- 对象的第一个属性是
templateUrl
。此属性指向一个文件,其中包含 Bootstrap 模态对话框的模板,我将在下一节中列出完整的代码。 - 第二个属性称为
controller
,其值为“dialogController
”。这个控制器在哪里?它在模块“dlgModule
”中定义,该模块在定义appModule
时被注入。 - 第三个属性称为
size
,其值可以是“sm
”、“md
”和“lg
”。 - 最后一个属性称为
resolve
,它接受一个对象。这是输入数据传入的地方。在此对象中,有一个名为“params
”的属性,它指向一个返回输入数据的方法。当模态对话框初始化时,它将调用此函数以获取输入数据。
$scope.openDialog
方法的第二部分处理对话框关闭后返回的数据。
modalInstance.result.then(function (result) {
$scope.result = result;
}, function () {
console.log("Dialog dismissed");
});
据我所知,modalInstance.result
是一个 Promise。Promise 是一种在不确定时间处理异步操作回调的方法。对于此示例,您只需要知道它用于将结果从模态对话框传递给调用者。在上面的代码中,then()
方法的第一个方法,我所做的只是将结果传递给作用域的结果 $scope.result
。当模态对话框被取消(或关闭)时,then()
的第二个方法将被调用。
当一个有效的结果返回并分配给应用程序控制器的结果变量时。在 html 标记上,一旦 $scope.result
可用,表格将立即将结果显示为一行。
对话框 HTML 标记
模态对话框的标记 HTML 非常简单。它位于文件“assets/app/templates/modalDialog.html”中。内容如下所示
<div class="modal-header">
<h4 class="modal-title">Test Dialog</h4>
</div>
<div class="modal-body">
<form class="form">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" ng-model="name">
</div>
<div class="form-group">
<label>Age</label>
<input type="number" class="form-control" ng-model="age">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="ok()">Search</button>
<button type="button" class="btn btn-default" ng-click="cancel()">Close</button>
</div>
这并非 Bootstrap 模态对话框的完整定义。我们不需要对话框的完整定义,因为最外层的 <div>
将由 ui-bootstrap 提供。我花了一些时间才弄清楚这一点。如果您使用模态对话框的完整定义,在运行示例应用程序时,您只会在屏幕上看到一条细线。
请注意,在此对话框上有两个输入字段。一个名为“Name
”,另一个名为“Age
”。两者都用于显示输入数据。还有两个按钮,一个用于将结果传回给调用者;另一个用于关闭对话框(用于取消对话框)。接下来,我将向您展示对话框的控制器。
模态对话框的控制器
模态对话框的控制器与应用程序的控制器类似。代码如下
(function () {
"use strict";
var dlgModule = angular.module("dlgModule", []);
dlgModule.controller("dialogController", function ($scope) {
var params = $scope.$resolve.params;
$scope.name = params.name;
$scope.age = params.age;
$scope.cancel = function () {
$scope.$dismiss();
};
$scope.ok = function () {
var retObj = {
name: $scope.name,
age: $scope.age,
profession: "Car Mechanic",
yearsOfExp: 3
};
$scope.$close(retObj);
};
});
})();
模块和控制器的定义非常直接。模块名为“dlgModule
”。没有依赖模块注入到“dlgModule
”中。控制器名为“dialogController
”。这个控制器在哪里使用?在 $uibModule.open()
中。您可以在 app.js 中找到它。
上述控制器定义中的两点很重要
- 检索输入数据的方式
- 将计算数据返回给调用者的方式
事实证明,两者都相当容易实现。正如我前面指出的,$uibModal.open()
使用 resolve
将输入数据传递到模态对话框的控制器,即 dialogController
。一旦程序执行流到达 dialogController
,获取输入数据的代码如下所示
var params = $scope.$resolve.params;
在对话框的作用域中,$resolve
包含输入数据。我通过 $uibModal.open
和 resolve
传入了一个名为“params
”的对象。这就是为什么我们在 $scope.$resolve
中有一个 params
引用。一旦我获得了 params
的引用,我就将它们赋值给作用域变量“name
”和“age
”。它们将显示在模态对话框的输入字段中。如下所示
$scope.name = params.name;
$scope.age = params.age;
假设用户与模态对话框的交互一切顺利,我们得到一个结果并返回给调用者。这是如何完成的
var retObj = {
name: $scope.name,
age: $scope.age,
profession: "Car Mechanic",
yearsOfExp: 3
};
$scope.$close(retObj);
第一部分是创建一个名为 retObj
的对象。然后我调用 $scope.$close()
,它传入对象 retObj
。这就是 ui-bootstrap 如何解决结果(resolve 和 promise)并将其返回给调用者的方式。调用者的回调通过 then()
将被触发并处理返回的结果。
如果用户取消了模态对话框的操作,我们调用 $scope.$dismiss()
,代码如下
$scope.cancel = function () {
$scope.$dismiss();
};
这就是全部。我已经涵盖了关于 ui-bootstrap 模态对话框用法的所有内容。是时候讨论如何测试了。
是时候测试了
此示例程序无法通过点击 index.html 并在浏览器窗口中打开来测试。因此,我在 Jetty Web 服务器中对其进行了测试。我之前写过一篇关于此的文章。这是链接。以下是步骤
- 将示例应用程序的 zip 压缩包下载到临时文件夹。
- 解压缩 zip 压缩包。
- 找到所有扩展名为 .sj 的文件,并将扩展名更改为 .js。
- 将包含“WEB-INF”子文件夹、“assets”子文件夹和“index.html”的文件夹“uibootstrap”复制到 Jetty web 服务器的 webapps 中。
- 使用命令启动 Jetty Web 服务器:
java -cp start.jar
。
一旦 Jetty web 服务器成功启动,我们就可以使用以下 URL 来测试示例应用程序
https://:8080/uibootstrap/
如果一切启动成功,您将看到以下屏幕截图
当您点击按钮时,它将弹出模态对话框
当您点击“搜索”按钮时,它将关闭模态对话框,索引页将显示隐藏的表格
点击“F5”刷新索引页,然后点击“打开对话框”,模态对话框将再次显示。这次,点击模态对话框上的“关闭”按钮。模态对话框将关闭,并且不会显示任何表格。
关注点
一点点的坚持、努力和耐心终于得到了回报。我学会了如何将 ui-bootstrap Angular 组件集成到我的 Angular JS 应用程序中。示例应用程序演示了所有工作原理。我喜欢 ui-bootstrap。我将忙于将其集成到我的个人项目中。
我真正希望的是,这个例子能帮助那些也曾遇到相同问题、无法让 ui-bootstrap 工作的人。本文中的示例应用程序和说明应该有助于您在 Angular JS 和 ui-bootstrap 方面的开发之旅。祝您好运!
历史
- 2018/03/26 - 初稿