目标
- 运用AngularJS前端框架的常用指令
- 完成品牌管理的列表功能
- 完成品牌管理的分页列表功能
- 完成品牌管理的增加功能
- 完成品牌管理的修改功能
- 完成品牌管理的删除功能
- 完成品牌管理的条件查询功能
1. 入门
1. 简介
AngularJS 诞生于2009年,由Misko Hevery 等人创建,后为Google所收购。是一款优秀的前端JS框架,已经被用于Google的多款产品当中。AngularJS有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、依赖注入等等。
2. 四大特征
1. mvc模式
Angular遵循软件工程的MVC模式,并鼓励展现,数据,和逻辑组件之间的松耦合.通过依赖注入(dependency injection),Angular为客户端的Web应用带来了传统服务端的服务,例如独立于视图的控制。 因此,后端减少了许多负担,产生了更轻的Web应用。
Model:数据,其实就是angular变量($scope.XX);
View: 数据的呈现,Html+Directive(指令);
Controller: 操作数据,就是function,数据的增删改查
2. 双向绑定
AngularJS是建立在这样的信念上的:即声明式编程应该用于构建用户界面以及编写软件构建,而指令式编程非常适合来表示业务逻辑。框架采用并扩展了传统HTML,通过双向的数据绑定来适应动态内容,双向的数据绑定允许模型和视图之间的自动同步。因此,AngularJS使得对DOM的操作不再重要并提升了可测试性。
3. 依赖注入
依赖注入(Dependency Injection,简称DI)是一种设计模式, 指某个对象依赖的其他对象无需手工创建,只需要“吼一嗓子”,则此对象在创建时,其依赖的对象由框架来自动创建并注入进来,其实就是最少知识法则;模块中所有的service和provider两类对象,都可以根据形参名称实现DI.
4. 模块化设计
高内聚低耦合法则
- 官方提供的模块 ng、ngRoute、ngAnimate
- 用户自定义的模块 angular.module(‘模块名’,[ ])
2. 入门小demo
1. 表达式
<!DOCTYPE html>
<html>
<head>
<title>angularJS入门小demo-表达式</title>
<script src="angular.min.js"></script>
</head>
<body ng-app>
{{100+100}}
</body>
</html>
2. 双向绑定
<!DOCTYPE html>
<html>
<head>
<title>angularJS入门小demo02-双向绑定</title>
<script src="angular.min.js"></script>
</head>
<body ng-app>
请输入姓名:<input ng-model="name">
<input ng-model="name">
{{name}}
</body>
</html>
3. 初始化指令
<!DOCTYPE html>
<html>
<head>
<title>angularJS入门小demo03-初始化指令</title>
<script src="angular.min.js"></script>
</head>
<body ng-app ng-init="name='梁静茹'">
请输入姓名:<input ng-model="name">
<input ng-model="name">
{{name}}
</body>
</html>
也可以调用方法
4. 控制器
<!DOCTYPE html>
<html>
<head>
<title>angularJS入门小demo04-控制器</title>
<script src="angular.min.js"></script>
<script>
// 建立模块
var app = angular.module("myApp",[]);
// 创建控制器 $scope就是控制层与视图层之间交换数据的桥梁
app.controller("myController",function($scope){
$scope.add=function(){
return parseInt($scope.x)+parseInt($scope.y);
}
});
</script>
</head>
<body ng-app="myApp" ng-controller="myController">
第一个数:<input ng-model="x">
第二个数:<input ng-model="y">
{{add()}}
</body>
</html>
5. 事件指令
<!DOCTYPE html>
<html>
<head>
<title>angularJS入门小demo05-事件指令</title>
<script src="angular.min.js"></script>
<script>
// 建立模块
var app = angular.module("myApp",[]);
// 创建控制器 $scope就是控制层与视图层之间交换数据的桥梁
app.controller("myController",function($scope){
$scope.add=function(){
$scope.z = parseInt($scope.x)+parseInt($scope.y);
}
});
</script>
</head>
<body ng-app="myApp" ng-controller="myController">
第一个数:<input ng-model="x">
第二个数:<input ng-model="y">
<br/>
<button ng-click="add()">运算</button>
<br/>
运算结果:{{z}}
</body>
</html>
6. 循环数组
<!DOCTYPE html>
<html>
<head>
<title>angularJS入门小demo06-循环数组</title>
<script src="angular.min.js"></script>
<script>
// 建立模块
var app = angular.module("myApp",[]);
// 创建控制器 $scope就是控制层与视图层之间交换数据的桥梁
app.controller("myController",function($scope){
$scope.list = [1,2,3,4,55];
});
</script>
</head>
<body ng-app="myApp" ng-controller="myController">
<table>
<tr ng-repeat="x in list">
<td>{{x}}</td>
</tr>
</table>
</body>
</html>
7.http请求
<!DOCTYPE html>
<html>
<head>
<title>angularJS入门小demo08-内置服务$http</title>
<script src="angular.min.js"></script>
<script>
// 建立模块
var app = angular.module("myApp",[]);
// 创建控制器 $scope就是控制层与视图层之间交换数据的桥梁
app.controller("myController",function($scope,$http){
$scope.findList = function () {
$http.get("data.json").success(
function (response) {
$scope.list = response;
}
);
}
});
</script>
</head>
<body ng-app="myApp" ng-controller="myController" ng-init="findList()">
<table>
<tr>
<td>姓名</td>
<td>数学</td>
<td>语文</td>
</tr>
<tr ng-repeat="entity in list">
<td>{{entity.name}}</td>
<td>{{entity.shuxue}}</td>
<td>{{entity.yuwen}}</td>
</tr>
</table>
</body>
</html>
2. 代码
1. 未分页
brand.html
<script src="../plugins/angularjs/angular.min.js"></script>
<script type="text/javascript">
var app = angular.module('pinyougou',[]);
app.controller('brandController',function ($scope, $http) {
// 查询品牌列表
$scope.findAll = function () {
$http.get('../brand/findAll.do').success(
function (response) {
$scope.list = response;
}
);
}
});
</script>
<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="brandController" ng-init="findAll()">
...
<tbody>
<tr ng-repeat="entity in list">
<td><input type="checkbox" ></td>
<td>{{entity.id}}</td>
<td>{{entity.name}}</td>
<td>{{entity.firstChar}}</td>
<td class="text-center">
<button type="button" class="btn bg-olive btn-xs" data-toggle="modal" data-target="#editModal" >修改</button>
</td>
</tr>
</tbody>
2. 分页
品牌分页:
前端给后端:当前页,每页记录数;
后端给前端:总记录数,当前页记录。
思路:
后端应该显示什么格式呢?
json,总记录数,当前页记录
{total:100,rows:[]}
方法1 :
Map map = new HashMap();
map.put('total',100);
map.put('rows',list);
return map;
方法2:
创建类,包含total和rows属性
1. 实体类
和具体业务无关,entity
public class PageResult implements Serializable {
private Long total;//总记录数
private List rows;// 当前页结果
public PageResult(Long total, List rows) {
this.total = total;
this.rows = rows;
}
//get set方法
2. 品牌分页的接口
public interface BrandService {
PageResult findPage(int pageNum,int pageSize);
}
3. 实现类
用到了pageHelper插件
@Service
public class BrandServiceImpl implements BrandService {
@Override
public PageResult findPage(int pageNum, int pageSize) {
PageHelper.startPage(pageNum,pageSize);
Page<TbBrand> page = (Page<TbBrand>) brandMapper.selectByExample(null);
// 封装到PageResult中
return new PageResult(page.getTotal(),page.getResult());
}
}
4. 商家controller
@RestController
@RequestMapping("/brand")
public class BrandController {
@RequestMapping("/findPage")
public PageResult findPage(int page,int size){
return brandService.findPage(page,size);
}
}
5. 分页的前端实现
在body的table下边数据列表部分增加一行
<!--数据列表/-->
<tm-pagination conf="paginationConf"></tm-pagination>
head里引入分页的js和css
<!--分页组件开始-->
<script src="../plugins/angularjs/pagination.js"></script>
<link rel="stylesheet" href="../plugins/angularjs/pagination.css">
<!--分页组件结束-->
在script里增加分页的功能
<script type="text/javascript">
var app = angular.module('pinyougou',['pagination']);
app.controller('brandController',function ($scope, $http) {
// 查询品牌列表
$scope.findAll = function () {
$http.get('../brand/findAll.do').success(
function (response) {
$scope.list = response;
}
);
}
// 分页控件配置 currentPage当前页 totalItems 总记录数 itemsPerPage 每页记录数
// perPageOptions分页选项 onChange 当页码变更后自动触发的方法
$scope.paginationConf = {
currentPage:1,
totalItems:10,
itemsPerPage:10,
perPageOptions:[10,20,30,40,50],
onChange:function () {
$scope.reloadList();
}
};
// 刷新列表,之后会复用,提取为方法
$scope.reloadList = function(){
$scope.findPage($scope.paginationConf.currentPage, $scope.paginationConf.itemsPerPage);
};
$scope.findPage = function (page, size) {
$http.get('../brand/findPage.do?page='+page+'&size='+size).success(
function (response) {
$scope.list = response.rows;//显示当前页数据
$scope.paginationConf.totalItems = response.total;//更新总记录数
}
)
}
});
</script>
6. 品牌新增
1. 新建实体类
用于封装向前端返回的结果,成功与否。
public class Result implements Serializable {
private boolean success; // 成功与否
private String message; // 传输的具体信息
public Result(boolean success, String message) {
this.success = success;
this.message = message;
}
// ...
2. 接口
public interface BrandService {
// 增加品牌
void addBrand(TbBrand brand);
}
3. 实现类
@Service
public class BrandServiceImpl implements BrandService {
@Override
public void addBrand(TbBrand brand) {
brandMapper.insert(brand);
}
}
4. 商家的controller
@RequestMapping("/add")
public Result addBrand(@RequestBody TbBrand brand){
try {
brandService.addBrand(brand);
return new Result(true,"增加品牌成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"增加品牌失败");
}
}
5. 前端
方法
// 新增品牌
$scope.add = function () {
$http.post('../brand/add.do',$scope.entity).success(
function (response) {
if(response.success){
$scope.reloadList();// 刷新
}else{
alert(response.message);
}
}
);
}
body的编辑窗口
<tr>
<td>品牌名称</td>
<td><input class="form-control" placeholder="品牌名称" ng-model="entity.name"> </td>
</tr>
<tr>
<td>首字母</td>
<td><input class="form-control" placeholder="首字母" ng-model="entity.firstChar"> </td>
// 保存按钮
<button class="btn btn-success" data-dismiss="modal" aria-hidden="true" ng-click="add()">保存</button>
小瑕疵:每次新建都能看到前一次,不爽,如何修改?
修改新建按钮
<button type="button" class="btn btn-default" title="新建" data-toggle="modal" data-target="#editModal" ng-click="entity={}"><i class="fa fa-file-o" ></i> 新建</button>
7. 品牌修改
其实分两步:根据id查找和更新
1. 接口
// 根据id查询
TbBrand findOne(Long id);
// 更新品牌
void updateBrand(TbBrand brand);
2. 实现类
@Override
public TbBrand findOne(Long id) {
return brandMapper.selectByPrimaryKey(id);
}
@Override
public void updateBrand(TbBrand brand) {
brandMapper.updateByPrimaryKey(brand);
}
3. 商家的controller
@RequestMapping("/findOne")
public TbBrand findOne(Long id){
return brandService.findOne(id);
}
@RequestMapping("/update")
public Result updateBrand(@RequestBody TbBrand brand){
try {
brandService.updateBrand(brand);
return new Result(true,"品牌更新成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"品牌更新失败");
}
}
4. 前端
点击修改按钮,弹出的框和增加的框一样,然后修改,然后保存
<button type="button" class="btn bg-olive btn-xs" data-toggle="modal" data-target="#editModal" ng-click="findOne(entity.id)" >修改</button>
方法
// 根据id查询品牌
$scope.findOne = function (id) {
$http.get('../brand/findOne.do?id='+id).success(
function (response) {
$scope.entity = response;
}
);
};
编辑窗口的保存
<button class="btn btn-success" data-dismiss="modal" aria-hidden="true" ng-click="save()">保存</button>
方法
借用add方法
$scope.save = function () {
var methodName = 'add'; //方法名
if($scope.entity.id!=null){
methodName = 'update';
}
$http.post('../brand/'+methodName+'.do',$scope.entity).success(
function (response) {
if(response.success){
$scope.reloadList();// 刷新
}else{
alert(response.message);
}
}
);
}
8. 删除品牌
本项目采用复式删除,通过复选框删除
1. 接口
// 批量删除品牌
void deleteBrands(Long[] ids);
2. 实现类
@Override
public void deleteBrands(Long[] ids) {
for (Long id : ids) {
brandMapper.deleteByPrimaryKey(id);
}
}
3. 商家的controller
@RequestMapping("/delete")
public Result deleteBrands(Long[] ids){
try {
brandService.deleteBrands(ids);
return new Result(true,"品牌删除成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"品牌删除失败");
}
}
4. 前端
需要定义一个集合存储勾选的复选框的id
方法
包含push添加,js原生的indexOf和splice
$scope.selectIds = [];// 用户勾选的ID集合
$scope.updateSelection = function ($event,id) {
if($event.target.checked){
$scope.selectIds.push(id);//push向集合中添加元素
}else{
// 删除元素
var index = $scope.selectIds.indexOf(id);//查找值的位置
$scope.selectIds.splice(index,1);// 参数1:值的位置 参数2:移除的个数
}
}
body
<td><input type="checkbox" ng-click="updateSelection($event,entity.id)"></td>
<td>{{entity.id}}</td>
<td>{{entity.name}}</td>
<td>{{entity.firstChar}}</td>
最好在后边添加个{{selectIds}}
删除
// 删除
$scope.deleteSelection = function () {
$http.get('../brand/delete.do?ids='+$scope.selectIds).success(
function (response) {
if(response.success){
$scope.reloadList();//刷新
}else{
alert(response.message);
}
}
);
}
body
<button type="button" class="btn btn-default" title="删除" ng-click="deleteSelection()"><i class="fa fa-trash-o"></i> 删除</button>
9.条件查询
其实跟分页查询所有一样,只是
TODO 后端碰到问题,未解决!
后端问题为:invalid constant type,修改依赖的javassist版本即可。
1. 接口
// 条件查询
PageResult findPage(TbBrand brand, int pageNum,int pageSize);
2. 实现类
@Override
public PageResult findPage(TbBrand brand, int pageNum, int pageSize) {
PageHelper.startPage(pageNum,pageSize);
TbBrandExample example = new TbBrandExample();
Criteria criteria = example.createCriteria();
if(brand!=null){
if(brand.getName()!=null && brand.getName().length()>0){
criteria.andNameLike("%"+brand.getName()+"%");
}
if(brand.getFirstChar()!=null && brand.getFirstChar().length()>0){
criteria.andFirstCharLike("%"+brand.getFirstChar()+"%");
}
}
Page<TbBrand> page = (Page<TbBrand>) brandMapper.selectByExample(example);
// 封装到PageResult中
return new PageResult(page.getTotal(),page.getResult());
}
3. controller
@RequestMapping("/search")
public PageResult search(@RequestBody TbBrand brand,int page,int size){
return brandService.findPage(brand,page,size);
}
4. 前端
需要增加搜索框
<div class="box-tools pull-right">
<div class="has-feedback">
品牌名称:<input ng-model="searchEntity.name"> 品牌首字母:<input ng-model="searchEntity.firstChar"> <button class="btn btn-default" ng-click="reloadList()">查询</button>
</div>
方法
$scope.searchEntity={};
// 条件查询
$scope.search = function (page,size) {
// 混合提交
$http.post('../brand/search.do?page='+page+'&size='+size,$scope.searchEntity).success(
function (response) {
$scope.list = response.rows;//显示当前页数据
$scope.paginationConf.totalItems = response.total;//更新总记录数
}
);
};
修改reload
// 刷新列表
$scope.reloadList = function(){
$scope.search($scope.paginationConf.currentPage,$scope.paginationConf.itemsPerPage);
};