Spring Boot实战(七)Spring Boot 的 Web 开发 7.7 基于 Bootstrap 和 AngularJS 的现代Web应用

现代的B/S系统软件有下面几个特色。
1.单页面应用
单页面应用(single-page application,简称SPA)指的是一种类似于原生客户端软件的更流畅的用户体验的页面。在单页面的应用中,所有的资源(HTML、Javascript、CSS)都是按需动态加载到页面上的,且不需要服务端控制页面的转向。
2.响应式设计
响应式设计(Responsive web design,简称RWD)指的是不同的设备(电脑、平板、手机)访问相同的页面的时候,得到不同的页面视图,而得到的视图是适应当前屏幕的。当然就算在电脑上,我们通过拖动浏览器窗口的大小, 也通用得到合适的视图。
3.数据导向
数据导向是对于页面导向而言的,页面上的数据获得是通过消费后台的REST服务来实现的,而不是通过服务器渲染的动态页面(如JSP)来实现的,一般数据交换使用的格式是JSON。
本节将针对Bootstrap 和 AngularJS 进行快速入门式的引导,如需深入学习,请参考官网或相关专题书籍。

7.7.1 Bootstrap
1.什么是Bootstrap
Bootstrap官方定义:Bootstrap是开发响应式和移动优先的Web应用的最流行的HTML、CSS、JavaScript框架。
2.下载并引入Bootstrap
下载地址:http://getbootstrap.com/getting-started/, 如图
在这里插入图片描述
下载的压缩包的目录结构如图
在这里插入图片描述
最简单的Bootstrap页面模板如下:

<!DOCTYPE html>
<html lang="zh-cn">
<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个之后 -->
<title>Bootstrap基本模板</title>

<!-- Bootstrap的CSS -->
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">

<!-- HTML5 shim and Respond.js 用来让IE 8 支持 HTML 5 元素和媒体查询 -->
	<!-- [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>

3.CSS支持
Bootstrap的CSS样式为基础的HTML元素提供了美观的样式,此外还提供了一个高级的网格系统用来做页面布局。
(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>
		<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>
		<div class="col-md-6">.col-md-6</div>
		<div class="col-md-6">.col-md-6</div>
	</div>

(2)html元素
Bootsrtap为html元素提供了大量的样式,如表单元素、按钮、图标等。更多内容请查看:http://getbootstrap.com/css/。
4.页面组件支持
Bootstrap为我们提供了大量的页面组件,包括字体图标、下拉框、导航条、进度条、缩略图等,更多请阅读http://getboostrap.com/components/。
5.javascript支持
Bootstrap为我们提供了大量的JavaScript插件,包含模式对话框、标签页、提示、警告等,更多内容请查看http://getbootstrap.com/javascript/。
7.7.2 AngularJS
1.什么是AngularJS
AugularJS官方定义:AngularJS是HTML开发本应该的样子,它是用来设计开发Web应用的。
AngularJS使用声明式模板+数据绑定(类似于JSP、Thymeleaf)、MVW(Model-View——Whatever)、MVVM(Model-View-ViewModel)、MVC(Model-View-Controller)、依赖注入和测试,但是这一切的实现却只借助纯客户端的JavaScript。
HTML一般是用来声明静态页面的,但是通常情况下我们希望页面是基于数据动态生成的,这也是我们很多服务端模板引擎出现的原因;而AngularJS可以只通过前端技术就能实现动态的页面。
2.下载并引入AngularJS
AngularJS下载地址:https://code.angularjs.org/
最简单的AngularJS页面

<!DOCTYPE html>
<html ng-app> <!-- ng-app所作用的范围是AngularJS起效的范围,本例是整个页面有效。 -->
<head>
<sctipt src="js/angular.min.js"></sctipt>  <!-- 载入AngularJS的 脚本 -->
</head>
<body>
<div>
	<label>名字:</label>
	<input type="text" ng-model="yourName" placeholder="输入你的名字"> <!-- ng-model定义整个AngularJS的前端数据模型,模型的名称为yourName,模型的值来自你输入的值若输入的值改变,则数据模型值也会变 -->
	<hr>
	<h1>你好{{yourName}}!</h1><!-- 使用{{模型名}}来读取模型中的值。 -->
</div>
</body>
</html>

3.模块、控制器和数据绑定
我们对MVC的概念已经烂熟于心了,但是平时的MVC都是服务端的MVC,这里用AngularJS实现了纯页面端MVC,即实现了视图模板、数据模型、代码控制的分离。
再来看看数据绑定,数据绑定是将视图和数据模型绑定在一起。如果视图变了,则模型的值就变了;如果模型值变了,则视图也会跟着改变。
AngularJS为了分离代码达到复用的效果,提供了一个module(模块)。定义一个模块需使用下面的代码。
无依赖模块:

angular.module('firstModule',[]);

有依赖模块

angular.module('firstModule',['moduleA','moduleB']);

我们看到了V就是我们的页面元素,M就是我们的ng-model,那C呢?我们可以通过下面的代码来定义控制器,页面使用ng-controller来和其关联:

augular.module('firstModule',[])
	.controller('firstController',funtion(){
	...
	};
);

<div ng-controller="firstController">
...
</div>

4.Scope和Event
(1)Scope
Scope是AngularJS的内置对象,用$Scope来获得。在Scope中定义的数据是数据模型,可以通过{{模型名}}在视图上获得。Scope主要是在编码中需要对数据模型进行处理的时候使用,Scope的作用范围与在页面声明的范围一致(如在controller内使用,scope的作用范围是页面声明ng-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){
...
})

5.多视图和路由
多视图和路由是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>  <!--此处为加载进来的页面显示的位置  -->

依赖注入
依赖注入是AngularJS的一大酷炫功能。可以实现对代码的解耦,在代码里可以注入AngularJS的对象或者我们自定义的对象。下面示例是在控制器中注入$scope,注意使用依赖注入的代码格式。

angular.module('firstModule')
	.controller("diController",['$scope',
		function($scope){
				...
			}]);

7.Service和Factory
AngularJS为我们内置了一些服务,如$location、$time、$rootScope 。很多时候我们需要自己定制一些服务,AngularJS为我们提供了Service和Factory。
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('lmz');
	}]);

(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.sayHelle('lmz');
		 }]);

8.http操作
AngularJS内置了$http对象用来进行Ajax的操作:

$http.get(url)
$http.post(url,data)
$http.put(url,data)
$http.delete(url)
$http.head(url)

9.自定义指令
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 />

7.3.3 实战
在前面两节我们快速介绍了Bootstrap和AngularJS,本节我人将它们和Spring Boot串起来做个例子。
在例子中,我们使用Bootstrap制作导航使用AngularJS实现导航切换页面的路由功能,并演示AngularJS通过$http服务和Spring Boot提供的REST服务,最后演示用指令封装jQuery UI的日期选择器。
1.新建Spring Boot项目
初始化一个Spring Boot项目,依赖只需选择Web 。
项目信息:

groupId:com.wisely
arctifactId:ch7_7
package:com.wisely.ch7_7

准备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>

<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="jqueryui/jquery-ui.min.css" rel="sylesheet">
<style type="text/css">

.content{
	padding:100px 15px;
	text-align: center;
}
</style>

<!--[if lt IE 9]>
	<script src="js/html5shiv.min.js"></script>
	<script src="js/respond.min.js"></scripte>
	<![endif]-->
</head>
<body>
<!-- 使用Bootstrap定义的导航,并配合AngularJS的路由,通过路由名称#/oper和#/directive切换视图 -->
	<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><a href="#/directive">自定义指令</a></li>
				</ul>
			</div>
		</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,并依赖于路由模块ngRout。

actionApp.config(['$routeProvider'] , function($routeProvider){  //配置路由,并注入$routeProvider来配置
	$routeProvider.when('/oper',{        //  /oper为路由名称
		controller:'View1Controller',     //controller定义的是路由的控制器名称
		templateUrl:'views/view1.html',    //templateUrl定义的是视图的真正地址
	}).when('/directive',{
		controller:'View2Controller',
		templateUrl:'views/view.html',
	});
});

控制器定义
脚本位置: 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('页面加载完成');
	});
	//这段代码是核心 代码,请结合下面的View1的界面一起理解
	$scope.search=function(){  //在scope内定义一个方法search,在页面上通过ng-click调用。
		personName = $scope.personName;        //通过$scope.personName获取页面定义的ng-model = "personName" 的值。
		$http.get('search',{                 //使用$http.get向服务器地址search发送get请求。
			  		params:{personName:personName}   //使用params增加请求参数
		}).success(function(data){                   //用success方法作为请求成功后的回调。
			$scope.person=data;                     //将服务端返回的数据data通过$scope.person赋给模型person,这样页面视图上可以通过{{person.name}}、{{person.age}}、{{person.address}}来调用,且模型person值改变后,视图是自动更新的
		});
	};
}]);

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">
				<li class="list-group-item">名字:{{person.name}}
				<li class="list-group-item">年龄:{{person.age}}
				<li class="list-group-item">地址:{{person.address}}
			</ul>
		</div>
	</div>

6.服务端代码
传值对象Javabean:

package com.wisely.ch7_7;

/**
 * @author mengzhi.ling
 *
 */
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;
	}
	
	

}

控制器:

package com.wisely.ch7_7;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class Ch77Application {
	
	@RequestMapping(value="/search",produces= {MediaType.APPLICATION_JSON_VALUE})
	public Person search(String personName) {
		return new Person(personName,32,"shenzhen");
	}

	public static void main(String[] args) {
		SpringApplication.run(Ch77Application.class, args);
	}

}


7.自定义指令
脚本位置: src/main/resources/static/js-action/directives.js:

actionApp.directive('datePicker',function(){  //定义一个指令名为datePicker。
	return{
		restrict:'AC',   //限制为属性指令和样式指令。
		link:function(scope,elem,attrs){   //使用link方法来定义指令,在Link方法内可使用当前scope、当前元素及元素属性。
			elem.datepicher();				//初始化jqueryui的datePicker(jquery的写法是$('#id').datePicker()).
		}
	};
});

通过上面的代码我们就定制了一个封装jqueryui的datePicker的指令,本例只是为了演示的目的,主流的脚本框架已经被很多人封装过了,有兴趣的读者可以访问http://ngmodules.org/网站,这个网站包含了大量的AngularJS的第三方模块、插件和指令。
8.View2的页面(演示自定义指令)
页面地址: src/main/resources/static/views/view2.html:

<div class="row">
	<label for="arrt" 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.运行
菜单及路由切换如图
在这里插入图片描述
在这里插入图片描述

与后台交互如图
在这里插入图片描述
自定义指定如图
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40929047/article/details/86679046