在正式实现首页和分页功能之前,我们首先思考两个问题:
1.index.jsp 页面动态数据怎么获取? 2.如果用 AJAX 获取,点击下一页的时候又要怎么办?
前期准备
引入 index.jsp 文件,将原 index.jsp 文件替换。
启动 Tomcat 后访问,效果如下:
因为页面都是死数据,无法完成分页功能,所以先导入文章的数据 SQL,SQL 文件会放入文末的百度网盘链接中。
说明:因为 user_content
表存在外键关系,所以要先解除外键约束,否则无法删除和修改表:
SET FOREIGN_KEY_CHECKS=0; #解除外键约束
等插入数据之后,再设置外键约束:
SET FOREIGN_KEY_CHECKS=1;#设置外键约束
导入 SQL 文件和之前一样,如图:
再将 image 文件夹拷贝到 dreamland\dreamland-web\target\dreamland-web\images\
目录下,主要是网页用到的网页图片,image 文件夹也在文末的百度网盘链接中。
index 页面
右上角的登录、注册
index.jsp 页面逻辑判断代码,如下所示:
<div style="position: absolute;margin-left: 980px;margin-top: -40px;">
<c:if test="${empty user}">
<a name="tj_login" class="lb" href="login?error=login" style="color: black">[登录]</a>
<a name="tj_login" class="lb" href="register" style="color: black">[注册]</a>
</c:if>
<c:if test="${not empty user}">
<a name="tj_loginp" href="void(0);" class="lb" onclick="personal('${user.id}');" style="color: black"><font color="#9370db">${user.nickName}, 欢迎您!</font></a>
<a name="tj_login" class="lb" href="${ctx}/loginout" style="color: black">[退出]</a>
</c:if>
</div>
通过 EL 表达式判断:
- 如果用户为空,则显示“登录”、“注册”;
- 如果不为空,则显示“xxx,欢迎您!”和“退出”。
引入天气预报
只需将最底下的 div,如下:
<div class="col-md-3" style="background-color: #C6E2FF;position:absolute;top:0px;left: 873px;width: 268px">
<h2>Sidebar</h2>
<ul class="nav nav-tabs nav-stacked">
<li><a href='#'>Another Link 1</a></li>
<li><a href='#'>Another Link 2</a></li>
<li><a href='#'>Another Link 3</a></li>
</ul>
</div>
替换成下面的代码,即可:
<div class="col-md-3" style="position:absolute;top:0px;left: 880px;width: 268px;">
<div style="background-color: white;width: 250px;height: 440px">
<iframe name="weather_inc" src="http://i.tianqi.com/index.php?c=code&id=82" width="250" height="440" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div>
</div>
这个只是简单的引用,且必须联网才能使用。效果如下:
关于 index 页面的数据获取
首先,通过 AJAX 请求获取数据,JavaScript 代码大概是这样的(看看就好,不建议):
<script language=javascript>
$.ajax({
type: "post",
dataType: "json",
url: '${ctx}/index',
success: function (data) {
var Data=data;
var content = null;
var page =null;
for(var key in Data) {
if(key == "page"){
page = Data[key];
}
if(key == "content"){
content = Data[key];
}
}
if (data!=null && data != "" ) {
$(content).each(function () {
var ll = this.title;
// alert(ll);
// alert(0);
$("#content_col").append("<div class='content-text' ><div class='author clearfix'><div>"
+"<a href='#' target='_blank' rel='nofollow' style='height: 35px' οnclick=''>"
+"<img src='"+this.imgUrl+"'></a></div><a href='' target='_blank' οnclick=''>"
+"<h2 class='author-h2'>"+this.nickName+"</h2></a></div>"
+"<h2>"+this.title+"</h2>"+this.content+
"<div style='height: 5px'></div><div class='stats'><span class='stats-vote'><i class='number'>"+this.upvote+"</i> 赞</span>"
+"<span class='stats-comments'><span class='dash'> · </span>"
+"<a href='#' class='comments' target='_blank' οnclick=''>"
+"<i class='number'>"+this.commentNum+"</i> 评论 </a></span> </div><div style='height: 5px'></div>"
+"<div class='stats-buttons bar clearfix'><a><i class='icon icon-thumbs-o-up icon-2x'></i><span class='number hidden'>"+this.upvote+"</span></a>"
+" <a><i class='icon icon-thumbs-o-down icon-2x'></i><span class='number hidden'>"+this.downvote+"</span></a> <a><i class='icon icon-comment-alt icon-2x'></i></a>"
+"</div><div class='single-share'><a class='share-wechat' data-type='wechat' title='分享到微信' rel='nofollow' style='margin-left:18px;color: grey'>"
+"<i class='icon icon-wechat icon-2x'></i></a><a class='share-qq' data-type='qq' title='分享到QQ' rel='nofollow' style='margin-left:18px;color: grey'>"
+"<i class='icon icon-qq icon-2x'></i> </a><a class='share-weibo' data-type='weibo' title='分享到微博' rel='nofollow' style='margin-left:18px;color: grey'>"
+"<i class='icon icon-weibo icon-2x'></i></a></div><br/> <div class='single-clear'></div></div>"
+"<div style='position: absolute;width:900px;background-color: #EBEBEB;height: 10px;left: 0px'></div>"
);
});
var cnt = "<ul class='pager pager-loose'>";
if(page.pageNum <= 1){
cnt += "<li><a href='void(0);'>« 上一页</a></li>";
}else {
var pNum = page.pageNum-1;
cnt += "<li class='previous'><a href='${ctx}/index_list?pageNum="+pNum+"&&id=${user.id}'>« 上一页</a></li>";
}
for(var i=1;i<=page.pages;i++){
if(page.pageNum == i){
cnt += "<li class='active'><a href='void(0);'>"+i+"</a></li>";
}else{
cnt += "<li ><a href='${ctx}/index_list?pageNum="+i+"&&id=${user.id}'>"+i+"</a></li>";
}
}
if(page.pageNum >= page.pages){
cnt += "<li><a href='void(0);'>下一页 »</a></li>";
}else {
var pNum = page.pageNum+1;
cnt += "<li><a href='${ctx}/index_list?pageNum="+pNum+"&&id=${user.id}'>下一页 »</a></li></ul>";
}
//分页
$("#page-info").append(cnt);
}
}
});
</script>
主要是通过 AJAX 获取数据后再添加到 div 中,这样只是解决了 index 页面的首次正常访问,有动态数据,但当点击下一页的时候就会产生新问题。这时可以新建一个和 index.jsp 一样的页面 index2.jsp,只不过这次不是用 AJAX 从后台获取数据,而是通过点击下一页或者其他页码,先经过 Controller,将分页数据封装后返回到 index2.jsp。这样做费时费力还耗资源,所以也不可取。
推荐的解决办法是:在进入 index.jsp 之前进行过滤器拦截,先获取页面数据,之后再返回 index.jsp 页面。
实现步骤如下:
(1)在 web.xml 中,引入自定义过滤器 filter:
<!--自定义过滤器-->
<filter>
<filter-name>dispatcherDemoFilter</filter-name>
<filter-class>wang.dreamland.www.interceptor.IndexJspFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>dispatcherDemoFilter</filter-name>
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
在访问 index.jsp 之前进入自定义过滤器 dispatcherDemoFilter,通过该名字找到自定义过滤器具体路径 wang.dreamland.www.interceptor.IndexJspFilter
。
(2)在 wang.dreamland.www 下新建包 interceptor,在 interceptor 包下新建 IndexJspFilter.java:
public class IndexJspFilter implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("===========自定义过滤器==========");
ServletContext context = request.getServletContext();
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
UserContentMapper userContentMapper = ctx.getBean(UserContentMapper.class);
PageHelper.startPage(null, null);//开始分页
List<UserContent> list = userContentMapper.select( null );
PageHelper.Page endPage = PageHelper.endPage();//分页结束
request.setAttribute("page", endPage );
chain.doFilter(request, response);
}
public void destroy() {
}
}
代码解读如下:
filter 初始化时,注解的 bean 还没初始化,加 @Autowired
注解不会起作用,所以通过 ApplicationContext 手动获取 UserContentMapper 对象。
UserContentMapper userContentMapper = ctx.getBean(UserContentMapper.class);
在开始分页和结束分页之间查询数据,PageHelper 会对分页开始之后的第一个查询语句进行分页,封装在 Page 对象中。
PageHelper.startPage(null, null);//开始分页
List<UserContent> list = userContentMapper.select( null );
PageHelper.Page endPage = PageHelper.endPage();//分页结束
pageNum 如果为 Null 则默认为第一页,pageSize 为 null 则默认每页显示7条数据,在 PageHelper 中有设默认值:
public Page(Integer pageNum, Integer pageSize) {
if (pageNum == null || pageNum < 1) {
pageNum = 1;
}
if (pageSize == null || pageSize < 1) {
pageSize = 7;
}
this.pageNum = pageNum;
this.pageSize = pageSize;
}
然后将 Page 对象放在 request 域中,前台可通过 EL 表达式 ${page}
获取:
request.setAttribute("page", endPage );
(3)index.jsp 页面获取数据的代码如下:
<c:forEach var="cont" items="${page.result}" varStatus="i">
<!-- 正文开始 -->
<div class="content-text">
<div class="author clearfix">
<div>
<a href="#" target="_blank" rel="nofollow" style="height: 35px">
<img src="${cont.imgUrl}">
</a>
</div>
<a href="#" target="_blank">
<h2 class="author-h2">
${cont.nickName}
</h2>
</a>
</div>
<h2>${cont.title}</h2>
${cont.content}
<div style="height: 5px"></div>
<div class="stats">
<!-- 笑脸、评论数等 -->
<span class="stats-vote"><i id="${cont.id}" class="number">${cont.upvote}</i> 赞</span>
<span class="stats-comments">
<span class="dash"> · </span>
<a onclick="reply(${cont.id},${cont.uId});">
<i class="number" id="comment_num_${cont.id}">${cont.commentNum}</i> 评论
</a>
</span>
</div>
<div style="height: 5px"></div>
<div class="stats-buttons bar clearfix">
<a style="cursor: pointer;" onclick="upvote_click(${cont.id},1);">
<i class="icon icon-thumbs-o-up icon-2x"></i>
<span class="number hidden" id="up_${cont.id}">${cont.upvote}</span>
</a>
<a style="cursor: pointer;" onclick="upvote_click(${cont.id},-1);">
<i class="icon icon-thumbs-o-down icon-2x"></i>
<span class="number hidden" id="down_${cont.id}">${cont.downvote}</span>
</a>
<a style="cursor: pointer;" onclick="reply(${cont.id},${cont.uId});" title="点击打开或关闭">
<i class="icon icon-comment-alt icon-2x"></i>
</a>
</div>
<div class="single-share">
<a class="share-wechat" data-type="wechat" title="分享到微信" rel="nofollow" style="margin-left:18px;color: grey;cursor: pointer; text-decoration:none;">
<i class="icon icon-wechat icon-2x"></i>
</a>
<a class="share-qq" data-type="qq" title="分享到QQ" rel="nofollow" style="margin-left:18px;color: grey;cursor: pointer; text-decoration:none;">
<i class="icon icon-qq icon-2x"></i>
</a>
<a class="share-weibo" data-type="weibo" title="分享到微博" rel="nofollow" style="margin-left:18px;color: grey;cursor: pointer; text-decoration:none;">
<i class="icon icon-weibo icon-2x"></i>
</a>
</div>
<br/>
<div class="commentAll" style="display:none" id="comment_reply_${cont.id}">
<!--评论区域 begin-->
<div class="reviewArea clearfix">
<textarea class="content comment-input" placeholder="Please enter a comment…" onkeyup="keyUP(this)"></textarea>
<a class="plBtn" id="comment_${cont.id}" onclick="_comment(${cont.id},${user.id==null?0:user.id},${cont.uId})" style="color: white;cursor: pointer;">评论</a>
</div>
<!--评论区域 end-->
<div class="comment-show-first" id="comment-show-${cont.id}">
</div>
</div>
<div class="single-clear">
</div>
</div>
<!-- 正文结束 -->
<div style="position: absolute;width:900px;background-color: #EBEBEB;height: 10px;left: 0px">
</div>
</c:forEach>
代码解读如下:
- 通过
<c:forEach></c:forEach>
循环获取文章内容 Content。 items="${page.result}"
中,item 是List<Content>
集合。“var="cont"
中,cont 是每一个 Content,通过${cont.属性}
即可获得对应的属性值。varStatus="i"
中,i.index 就是从0开始的迭代索引,i.count 就是从1开始迭代计数。
(4)分页信息如下:
<div id="page-info" style="position: absolute;width:900px;background-color: #EBEBEB;height: 80px;left: 0px;">
<ul class="pager pager-loose">
<c:if test="${page.pageNum <= 1}">
<li><a href="void(0);">« 上一页</a></li>
</c:if>
<c:if test="${page.pageNum > 1}">
<li class="previous"><a href="${ctx}/index_list?pageNum=${page.pageNum-1}&&id=${user.id}">« 上一页</a></li>
</c:if>
<c:forEach begin="1" end="${page.pages}" var="pn">
<c:if test="${page.pageNum==pn}">
<li class="active"><a href="void(0);">${pn}</a></li>
</c:if>
<c:if test="${page.pageNum!=pn}">
<li ><a href="${ctx}/index_list?pageNum=${pn}&&id=${user.id}">${pn}</a></li>
</c:if>
</c:forEach>
<c:if test="${page.pageNum>=page.pages}">
<li><a href="void(0);">下一页 »</a></li>
</c:if>
<c:if test="${page.pageNum<page.pages}">
<li><a href="${ctx}/index_list?pageNum=${page.pageNum+1}&&id=${user.id}">下一页 »</a></li>
</c:if>
</ul>
</div>
判断过程如下:
- 页数 <=1 显示上一页,但是不能点击(点击无效);
- 页数 >1 显示上一页,点击返回上一页;
- 页数 >= 最后一页 ,显示下一页,但是点击无效;
- 页数 < 最后一页,显示下一页,点击跳转到下一页。
(5)中间页数循环。
代码如下:
<c:forEach begin="1" end="${page.pages}" var="pn">
从第一页开始,最后一页结束,变量 pn=当前页数。
(6)如果 pageNum 等于当前页 pn,说明被选中,不能被再次点击(点击无效):
<c:if test="${page.pageNum==pn}">
<li class="active"><a href="void(0);">${pn}</a></li>
</c:if>
(7)如果 pageNum 不是当前页,既不是被选中页,可以被点击,点击后跳转到该页:
<c:if test="${page.pageNum!=pn}">
<li ><a href="${ctx}/index_list?pageNum=${pn}&&id=${user.id}">${pn}</a></li>
</c:if>
重启 Tomcat,访问:http://localhost:8080/,页面已经有动态数据了,但是点击下一页或者其他页还没实现。
IndexJspController
在 wang.dreamland.www 包下新建 IndexJspController.java,并继承 BaseController,将关于首页有关的方法都放在这里,创建映射 URL 为 /index_list
的方法:
@Controller
public class IndexJspController extends BaseController {
private final static Logger log = Logger.getLogger(IndexJspController.class);
@RequestMapping("/index_list")
public String findAllList(Model model, @RequestParam(value = "id",required = false) String id ,
@RequestParam(value = "pageNum",required = false) Integer pageNum ,
@RequestParam(value = "pageSize",required = false) Integer pageSize) {
log.info( "===========进入index_list=========" );
User user = (User)getSession().getAttribute("user");
if(user!=null){
model.addAttribute( "user",user );
}
Page<UserContent> page = findAll(null,pageNum, pageSize);
model.addAttribute( "page",page );
return "../index";
}
}
代码解读如下:
-
通过 Session 获取 User 信息,如果不为空则把 User 添加到 Model 中;
-
调用 BaseController 中的 findAll 方法查询分页并将结果封装到 Page 对象中,参数分别是 null 代表查询所有、pageNum 当前页、pageSize 每页显示条数;
-
将 Page 对象添加到 Model 中,并返回到 index.jsp 页面。
最后重启 Tomcat 访问:http://localhost:8080/,分页完成!
第08课百度网盘地址:
链接:https://pan.baidu.com/s/1iyTbvLepD07MUeFVbpMxRw 密码:1xxw