基于Bootstrap和AngularJS的现代Web应用
现代的B/S系统特色
1.单页面应用(single-page application,简称SPA)
所有资源(html、js、css)都按需动态加载到页面上,且不需要服务端控制页面页面的转向
2.响应式设计(Responsive web design,简称RWD)
不同设备访问相同页面,得到不同页面视图,得到的视图是适应当前屏幕的。
3.数据导向
针对于页面导向而言,页面上的数据获得是通过消费后台的REST服务来实现的,而不是通过服务器渲染的动态页面实现,一般数据交换使用的格式是JSON
1.Bootstrap
Bootstrap定义:Bootstrap是开发响应式和移动优先的Web应用的最流行的HTML、css、JavaScript框架
Bootstrap实现了只使用一套代码就可以在不同设备显示你想要的视图的功能。提供了大量美观的HTML元素前端组件和jQuery插件。
1.1 下载并引入Bootstrap
1.2 CSS支持
Bootstrap的CSS样式为基础的HTML元素提供了美观的央视,此外提供了高级的网格系统用来做页面布局。简单的页面模板:
<!DOCTYPE
html
>
<
html
lang=
"zh-cn"
>
<!-- 上面两句为HTML5的doctype -->
<
head
>
<
meta
charset=
"utf-8"
>
<
meta
http-equiv=
"X-UA-Compatible"
content=
"IE=edge"
>
<
meta
name=
"viewport"
content=
"width=device-width, initial-scale=1"
>
<!-- 上面3个meta标签必须是head的头三个标签,其余的head内标签在此3个之后The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<
title
>
Bootstrap基本模板
</
title
>
<!-- Bootstrap的CSS -->
<
link
href=
"bootstrap/css/bootstrap.min.css"
rel=
"stylesheet"
>
<!-- HTML5 shim and Respond.js用来让IE8支持HTML5元素和媒体查询 -->
<!--[if lt IE 9]>
<script src="js/html5shiv.min.js"></script>
<script src="js/respond.min.js"></script>
<![endif]-->
</
head
>
<
body
>
<
h1
>
你好, Bootstrap!
</
h1
>
<!-- jQuery是Bootstrap脚本插件必须的 -->
<
script
src=
"js/jquery.min.js"
></
script
>
<!-- 包含所有编译的插件 -->
<
script
src=
"bootstrap/js/bootstrap.min.js"
></
script
>
</
body
>
</
html
>
1.2.1 布局网格
在Bootstrap里,行使用的样式为row,列使用col-md-数字,此数字范围为1-12,所有列加起来的和也是12.
<
div
class=
"row"
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
<
div
class=
"col-md-1"
>
.col-md-1
</
div
>
</
div
>
<
div
class=
"row"
>
<
div
class=
"col-md-8"
>
.col-md-8
</
div
>
<
div
class=
"col-md-4"
>
.col-md-4
</
div
>
</
div
>
<
div
class=
"row"
>
<
div
class=
"col-md-4"
>
.col-md-4
</
div
>
<
div
class=
"col-md-4"
>
.col-md-4
</
div
>
<
div
class=
"col-md-4"
>
.col-md-4
</
div
>
</
div
>
<
div
class=
"row"
>
<
div
class=
"col-md-6"
>
.col-md-6
</
div
>
<
div
class=
"col-md-6"
>
.col-md-6
</
div
>
</
div
>
1.2.2 html元素
Bootstrap为html元素提供了大量的样式,如表单元素、按钮等,具体内容查看
http://getbootstrap.com/css
1.3 页面组件支持
Bootstrap提供了大量的页面组件,包括字体图标、下拉框等,更多阅读
http://getbootstrap.com/components/
1.4 javascript支持
Bootstrap提供大量JavaScript插件,如模式对话框、标签页、提示灯,更多阅读
http://getbootstrap.com/javascript/
2.AngularJS
AngularJS定义:是HTML开发本应该的样子,用来设计开发Web应用的。
AngularJS使用声明式模板+数据绑定(类似JSP、Thymeleaf)、MVW(Model-View-Whatever)、MVVM(Model-View-ViewModel)、MVC(Model-View-Controller)、依赖注入和测试,这一切实现只借助纯客户端的JS。
HTML用来声明静态页面,AngularJS可以只通过前端技术就实现动态的页面。
2.1 下载并引入AngularJS
下载地址:
https://angularjs.org
简单的页面代码:
<!doctype
html
>
<
html
ng-app
>
<!-- ng-app所作用的范围是AngularJS起效的范围,本例是整个页面有效 -->
<
head
>
<
meta
charset=
"utf-8"
>
<
script
src=
"js/angular.min.js"
></
script
>
<!-- 载入AngularJS的脚本 -->
</
head
>
<
body
>
<
div
>
<
label
>
名字:
</
label
>
<
input
type=
"text"
ng-model=
"yourName"
placeholder=
"输入你的名字"
>
<!-- ng-model定义整个AngularJS的前端数据模型,模型的名称为yourName,模型的值来自你输入的值,若输入值改变,则数据模型值也改变。 -->
<
hr
>
<
h1
>
你好 {{yourName}}!
</
h1
>
<!-- 使用{{模型名}}来读取模型中的值 -->
</
div
>
</
body
>
</
html
>
2.2 模块、控制器和数据绑定
用AngularJS实现纯页面端的MVC,即
实现了视图模板、数据模板、代码控制的分离。
数据绑定是将视图和数据模型绑定在一起。如果视图变了,模型的值也随着变了,反之一样。
AngularJS为了分离代码达到复用的效果,提供了一个module(模块)。定义一个模块需要下面的代码。
无依赖模块:angular.module('firstModule',[]);
有依赖模块:angular.module('firstModule',['moduleA','moduleB']);
V就是页面元素,M就是ng-model,C就是ng-controller(页面通过ng-controller来与其关联)
angular.module('firstModule',[])
.controller('firstController',function(){
.....
};
);
<div ng-controller="firstController">
...
</div>
2.3 Scope和Event
(1) Scope
Scope是AngularJS的内置对象,用$Scope获得。在Scope中定义的数据是数据模型,可以通过
{{模型名}}在视图上获得。
Scope主要是在编码中需要对数据模型进行处理的时候使用。
Scope的作用范围与在页面声明的范围一致(如在controller内使用,scope的作用范围是页面声明2ng-controller标签元素的作用范围)
定义:$scope.greeting='Hello'
获取:{{greeting}}
(2)Event
因为Scope的作用范围不同,所以不同的Scope之间若要交互的话需要通过事件(Event)来完成
1)冒泡事件(Emit)冒泡事件负责从子Scope向上发送事件
子Scope发送:
$scope.$emit('EVENT_NAME_EMIT',''message);
父Scope接受:
$scope.$on(''EVENT_NAME_EMIT',function(event,data){
.......})
2)广播事件(Broadcast).广播事件负责从父Scope向下发送事件。
父Scope发送:
$scope.$broadcast('EVENT_NAME_BROAD','message');
子scope接受:
$scope.$on(''EVENT_NAME_BROAD',function(event,data){
.......})
2.4 多视图和路由
多视图和路由是AngularJS实现单页面应用的技术关键,AngularJS内置了一个$routeProvider对象来负责页面加载和页面路由转向。
注意:1.2.0之后的AngularJS将路由功能移出
,所以使用路由功能要另外引入angular-route.js
例如:
angular.module('firstModule').config(function($routeProvider){
$routeProvider.when('/view1',{//此处定义的是某个页面的路由名称
controller:'Controller1',//此处定义的是当前页面使用的控制器
templateUrl:'view1.html',//此处定义的是加载的真实页面
}).when('/view2',{
controller:'Controller2',
templateUrl:'view2.html',
});
})
在页面上可以用下面代码来使用我们定义的路由:
<ul>
<li><a href="#/view1">view1</a></li>
<li><a href="#/view2">view2</a></li>
</ul>
<ng-view></ng-view><!--此处为加载进来的页面显示的位置-->
2.5 依赖注入
依赖注入是AngularJS的重要功能,可以实现对代码的解耦,在代码里可以注入AngularJS的对象或者我们自定义的对象。
在控制器注入$scope,注意使用依赖注入的代码格式:
angular.module('firstModule')
.controller("diController",{'$scope',function($scope){
.........}});
2.6 Service和Factory
AngularJS内置了一些服务,如$location、$timeout、$root.
自己定制服务,AngularJS提供了Service和Factory
Service:AngularJS使用new来初始化对象
Factory:直接获得对象
(1) Service
定义:
angular.module('firstModule').service('helloService',function(){
this.sayHello=function(name){
alert('Hello'+name);
}
});
注入调用:
angular.module('firstModule').controller("diController",['$scope','helloService',
function($scope,helloService){
helloService.sayHello('lqy');
}]);
(2) Factory
定义:
angular.module('firstModule'.service('helloFactory',function(){
return{
sayHello:function(name){
alert('Hello'+name);
}
}
}));
注入调用:
angular.module('firstModule').controller("diController",['$scope','helloFactory',
function($scope,helloFactory){
helloFactory.sayHello('lqy');
}]);
2.7 自定义指令
AngularJS内置了大量的指令(directive),如ng-repeat、ng-show、ng-model等。即使用一个简短的指令可实现一个前端组件。
举例:一个日期的js/jQuery插件,使用AngularJS封装后,在页面上调用该插件可以通过指令实现。
元素指令:<date-picker></date-picker>
属性指令:<input type="text" date-picker/>
样式指令:<input type="text" class="date-picker"/>
注释指令:<!-- directive;date-picker-->
定义指令:
angular.module('myApp',[]).directive('helloWorld',function(){
return {
restrict:'AE',//支持使用属性、元素
replace:true,
template:'<h3>Hello,World!</h3>'
};
});
调用指令,元素标签:
<hello-world/>
<hello:world/>
属性方式:
<div hello-world/>
实战
使用Bootstrap制作导航,使用AngularJS实现导航切换页面的路由功能,
并演示AngularJS通过$http服务和Spring Boot提供的REST服务,最后演示用指令封装jQueryUI的日期选择器。
1.新建Spring Boot项目
选择Web依赖(spring-boot-starter-web)
准备Bootstrap、AngularJS、jQuery、jQueryUI相关的资源到src/main/resources/static下。(静态页面统一放到static下)
2.制作导航
页面位置:src/main/resources/static/action.html
<!DOCTYPE
html
>
<
html
lang=
"zh-cn"
ng-app=
"actionApp"
>
<
head
>
<
meta
charset=
"utf-8"
>
<
meta
http-equiv=
"X-UA-Compatible"
content=
"IE=edge"
>
<
meta
name=
"viewport"
content=
"width=device-width, initial-scale=1"
>
<
title
>
实战
</
title
>
<!-- Bootstrap的CSS -->
<
link
href=
"bootstrap/css/bootstrap.min.css"
rel=
"stylesheet"
>
<
link
href=
"jqueryui/jquery-ui.min.css"
rel=
"stylesheet"
>
<
style
type=
"text/css"
>
.
content
{
padding
:
100
px
15
px
;
text-align
:
center
;
}
</
style
>
<!--[if lt IE 9]>
<script src="js/html5shiv.min.js"></script>
<script src="js/respond.min.js"></script>
<![endif]-->
</
head
>
<!--使用Bootstrap定义的导航,并配合AngularJS的路由,通过路由名称#/oper和#/directive切换视图 -->
<
body
>
<
nav
class=
"navbar navbar-inverse navbar-fixed-top"
>
<
div
class=
"container"
>
<
div
id=
"navbar"
class=
"collapse navbar-collapse"
>
<
ul
class=
"nav navbar-nav"
>
<
li
><
a
href=
"#/oper"
>
后台交互
</
a
></
li
>
<
li
><
a
href=
"#/directive"
>
自定义指令
</
a
></
li
>
</
ul
>
</
div
>
<!--/.nav-collapse -->
</
div
>
</
nav
>
<!--通过<ng-view></ng-view>展示载入的页面-->
<
div
class=
"content"
>
<
ng-view
></
ng-view
>
</
div
>
<!--加载本例所需的脚本,其中jquery-ui.min.js的脚本是为定制指令所用;
app.js定义AngularJS的模块和路由;
directives.js为自定义的指令;
controllers.js是控制器定义之处 -->
<
script
src=
"js/jquery.min.js"
></
script
>
<
script
src=
"jqueryui/jquery-ui.min.js"
></
script
>
<
script
src=
"bootstrap/js/bootstrap.min.js"
></
script
>
<
script
src=
"js/angular.min.js"
></
script
>
<
script
src=
"js/angular-route.min.js"
></
script
>
<
script
src=
"js-action/app.js"
></
script
>
<
script
src=
"js-action/directives.js"
></
script
>
<
script
src=
"js-action/controllers.js"
></
script
>
</
body
>
</
html
>
3.模块和路由定义
页面位置:src/main/resources/static/js-action/app.js
/**
*
*/
var
actionApp
=
angular
.module(
'actionApp'
,[
'ngRoute'
]);
//定义模块actionApp,并依赖于路由模块ngRoute
actionApp
.
config
([
'$routeProvider'
,
function
($routeProvider) {
//配置路由,并注入$routeProvider来配置
$routeProvider.
when
(
'/oper'
, {
//oper为路由名称
controller
:
'View1Controller'
,
//controller定义的是路由的控制器名称
templateUrl
:
'views/view1.html'
,
//templateUrl定义的是视图的真正地址
}).
when
(
'/directive'
, {
controller
:
'View2Controller'
,
templateUrl
:
'views/view2.html'
,
});
}]);
4.控制器定义
脚本位置:src/main/resources/static/js-action/controllers.js
/**
*
*/
//定义控制器View1Controller,并注入$rootScope、$scope和$http
actionApp
.
controller
(
'View1Controller'
, [
'$rootScope'
,
'$scope'
,
'$http'
,
function
($rootScope, $scope,$http) {
//使用$scope.$on监听$viewContentLoaded事件,可以在页面内容加载完成后进行一些操作
$scope.
$on
(
'$viewContentLoaded'
,
function
() {
console
.
log
(
'页面加载完成'
);
});
//这段代码是演示的核心代码,结合View的界面一起理解:
/**
* 1.在scope内定义一个方法search,在页面上通过ng-click调用。
* 2.通过$scope.personName获取页面定义的ng-model="personName"的值
* 3.使用$http.get向服务端地址search发送get请求
* 4.使用params增加请求参数。
* 5.用success方法作为请求成功后的回调
* 6.将服务器返回的数据data通过$scope.person赋给模型person,
* 这样页面视图上可以通过{{person.name}}、{{person.age}}、{{person.address}}来调用,
* 且模型person值改变后,视图是自动更新的。
*/
$scope.
search
=
function
(){
//1
personName
= $scope.
personName
;
//2
$http.get(
'search'
,{
//3
params
:{
personName
:
personName
}
//4
}).
success
(
function
(data){
//5
$scope.
person
=data;
//6
});;
};
}]);
actionApp
.
controller
(
'View2Controller'
, [
'$rootScope'
,
'$scope'
,
function
($rootScope, $scope) {
$scope.
$on
(
'$viewContentLoaded'
,
function
() {
console
.
log
(
'页面加载完成'
);
});
}]);
5.View1的界面(演示与服务端交互)src/main/resources/static/views/view1.html
<
div
class=
"row"
>
<
label
for=
"attr"
class=
"col-md-2 control-label"
>
名称
</
label
>
<
div
class=
"col-md-2"
>
<!-- 定义数据模型ng-model="personName" -->
<
input
type=
"text"
class=
"form-control"
ng-model=
"personName"
>
</
div
>
<
div
class=
"col-md-1"
>
<!-- 通过ng-click="search()"调用控制器中定义的方法 -->
<
button
class=
"btn btn-primary"
ng-click=
"search()"
>
查询
</
button
>
</
div
>
</
div
>
<
div
class=
"row"
>
<
div
class=
"col-md-4"
>
<
ul
class=
"list-group"
>
<!-- 通过{{person.name}}、{{person.age}}、{{person.address}}访问控制器的scope里定义person模型,模型和视图是绑定的 -->
<
li
class=
"list-group-item"
>
名字: {{person.name}}
</
li
>
<
li
class=
"list-group-item"
>
年龄: {{person.age}}
</
li
>
<
li
class=
"list-group-item"
>
地址: {{person.address}}
</
li
>
</
ul
>
</
div
>
</
div
>
6.服务端 代码
传值对象Javabean:
package
com.hand;
public class
Person {
private
String
name
;
private
Integer
age
;
private
String
address
;
public
Person() {
super
();
}
public
Person(String name, Integer age, String address) {
super
();
this
.
name
= name;
this
.
age
= age;
this
.
address
= address;
}
public
String getName() {
return
name
;
}
public void
setName(String name) {
this
.
name
= name;
}
public
Integer getAge() {
return
age
;
}
public void
setAge(Integer age) {
this
.
age
= age;
}
public
String getAddress() {
return
address
;
}
public void
setAddress(String address) {
this
.
address
= address;
}
}
7.自定义指令:src/main/resources/static/js-action/directives.js;
/**
* 定制了一个封装jqueryui的datePicker的指令
*/
actionApp
.
directive
(
'datePicker'
,
function
(){
//定义一个指令名为datePicker
return
{
restrict
:
'AC'
,
//限制为属性指令和样式指令
link
:
function
(scope,elem,attrs){
//使用link方法来定义指令,在Link方法内可使用当前scope,当前元素及元素属性
// scope.treeObj = $.fn.zTree.init(elem, scope.settings);
elem.
datepicker
();
//初始化jqueryui的dataPicker(jquery的写法是$('#id').dataPicker())
}
};
});
8.View2的页面(演示自定义指令)
<
div
class=
"row"
>
<
label
for=
"attr"
class=
"col-md-2 control-label"
>
属性形式
</
label
>
<
div
class=
"col-md-2"
>
<!-- 使用属性形式调用指令 -->
<
input
type=
"text"
class=
"form-control"
date-picker
>
</
div
>
</
div
>
<
div
class=
"row"
>
<
label
for=
"style"
class=
"col-md-2 control-label"
>
样式形式
</
label
>
<
div
class=
"col-md-2"
>
<!-- 使用样式形式调用指令 -->
<
input
type=
"text"
class=
"form-control date-picker"
>
</
div
>
</
div
>
9.运行