Bootstrap+AngularJS实现对话框、表单和表格
本文主要介绍Bootstrap+AngularJS实现简单的对话框,并实现表单提交功能,表单提交之后表格的数据同步更新,并且表格实现了选择动态样式和删除行功能。本文适合作为Bootstrap和AngularJS入门的例子来进行学习。
界面展示
环境准备
由于只是写一个静态页面,所以环境准备只需要在页面中引入所需要的第三方脚本文件和样式文件即可。
<link href="./plugin/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<link href="./plugin/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
第一个link引入的是Bootstrap的样式文件,第二个link是一些常用的三方图标样式文件。
<script type="text/javascript" src="./plugin/angular/angular.js"></script>
<script type="text/javascript" src="./plugin/bootstrap/js/bootstrap.min.js"></script>
两个脚本引入的分别是AngularJS和Bootstrap的脚本文件
(以上引入的文件均为本地文件,也可以引入CDN静态服务的URL获取线上的资源,推荐一个可以查找URL的网址:http://www.bootcdn.cn/)
<script type="text/javascript" src="./script/app.js"></script>
<script type="text/javascript" src="./script/modalController.js"></script>
另外还需要引入自己编写的脚本文件,这里第一个是AngularJS逻辑控制部分的脚本,第二个是对话框交互操作函数的脚本。
html编码
<html ng-app="testviewApp">
在html标签中加入ng-app指令声明html为一个angularJS应用。
<body ng-controller="MainCtrl as mainCtrl">
在body标签中加入指令ng-controller指令,声明控制器作用于为整个body。
<div id="createButton" style="margin:20px;">
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#limitSpeedTemplateDialog">
<span id="tv-iui-createTemplate" name_i18n="tv_iui_i18n"></span>
</button>
</div>
以上代码为点击弹出对话框的按钮,data-toggle=”modal”表示点击按钮弹出模态框,data-target=”#limitSpeedTemplateDialog”表示打开的对话框目标为id为limitSpeedTemplateDialog的模态框。span标签为内联的文本。
一级对话框代码:
<div class="modal fade" id="limitSpeedTemplateDialog"
data-backdrop="static" data-keyboard="false" aria-hidden="true">
<div class="modal-dialog" style="width: 80%;">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span>×</span>
</button>
<h4 class="modal-title" id="tv-iui-LimitSpeedTemplateTitle">创建限速模板</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="templateForm" novalidate>
<div class="form-group">
<label class="control-label col-xs-3 col-sm-3"
for="limit-template-name-input"> <span
id="tv-iui-limitTemplateName">模板名称</span> <span class="required">*</span>
</label>
<div class="col-xs-7 col-sm-7">
<input type="text" class="form-control"
id="limit-template-name-input" required>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-3 col-sm-3"
for="limit-template-flow-list"> <span
id="tv-iui-limitTemplateFlow">流量方向</span>
</label>
<div class="col-xs-7 col-sm-7">
<select class="form-control" id="limit-template-flow-list"
ng-model="mainCtrl.selectedFlow"
ng-options="flow for flow in mainCtrl.flows">
</select>
</div>
</div>
</form>
<hr>
<button class="btn btn-success btn-sm" style="margin: 10px;"
data-toggle="modal" data-target="#matchRuleDialog">
<span class="glyphicon glyphicon-plus pull-left"></span> <span
id="tv-iui-openCreateModal">创建</span>
</button>
<div id="matchRuleTable" class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th id="tv-iui-matchRuleTable-matchItem">匹配项</th>
<th id="tv-iui-matchRuleTable-matchItemValue">匹配项值</th>
<th id="tv-iui-matchRuleTable-promiseSpeed">承诺速率(kbps)</th>
<th id="tv-iui-matchRuleTable-promiseSize">承诺突发尺寸(kbps)</th>
<th id="tv-iui-matchRuleTable-peakSpeed">峰值速率(kbps)</th>
<th id="tv-iui-matchRuleTable-peakSize">峰值突发尺寸(kB)</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="template in mainCtrl.templates"
ng-class="{danger:$index == mainCtrl.selectedRow}"
ng-click="mainCtrl.selectRow($index)">
<td ng-bind="template.matchItem"></td>
<td ng-bind="template.matchItemValue"></td>
<td ng-bind="template.promiseSpeed"></td>
<td ng-bind="template.promiseSize"></td>
<td ng-bind="template.peakSpeed"></td>
<td ng-bind="template.peakSize"></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger pull-left"
id="tv-iui-template-delete"
ng-show="mainCtrl.selectedRow!=undefined"
ng-click="mainCtrl.deleteRow()">删除</button>
<button type="submit" class="btn btn-primary"
id="tv-iui-template-save">确定</button>
<button class="btn btn-default" data-dismiss="modal"
id="tv-iui-template-cancle">取消</button>
</div>
</div>
</div>
</div>
以上代码为创建了一个一级对话框,如下图所示:
代码解析:
- class=”modal fade”表示 Bootstrap 内置模态框,fade表示窗口淡入淡出。
- data-backdrop=”static” data-keyboard=”false” 实现点击窗口之外空白处和按键盘Esc键时不关闭窗口(默认为关闭)。
- aria-hidden=”true”实现窗口在未触发时保持隐藏。
<button type="button" class="close" data-dismiss="modal">
<span>×</span>
</button>
实现窗口关闭按钮,"×"
为关闭符号x的实体符号。- class=”form-horizontal” 实现水平样式的表单。
- class=”control-label 实现label左对齐。
- class=”form-control” 实现表单项的响应式设计,适应不同的屏幕大小。
- class=”col-xs-7 col-sm-7” 同样为了实现响应式设计,为Bootstrap的布局方式,col-xs-7表示在小屏幕上占7列,col-sm-7表示在中型屏幕上占7列。
- ng-model=”mainCtrl.selectedFlow” 为angularJS指令,实现数据的双向绑定
ng-options=”flow for flow in mainCtrl.flows” 为angularJS指令,实现数据遍历并填充到select标签中。 - class=”glyphicon glyphicon-plus” 引入font-awesome中的加号图标。
<table class="table table-bordered table-hover">
实现带边框和悬停效果的表格。<tr ng-repeat="template in mainCtrl.templates"
ng-class="{danger:$index == mainCtrl.selectedRow}"
ng-click="mainCtrl.selectRow($index)">
<td ng-bind="template.matchItem"></td>
其中ng-repeat用来遍历数据,danger:$index == mainCtrl.selectedRow表示点击选择的表格行为当前行时显示danger红色样式(即点击表格哪行,哪行就变红色),ng-click添加点击事件函数,ng-bind单向绑定遍历的数据。
二级对话框代码:
<div class="modal fade" id="matchRuleDialog" data-backdrop="static"
data-keyboard="false" aria-hidden="true">
<div class="modal-dialog" style="width: 50%;">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span>×</span>
</button>
<h4 class="modal-title" id="tv-iui-createMatchRuleTitle">创建匹配规则</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="createMatchRuleForm"
name="mainCtrl.matchRuleForm" novalidate>
<div class="form-group">
<label class="control-label col-lg-3 col-sm-3 col-xs-3"
for="matchItemList"><span id="tv-iui-matchItemLabel">匹配项</span></label>
<div class="col-lg-7 col-sm-7 col-xs-7">
<select class="form-control" id="matchItemList"
ng-model="mainCtrl.newTemplate.matchItem"
ng-options="item for item in mainCtrl.matchItems">
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3 col-sm-3 col-xs-3"
for="matchItemValue-input"> <span
id="tv-iui-matchItemValueLabel">匹配项值</span> <span
class="required">*</span>
</label>
<div class="col-lg-7 col-sm-7 col-xs-7">
<input type="text" class="form-control" id="matchItemValue-input"
name="matchItemValue"
ng-model="mainCtrl.newTemplate.matchItemValue" required>
<span style="color: red"
ng-show="mainCtrl.matchRuleForm.matchItemValue.$dirty && mainCtrl.matchRuleForm.matchItemValue.$invalid">
<span
ng-show="mainCtrl.matchRuleForm.matchItemValue.$error.required"
id="tv-iui-requiredWarning">必填项</span>
</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3 col-sm-3 col-xs-3"
for="promiseSpeed-input"> <span
id="tv-iui-promiseSpeedLabel">承诺速率(kbps)</span> <span
class="required">*</span>
</label>
<div class="col-lg-7 col-sm-7 col-xs-7">
<input type="text" class="form-control" id="promiseSpeed-input"
name="promiseSpeed" ng-model="mainCtrl.newTemplate.promiseSpeed"
placeholder="0-9999" ng-pattern="/^0|[1-9][0-9]{0,3}$/" required>
<span style="color: red"
ng-show="mainCtrl.matchRuleForm.promiseSpeed.$dirty && mainCtrl.matchRuleForm.promiseSpeed.$invalid">
<span
ng-show="mainCtrl.matchRuleForm.promiseSpeed.$error.required"
id="tv-iui-requiredWarning">必填项</span> <span
ng-show="mainCtrl.matchRuleForm.promiseSpeed.$error.pattern"
id="tv-iui-patternWarning">不符合规则</span>
</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3 col-sm-3 col-xs-3"
for="promiseBandwidth-input"> <span
id="tv-iui-promiseSizeLabel">承诺突发尺寸(kB)</span>
</label>
<div class="col-lg-7 col-sm-7 col-xs-7">
<input type="text" class="form-control"
id="promiseBandwidth-input" name="promiseSize"
ng-model="mainCtrl.newTemplate.promiseSize" placeholder="0-9999"
ng-pattern="/^0|[1-9][0-9]{0,3}$/"> <span
style="color: red"
ng-show="mainCtrl.matchRuleForm.promiseSize.$dirty && mainCtrl.matchRuleForm.promiseSize.$invalid">
<span
ng-show="mainCtrl.matchRuleForm.promiseSize.$error.pattern"
id="tv-iui-patternWarning">不符合规则</span>
</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3 col-sm-3 col-xs-3" for="promiseSpeed-input"> <span
id="tv-iui-peakSpeedLabel">峰值速率(kbps)</span> <span
class="required">*</span>
</label>
<div class="col-lg-7 col-sm-7 col-xs-7">
<input type="text" class="form-control" id="peakSpeed-input"
name="peakSpeed" ng-model="mainCtrl.newTemplate.peakSpeed"
placeholder="0-9999" ng-pattern="/^0|[1-9][0-9]{0,3}$/" required>
<span style="color: red"
ng-show="mainCtrl.matchRuleForm.peakSpeed.$dirty && mainCtrl.matchRuleForm.peakSpeed.$invalid">
<span ng-show="mainCtrl.matchRuleForm.peakSpeed.$error.required"
id="tv-iui-requiredWarning">必填项</span> <span
ng-show="mainCtrl.matchRuleForm.peakSpeed.$error.pattern"
id="tv-iui-patternWarning">不符合规则</span>
</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3 col-sm-3 col-xs-3"
for="promiseBandwidth-input"> <span
id="tv-iui-peakSizeLabel">峰值突发尺寸(kB)</span>
</label>
<div class="col-lg-7 col-sm-7 col-xs-7">
<input type="text" class="form-control"
id="promiseBandwidth-input" name="peakSize"
ng-model="mainCtrl.newTemplate.peakSize" placeholder="0-9999"
ng-pattern="/^0|[1-9][0-9]{0,3}$/"> <span
style="color: red"
ng-show="mainCtrl.matchRuleForm.peakSize.$dirty && mainCtrl.matchRuleForm.peakSize.$invalid">
<span ng-show="mainCtrl.matchRuleForm.peakSize.$error.pattern"
id="tv-iui-patternWarning">不符合规则</span>
</span>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary"
id="tv-iui-matchRule-save"
ng-click="[mainCtrl.addTemplate(),mainCtrl.resetTemplate()]"
ng-disabled="mainCtrl.matchRuleForm.$invalid">确定</button>
<button class="btn btn-default" data-dismiss="modal"
id="tv-iui-matchRule-cancle">取消</button>
</div>
</div>
</div>
</div>
以上为点击一级对话框中的创建按钮后弹出的二级对话框代码,对应界面如下图:
代码解析:
- ng-model=”mainCtrl.newTemplate.matchItemValue” required 在表单input标签中,与数据绑定,且required对输入框进行必填项验证。
placeholder=”0-9999”
ng-pattern=”/^0|[1-9][0-9]{0,3}$/”
分别给输入框加入输入提示,正则表达式验证。<span style="color: red"
ng-show="mainCtrl.matchRuleForm.promiseSize.$dirty
&& mainCtrl.matchRuleForm.promiseSize.$invalid">
<span ng-show="mainCtrl.matchRuleForm.promiseSize.$error.pattern"
id="tv-iui-patternWarning">不符合规则</span>
</span>
这段代码实现表单的验证动态样式,输入框输入值且未通过验证则输入框显示红色且在输入框旁边显示”不符合规则“文本,若通过验证则提示样式取消。ng-disabled=”mainCtrl.matchRuleForm.$invalid” 实现确定按钮在表单未通过验证情况下不能点击。
js脚本编码
AngularJS的脚本为app.js,代码如下:
angular.module('testviewApp',[])
.controller('MainCtrl',[function(){
this.templates=[
{matchItem:'A', matchItemValue:1, promiseSpeed:10, promiseSize:10, peakSpeed:10,peakSize:10},
{matchItem:'B', matchItemValue:2, promiseSpeed:10, promiseSize:10, peakSpeed:10,peakSize:10},
{matchItem:'C', matchItemValue:3, promiseSpeed:10, promiseSize:10, peakSpeed:10,peakSize:10}
];
this.flows=['W','N','E','S'];
this.selectedFlow='W';
this.matchItems=['A','B','C','D'];
this.newTemplate={matchItem:'A'};
this.addTemplate = function(){
this.templates.push(this.newTemplate);
};
this.resetTemplate = function(){
this.newTemplate={matchItem:'A'};
this.matchRuleForm.$setPristine();
};
this.selectRow = function(rowIndex){
this.selectedRow=rowIndex;
};
this.deleteRow = function(){
this.templates.splice(this.selectedRow,1);
};
}]);
代码解析:
- angular.module(‘testviewApp’,[]) 定义angular应用,并返回应用。第一个参数与html中声明的应用名对应,第二个参数为依赖的服务或控制器数组(目前未用到,所以为空数组)。
- controller(‘MainCtrl’,[function(){ 定义控制器,第一个参数为控制器名称,第二个参数为控制器函数,对数据的操作都在控制器函数中。
- 控制器函数中的代码都是对控制器作用域内的数据和事件函数进行初始化和定义,其中 this.matchRuleForm.$setPristine(); 使用angular内置函数让表单重置为初始状态,在取消对话框或提交表单后触发。
- this.templates.splice(this.selectedRow,1); 实现删除选中的表格行。
需要注意的是,在angular控制器函数中对绑定数据进行的操作,都会自动同步到界面,不需要对界面进行操作, 实现数据和界面完全分离。
二级对话框提交表单之后同步在一级对话框表格的界面如下图:
总结
利用Bootstrap直接进行样式定制可以快速创建出可用的响应式界面,可以比较容易地适应各种大小屏幕;使用AngularJS框架可以用少量的代码就实现前端页面的复杂逻辑,将MVC模式完美地应用于前端开发。