目标
- 理解和运用angularJS的service
- 理解和运用控制器继承
- 掌握代码生成器的使用
- 实现规格管理
- 实现模板管理
1. 前端分层开发
1. 前端服务层的抽取
服务层就是和后端进行交互的部分,是一个函数或对象。如内置服务$http
,在不同的控制器中调用同一个服务,提高代码复用性,便于维护。
修改品牌管理代码
var app = angular.module('pinyougou',['pagination']);//定义品优购模块
// 品牌服务
app.service("brandService",function ($http) {
this.findAll = function () {
return $http.get('../brand/findAll.do');
};
this.findPage = function (page,size) {
return $http.get('../brand/findPage.do?page='+page+'&size='+size);
};
this.findOne = function (id) {
return $http.get('../brand/findOne.do?id='+id);
};
this.add = function (entity) {
return $http.post('../brand/add.do',entity);
};
this.update = function (entity) {
return $http.post('../brand/update.do',entity);
};
this.deleteSelection = function (ids) {
return $http.get('../brand/delete.do?ids='+ids);
};
this.search = function (page,size,searchEntity) {
return $http.post('../brand/search.do?page='+page+'&size='+size,searchEntity)
}
});
controller
记得参数加上brandService,save方法修改
app.controller('brandController',function ($scope, $http,brandService) {
// 查询品牌列表
$scope.findAll = function () {
brandService.findAll().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.search($scope.paginationConf.currentPage, $scope.paginationConf.itemsPerPage);
};
// 分页
$scope.findPage = function (page, size) {
brandService.findPage(page, size).success(
function (response) {
$scope.list = response.rows;//显示当前页数据
$scope.paginationConf.totalItems = response.total;//更新总记录数
}
)
};
// 根据id查询品牌
$scope.findOne = function (id) {
brandService.findOne(id).success(
function (response) {
$scope.entity = response;
}
);
};
// 保存
$scope.save = function () {
var object = null;
if($scope.entity.id!=null){
object = brandService.update($scope.entity);
}else{
object = brandService.add($scope.entity);
}
object.success(
function (response) {
if(response.success){
$scope.reloadList();// 刷新
}else{
alert(response.message);
}
}
);
};
$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:移除的个数
}
};
// 删除
$scope.deleteSelection = function () {
brandService.deleteSelection($scope.selectIds).success(
function (response) {
if(response.success){
$scope.reloadList();//刷新
}else{
alert(response.message);
}
}
);
};
$scope.searchEntity={};
// 条件查询
$scope.search = function (page,size) {
// 混合提交
brandService.search(page,size,$scope.searchEntity).success(
function (response) {
$scope.list = response.rows;//显示当前页数据
$scope.paginationConf.totalItems = response.total;//更新总记录数
}
);
};
});
2. 代码分离
将script下的代码分离,主要是service和controller
最终效果
<script type="text/javascript" src="../js/base_pagination.js"></script>
<script type="text/javascript" src="../js/service/brandService.js"></script>
<script type="text/javascript" src="../js/controller/brandController.js"></script>
3. 控制器继承
baseController
app.controller('baseController',function ($scope) {
// 分页控件配置 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.search($scope.paginationConf.currentPage, $scope.paginationConf.itemsPerPage);
};
// 分页
$scope.findPage = function (page, size) {
brandService.findPage(page, size).success(
function (response) {
$scope.list = response.rows;//显示当前页数据
$scope.paginationConf.totalItems = response.total;//更新总记录数
}
)
};
$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:移除的个数
}
};
});
brandController
app.controller('brandController',function ($scope, $controller,$http,brandService) {
$controller('baseController',{$scope:$scope});
//...
brand.html
<script type="text/javascript" src="../js/base_pagination.js"></script>
<script type="text/javascript" src="../js/service/brandService.js"></script>
<script type="text/javascript" src="../js/controller/brandController.js"></script>
<script type="text/javascript" src="../js/controller/baseController.js"></script>
继承为伪继承,scope传递
4. 代码生成
使用代码生成器,生成后,拷贝商家相关。
SellerService修改主键的类型为String。
2. 规格管理
1. 需求和表结构的分析
两个表 tb_specification和tb_specification_option
2. 规格列表
1. 引入JS
修改specification.html
<script src="../plugins/angularjs/angular.min.js"></script>
<!--分页组件开始-->
<script src="../plugins/angularjs/pagination.js"></script>
<link rel="stylesheet" href="../plugins/angularjs/pagination.css">
<!--分页组件结束-->
<script type="text/javascript" src="../js/base_pagination.js"></script>
<script type="text/javascript" src="../js/service/specificationService.js"></script>
<script type="text/javascript" src="../js/controller/specificationController.js"></script>
<script type="text/javascript" src="../js/controller/baseController.js"></script>
放置分页组件
<tm-pagination conf="paginationConf"></tm-pagination>
指令和表达式
在body元素指定模块名和控制器名
<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="specificationController" >
循环表格行
<tbody>
<tr ng-repeat="entity in list">
<td><input type="checkbox" ></td>
<td>{{entity.id}}</td>
<td>{{entity.specName}}</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. 新增规格
1. 新增行的实现
修改specificationController.js
// 增加规格选项行
$scope.addTableRow = function () {
$scope.entity.specificationOptionList.push({});
};
specification.html "新建选项"按钮
<button type="button" class="btn btn-default" title="新建" data-toggle="modal" data-target="#editModal" ng-click="entity={'specificationOptionList':[]}"><i class="fa fa-file-o"></i> 新建</button>
循环列表行,绑定表格内的编辑框
<tbody>
<tr ng-repeat="pojo in entity.specificationOptionList">
<td>
<input class="form-control" placeholder="规格选项" ng-model="pojo.optionName">
</td>
<td>
<input class="form-control" placeholder="排序" ng-model="pojo.orders">
</td>
<td>
<button type="button" class="btn btn-default" title="删除" ng-click="deleTableRow($index)"><i class="fa fa-trash-o"></i> 删除</button>
</td>
</tr>
</tbody>
注意:新建时,弹出窗口时对entity初始化,否则添加数据会报错
3. 删除行的实现
还是批量删除
specificationController.js
// 删除规格选项行
$scope.deleTableRow = function (index) {
$scope.entity.specificationOptionList.splice(index,1);
}
删除按钮见上上段代码,记得参数为$index
该指令可以获取循环中的索引。
4. 保存
思路:将规格和规格选项参数数据合并为一个对象来传递。
1. pojo中新建个pojogroup包,里面Specification
public class Specification implements Serializable {
private TbSpecification specification;
private List<TbSpecificationOption> specificationOptionList;
// ...
2. dao的修改
要新增规格选项,需要知道新增规格的id,修改TbSpecificationMapper.xml,在insert节点后添加配置
<insert id="insert" parameterType="com.pinyougou.pojo.TbSpecification" >
<selectKey resultType="java.lang.Long" order="AFTER" keyProperty="id">
SELECT LAST_INSERT_ID() AS id
</selectKey>
insert into tb_specification (id, spec_name)
...
3. 修改interface
public interface SpecificationService {
// ...
public void add(Specification specification);
}
4. 修改service
@Service
public class SpecificationServiceImpl implements SpecificationService {
@Autowired
private TbSpecificationMapper specificationMapper;
@Autowired
private TbSpecificationOptionMapper specificationOptionMapper;
// ...
@Override
public void add(Specification specification) {
TbSpecification tbSpecification = specification.getSpecification();
specificationMapper.insert(tbSpecification);
List<TbSpecificationOption> specificationOptionList = specification.getSpecificationOptionList();
for (TbSpecificationOption option : specificationOptionList) {
option.setSpecId(tbSpecification.getId());//设置规格ID
specificationOptionMapper.insert(option);//新增规格
}
}
}
5. 修改controller
@RequestMapping("/add")
public Result add(@RequestBody Specification specification){
try {
specificationService.add(specification);
//...
6. 修改页面specification.html
绑定规格名称
<tr>
<td>规格名称</td>
<td><input class="form-control" placeholder="规格名称" ng-model="entity.specification.specName"> </td>
</tr>
绑定保存按钮
<button class="btn btn-success" data-dismiss="modal" aria-hidden="true" ng-click="save()">保存</button>
7. 删除规格
较为简单,记得删除规格的同时也要删除关联的规格选项
修改service
@Override
public void delete(Long[] ids) {
for(Long id:ids){
// 删除规格
specificationMapper.deleteByPrimaryKey(id);
// 删除规格选项
TbSpecificationOptionExample example = new TbSpecificationOptionExample();
TbSpecificationOptionExample.Criteria criteria = example.createCriteria();
criteria.andSpecIdEqualTo(id);
specificationOptionMapper.deleteByExample(example);
}
}
前端注意两点
删除按钮
<button type="button" class="btn btn-default" title="删除" ng-click="dele()"><i class="fa fa-trash-o"></i> 删除</button>
复选框
<tr ng-repeat="entity in list" ng-click="updateSelection($event,entity.id)">
<td><input type="checkbox" ></td>
<td>{{entity.id}}</td>
<td>{{entity.specName}}</td>
3. 模板管理
1. 需求和表结构分析
模板主要有两个:1是用于关联品牌和规格;2是定义扩展属性
2. 模板列表
修改type_template.html,引入js
<script src="../plugins/angularjs/angular.min.js"></script>
<!--分页组件开始-->
<script src="../plugins/angularjs/pagination.js"></script>
<link rel="stylesheet" href="../plugins/angularjs/pagination.css">
<!--分页组件结束-->
<script type="text/javascript" src="../js/base_pagination.js"></script>
<script type="text/javascript" src="../js/service/typeTemplateService.js"></script>
<script type="text/javascript" src="../js/controller/typeTemplateController.js"></script>
<script type="text/javascript" src="../js/controller/baseController.js"></script>
放置分页组件
<!--分页-->
<tm-pagination conf="paginationConf"></tm-pagination>
指令和表达式
<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="typeTemplateController">
...
<tbody>
<tr ng-repeat="entity in list">
<td><input type="checkbox"></td>
<td>{{entity.id}}</td>
<td>{{entity.name}}</td>
<td>{{entity.brandIds}}</td>
<td>{{entity.specIds}}</td>
<td>{{entity.customAttributeItems}}</td>
3. 品牌下拉列表
使用select2组件完成
要求品牌可以多选
1. 静态品牌下拉列表
- 修改type_template.html 引入js
<link rel="stylesheet" href="../plugins/select2/select2.css" />
<link rel="stylesheet" href="../plugins/select2/select2-bootstrap.css" />
<script src="../plugins/select2/select2.min.js" type="text/javascript"></script>
<script src="../plugins/angularjs/angular.min.js"></script>
<!--分页组件开始-->
<script src="../plugins/angularjs/pagination.js"></script>
<link rel="stylesheet" href="../plugins/angularjs/pagination.css">
<!--分页组件结束-->
<script type="text/javascript" src="../js/base_pagination.js"></script>
<script type="text/javascript" src="../js/angular-select2.js"></script>
<script type="text/javascript" src="../js/service/typeTemplateService.js"></script>
<script type="text/javascript" src="../js/service/brandService.js"></script>
<script type="text/javascript" src="../js/service/specificationService.js"></script>
<script type="text/javascript" src="../js/controller/baseController.js"></script>
<script type="text/javascript" src="../js/controller/typeTemplateController.js"></script>
注意angular-select2的位置在base_pagination之后
- 修改typeTemplateController.js,定义品牌列表数据
$scope.brandList = {data:[{id:1,text:'华为'},{id:2,text:'小米'},{id:3,text:'苹果'}]};
- 在type_template.html中,用select2组件实现多选下拉框
<td>关联品牌</td>
<td>
<input select2 select2-model="entity.brandIds" config="brandList" multiple placeholder="选择品牌(可多选)"
class="form-control" type="text" />
其中 multiple表示可多选;config配置数据来源,select2-model用于指定用户选择后提交的变量。
2. 后端数据支撑
让数据从数据库中提取。
1. pinyougou.dao
在TbBrandMapper.xml中添加SQL语句配置
<select id="selectOptionList" resultType="java.util.Map">
select id,name as text from tb_brand
</select>
在TbBrandMapper中添加方法定义
List<Map> selectOptionList();
2. pinyougou-sellergoods-interface
BrandService增加方法定义
// 复选下拉框的实现
List<Map> selectOptionList();
3. service
@Override
public List<Map> selectOptionList() {
return brandMapper.selectOptionList();
}
4. cotroller
@RequestMapping("/selectOptionList")
public List<Map> selectOptionList(){
return brandService.selectOptionList();
}
5. 测试
通过
3. 前端修改
1. 修改brandService.js
增加方法
this.selectOptionList = function () {
return $http.post('../brand/selectOptionList.do');
}
2. 修改typeTemplateController.js
使用服务层的方法,添加依赖注入
//控制层
app.controller('typeTemplateController' ,function($scope,$controller ,typeTemplateService, brandService,specificationService){...}
使用服务方法实现查询,结果赋值给变量
// 多选下拉框
// $scope.brandList = {data:[{id:1,text:'华为'},{id:2,text:'小米'},{id:3,text:'苹果'}]};
$scope.brandList = {data:[]};
// 读取品牌列表
$scope.findBrandList = function () {
brandService.selectOptionList().success(
function (response) {
$scope.brandList = {data:response};
}
);
}
3. 修改type_template.html
引入brandService.js
body里面 添加初始化指令
<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="typeTemplateController" ng-init="findBrandList();findSpecList()">
4. 新增模板
修改type_template.html,绑定文本框
<td>商品类型</td>
<td><input class="form-control" placeholder="商品类型" ng-model="entity.name"> </td>
保存按钮
<button class="btn btn-success" data-dismiss="modal" aria-hidden="true" ng-click="save()">保存</button>
5. 修改模板
修改typeTemplateController.js的findOne方法
从数据库中查询的到的是字符串,必须转化为json对象才能实现信息的回显
//查询实体
$scope.findOne=function(id){
typeTemplateService.findOne(id).success(
function(response){
$scope.entity= response;
// 转换字符串为json
$scope.entity.brandIds = JSON.parse($scope.entity.brandIds);
$scope.entity.specIds = JSON.parse($scope.entity.specIds);
$scope.entity.customAttributeItems = JSON.parse($scope.entity.customAttributeItems);
}
);
}
6. 删除模板
修改type_template.html
表格中的复选框
<tr ng-repeat="entity in list">
<td><input type="checkbox" ng-click="updateSelection($event,entity.id)"></td>
<td>{{entity.id}}</td>
<td>{{entity.name}}</td>
<td>{{entity.brandIds}}</td>
删除按钮
<button type="button" class="btn btn-default" title="删除" ng-click="dele()"><i class="fa fa-trash-o"></i> 删除</button>
7. 优化模板列表的显示
现在显示的是json格式,不利于用户的查询
需要显示text内容
将方法写到baseController.js中
$scope.jsonToString=function(jsonString,key){
var json= JSON.parse(jsonString);
var value="";
for(var i=0;i<json.length;i++){
if(i>0){
value+=",";
}
value +=json[i][key];
}
return value;
}
页面使用该函数转换
<tr ng-repeat="entity in list">
<td><input type="checkbox" ng-click="updateSelection($event,entity.id)"></td>
<td>{{entity.id}}</td>
<td>{{entity.name}}</td>
<td>{{entity.brandIds}}</td>
<td>{{jsonToString(entity.specIds,'text')}}</td>
<td>{{jsonToString(entity.customAttributeItems,'text')}}</td>