Using kkpager plugin for Odoo Ajax pagination method
controllers/controllers.py
from odoo import http
from odoo.http import request
from odoo.addons.portal.controllers.portal import pager
class MyWeb(http.Controller):
_news_per_page = 15
@http.route(['/news/', '/news/?page/<int:page>'], type='http', auth="public", methods=['GET', 'POST'], website=True)
def news_list(self, page=1, **kw):
news = request.env['my.news'].sudo()
domain = []
page_count = news.search_count(domain)
# make pagination
pagination = pager(
url="/news",
total=page_count,
page=page,
step=self._news_per_page,
)
records = news.sudo().search(
domain, order="create_date desc", limit=self._news_per_page, offset=pagination['offset']
)
return request.render("my_website_module.news_list", {'records': records})
@http.route('/news/<int:news_id>', type='http', auth="public", methods=['GET', 'POST'], website=True)
def news_detail(self, news_id=None, **kw):
record = request.env['my.news'].sudo().browse(int(news_id)).exists()
return request.render("my_website_module.news_detail", {'record': record})
@http.route('/session_info.json', type='json', auth="public", methods=['POST'], website=True)
def session_info(self, **kw):
news_records = request.env['my.news'].sudo().search_count([])
news_pages, a = divmod(news_records, self._news_per_page)
if a:
news_pages += 1
return dict(
news_pages=news_pages,
news_records=news_records,
)
my_website_module/templates/news_list_templates.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="news_list" name="News list">
<t t-call="website.layout">
<div class="container">
<div class="divider clearfix">
<div class="divFirst">
<div class="innerInfo" style="padding-top:0;">
<ul class="clearfix" id="">
<li t-if="not records">No news records&nbsp;...</li>
<li t-foreach="records" t-as="r">
<div class="clearfix">
<span class="bfR" t-field="r.create_date"
t-options='{"widget": "date"}'/>
<a t-attf-href="/news/#{r.id}" class="mainA">
<t t-esc="r.name"/>
</a>
</div>
</li>
</ul>
<div id="pager"/>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="/my_website_module/static/src/js/news_pager.js"/>
</t>
</template>
</data>
</odoo>
my_website_module/templates/news_detail_templates.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="news_detail" name="News detail">
<t t-call="website.layout">
<div class="container">
<div class="divider clearfix">
<div class="divFirst">
<div class="innerNewsInfo">
<div class="newTitle">
<h2 t-esc="record.name"/>
<div id="divCome" t-field="record.create_date"/>
</div>
<div id="divContent" class="newsContent" t-raw="record.page"></div>
</div>
</div>
</div>
</div>
</t>
</template>
</data>
</odoo>
static/src/js/news_pager.js
odoo.define('my_website_module.news_pager', function (require) {
"use strict";
var ajax = require('web.ajax');
function getParameter(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}
$(function () {
ajax.jsonRpc('/session_info.json', 'call', {}).then(function (data) {
var totalPage = data.news_pages;
var totalRecords = data.news_records;
var pageNo = getParameter('page');
if (!pageNo) {
pageNo = 1;
}
kkpager.generPageHtml({
pno: pageNo,
total: totalPage,
totalRecords: totalRecords,
hrefFormer: '/news/',
hrefLatter: '',
getLink: function (n) {
return this.hrefFormer + this.hrefLatter + "?page=" + n;
}
})
});
});
});
static/src/js/kkpager.js
var kkpager = {
pagerid: 'pager', //divID
mode: 'link', //模式(link 或者 click)
pno: 1, //当前页码
total: 1, //总页码
totalRecords: 0, //总数据条数
isShowFirstPageBtn: true, //是否显示首页按钮
isShowLastPageBtn: true, //是否显示尾页按钮
isShowPrePageBtn: true, //是否显示上一页按钮
isShowNextPageBtn: true, //是否显示下一页按钮
isShowTotalPage: true, //是否显示总页数
isShowCurrPage: true,//是否显示当前页
isShowTotalRecords: false, //是否显示总记录数
isGoPage: true, //是否显示页码跳转输入框
isWrapedPageBtns: true, //是否用span包裹住页码按钮
isWrapedInfoTextAndGoPageBtn: true, //是否用span包裹住分页信息和跳转按钮
hrefFormer: '', //链接前部
hrefLatter: '', //链接尾部
gopageWrapId: 'pager_gopage_wrap',
gopageButtonId: 'pager_btn_go',
gopageTextboxId: 'pager_btn_go_input',
lang: {
firstPageText: '首页',
firstPageTipText: '首页',
lastPageText: '尾页',
lastPageTipText: '尾页',
prePageText: '上一页',
prePageTipText: '上一页',
nextPageText: '下一页',
nextPageTipText: '下一页',
totalPageBeforeText: '共',
totalPageAfterText: '页',
currPageBeforeText: '当前第',
currPageAfterText: '页',
totalInfoSplitStr: '/',
totalRecordsBeforeText: '共',
totalRecordsAfterText: '条数据',
gopageBeforeText: ' 转到',
gopageButtonOkText: '确定',
gopageAfterText: '页',
buttonTipBeforeText: '第',
buttonTipAfterText: '页'
},
//链接算法(当处于link模式),参数n为页码
getLink: function (n) {
//这里的算法适用于比如:
//hrefFormer=http://www.xx.com/news/20131212
//hrefLatter=.html
//那么首页(第1页)就是http://www.xx.com/news/20131212.html
//第2页就是http://www.xx.com/news/20131212_2.html
//第n页就是http://www.xx.com/news/20131212_n.html
if (n == 1) {
return this.hrefFormer + this.hrefLatter;
}
return this.hrefFormer + '_' + n + this.hrefLatter;
},
//页码单击事件处理函数(当处于mode模式),参数n为页码
click: function (n) {
//这里自己实现
//这里可以用this或者kkpager访问kkpager对象
return false;
},
//获取href的值(当处于mode模式),参数n为页码
getHref: function (n) {
//默认返回'#'
return '#';
},
//跳转框得到输入焦点时
focus_gopage: function () {
var btnGo = $('#' + this.gopageButtonId);
$('#' + this.gopageTextboxId).attr('hideFocus', true);
btnGo.show();
btnGo.css('left', '25px');
$('#' + this.gopageTextboxId).addClass('focus');
btnGo.animate({left: '+=30'}, 50);
},
//跳转框失去输入焦点时
blur_gopage: function () {
var _this = this;
setTimeout(function () {
var btnGo = $('#' + _this.gopageButtonId);
btnGo.animate({
left: '-=25'
}, 100, function () {
btnGo.hide();
$('#' + _this.gopageTextboxId).removeClass('focus');
});
}, 400);
},
//跳转输入框按键操作
keypress_gopage: function () {
var event = arguments[0] || window.event;
var code = event.keyCode || event.charCode;
//delete key
if (code == 8) return true;
//enter key
if (code == 13) {
kkpager.gopage();
return false;
}
//copy and paste
if (event.ctrlKey && (code == 99 || code == 118)) return true;
//only number key
if (code < 48 || code > 57) return false;
return true;
},
//跳转框页面跳转
gopage: function () {
var str_page = $('#' + this.gopageTextboxId).val();
if (isNaN(str_page)) {
$('#' + this.gopageTextboxId).val(this.next);
return;
}
var n = parseInt(str_page);
if (n < 1) n = 1;
if (n > this.total) n = this.total;
if (this.mode == 'click') {
this._clickHandler(n);
} else {
window.location = this.getLink(n);
}
},
//不刷新页面直接手动调用选中某一页码
selectPage: function (n) {
this._config['pno'] = n;
this.generPageHtml(this._config, true);
},
//生成控件代码
generPageHtml: function (config, enforceInit) {
if (enforceInit || !this.inited) {
this.init(config);
}
var str_first = '', str_prv = '', str_next = '', str_last = '';
if (this.isShowFirstPageBtn) {
if (this.hasPrv) {
str_first = '<a ' + this._getHandlerStr(1) + ' title="'
+ (this.lang.firstPageTipText || this.lang.firstPageText) + '">' + this.lang.firstPageText + '</a>';
} else {
str_first = '<span class="disabled">' + this.lang.firstPageText + '</span>';
}
}
if (this.isShowPrePageBtn) {
if (this.hasPrv) {
str_prv = '<a ' + this._getHandlerStr(this.prv) + ' title="'
+ (this.lang.prePageTipText || this.lang.prePageText) + '">' + this.lang.prePageText + '</a>';
} else {
str_prv = '<span class="disabled">' + this.lang.prePageText + '</span>';
}
}
if (this.isShowNextPageBtn) {
if (this.hasNext) {
str_next = '<a ' + this._getHandlerStr(this.next) + ' title="'
+ (this.lang.nextPageTipText || this.lang.nextPageText) + '">' + this.lang.nextPageText + '</a>';
} else {
str_next = '<span class="disabled">' + this.lang.nextPageText + '</span>';
}
}
if (this.isShowLastPageBtn) {
if (this.hasNext) {
str_last = '<a ' + this._getHandlerStr(this.total) + ' title="'
+ (this.lang.lastPageTipText || this.lang.lastPageText) + '">' + this.lang.lastPageText + '</a>';
} else {
str_last = '<span class="disabled">' + this.lang.lastPageText + '</span>';
}
}
var str = '';
var dot = '<span class="spanDot">...</span>';
var total_info = '<span class="totalText">';
var total_info_splitstr = '<span class="totalInfoSplitStr">' + this.lang.totalInfoSplitStr + '</span>';
if (this.isShowCurrPage) {
total_info += this.lang.currPageBeforeText + '<span class="currPageNum">' + this.pno + '</span>' + this.lang.currPageAfterText;
if (this.isShowTotalPage) {
total_info += total_info_splitstr;
total_info += this.lang.totalPageBeforeText + '<span class="totalPageNum">' + this.total + '</span>' + this.lang.totalPageAfterText;
} else if (this.isShowTotalRecords) {
total_info += total_info_splitstr;
total_info += this.lang.totalRecordsBeforeText + '<span class="totalRecordNum">' + this.totalRecords + '</span>' + this.lang.totalRecordsAfterText;
}
} else if (this.isShowTotalPage) {
total_info += this.lang.totalPageBeforeText + '<span class="totalPageNum">' + this.total + '</span>' + this.lang.totalPageAfterText;
if (this.isShowTotalRecords) {
total_info += total_info_splitstr;
total_info += this.lang.totalRecordsBeforeText + '<span class="totalRecordNum">' + this.totalRecords + '</span>' + this.lang.totalRecordsAfterText;
}
} else if (this.isShowTotalRecords) {
total_info += this.lang.totalRecordsBeforeText + '<span class="totalRecordNum">' + this.totalRecords + '</span>' + this.lang.totalRecordsAfterText;
}
total_info += '</span>';
var gopage_info = '';
if (this.isGoPage) {
gopage_info = '<span class="goPageBox">' + this.lang.gopageBeforeText + '<span id="' + this.gopageWrapId + '">' +
'<input type="button" id="' + this.gopageButtonId + '" onclick="kkpager.gopage()" value="'
+ this.lang.gopageButtonOkText + '" />' +
'<input type="text" id="' + this.gopageTextboxId + '" onfocus="kkpager.focus_gopage()" onkeypress="return kkpager.keypress_gopage(event);" onblur="kkpager.blur_gopage()" value="' + this.next + '" /></span>' + this.lang.gopageAfterText + '</span>';
}
//分页处理
if (this.total <= 8) {
for (var i = 1; i <= this.total; i++) {
if (this.pno == i) {
str += '<span class="curr">' + i + '</span>';
} else {
str += '<a ' + this._getHandlerStr(i) + ' title="'
+ this.lang.buttonTipBeforeText + i + this.lang.buttonTipAfterText + '">' + i + '</a>';
}
}
} else {
if (this.pno <= 5) {
for (var i = 1; i <= 5; i++) {
if (this.pno == i) {
str += '<span class="curr">' + i + '</span>';
} else {
str += '<a ' + this._getHandlerStr(i) + ' title="' +
this.lang.buttonTipBeforeText + i + this.lang.buttonTipAfterText + '">' + i + '</a>';
}
}
str += dot;
} else {
// str += '<a ' + this._getHandlerStr(1) + ' title="'
// + this.lang.buttonTipBeforeText + '1' + this.lang.buttonTipAfterText + '">1</a>';
// str += '<a ' + this._getHandlerStr(2) + ' title="'
// + this.lang.buttonTipBeforeText + '2' + this.lang.buttonTipAfterText + '">2</a>';
str += dot;
var begin = this.pno - 2;
var end = this.pno + 2;
if (end > this.total) {
end = this.total;
begin = end - 4;
if (this.pno - begin < 2) {
begin = begin - 1;
}
} else if (end + 1 == this.total) {
end = this.total;
}
for (var i = begin; i <= end; i++) {
if (this.pno == i) {
str += '<span class="curr">' + i + '</span>';
} else {
str += '<a ' + this._getHandlerStr(i) + ' title="'
+ this.lang.buttonTipBeforeText + i + this.lang.buttonTipAfterText + '">' + i + '</a>';
}
}
if (end != this.total) {
str += dot;
}
}
}
var pagerHtml = '<div>';
if (this.isWrapedPageBtns) {
pagerHtml += '<span class="pageBtnWrap">' + str_first + str_prv + str + str_next + str_last + '</span>'
} else {
pagerHtml += str_first + str_prv + str + str_next + str_last;
}
if (this.isWrapedInfoTextAndGoPageBtn) {
pagerHtml += '<span class="infoTextAndGoPageBtnWrap hidden-xs">' + total_info + gopage_info + '</span>';
} else {
pagerHtml += total_info + gopage_info;
}
pagerHtml += '</div><div style="clear:both;"></div>';
$("#" + this.pagerid).html(pagerHtml);
},
//分页按钮控件初始化
init: function (config) {
this.pno = isNaN(config.pno) ? 1 : parseInt(config.pno);
this.total = isNaN(config.total) ? 1 : parseInt(config.total);
this.totalRecords = isNaN(config.totalRecords) ? 0 : parseInt(config.totalRecords);
if (config.pagerid) {
this.pagerid = config.pagerid;
}
if (config.mode) {
this.mode = config.mode;
}
if (config.gopageWrapId) {
this.gopageWrapId = config.gopageWrapId;
}
if (config.gopageButtonId) {
this.gopageButtonId = config.gopageButtonId;
}
if (config.gopageTextboxId) {
this.gopageTextboxId = config.gopageTextboxId;
}
if (config.isShowFirstPageBtn != undefined) {
this.isShowFirstPageBtn = config.isShowFirstPageBtn;
}
if (config.isShowLastPageBtn != undefined) {
this.isShowLastPageBtn = config.isShowLastPageBtn;
}
if (config.isShowPrePageBtn != undefined) {
this.isShowPrePageBtn = config.isShowPrePageBtn;
}
if (config.isShowNextPageBtn != undefined) {
this.isShowNextPageBtn = config.isShowNextPageBtn;
}
if (config.isShowTotalPage != undefined) {
this.isShowTotalPage = config.isShowTotalPage;
}
if (config.isShowCurrPage != undefined) {
this.isShowCurrPage = config.isShowCurrPage;
}
if (config.isShowTotalRecords != undefined) {
this.isShowTotalRecords = config.isShowTotalRecords;
}
if (config.isWrapedPageBtns) {
this.isWrapedPageBtns = config.isWrapedPageBtns;
}
if (config.isWrapedInfoTextAndGoPageBtn) {
this.isWrapedInfoTextAndGoPageBtn = config.isWrapedInfoTextAndGoPageBtn;
}
if (config.isGoPage != undefined) {
this.isGoPage = config.isGoPage;
}
if (config.lang) {
for (var key in config.lang) {
this.lang[key] = config.lang[key];
}
}
this.hrefFormer = config.hrefFormer || '';
this.hrefLatter = config.hrefLatter || '';
if (config.getLink && typeof(config.getLink) == 'function') {
this.getLink = config.getLink;
}
if (config.click && typeof(config.click) == 'function') {
this.click = config.click;
}
if (config.getHref && typeof(config.getHref) == 'function') {
this.getHref = config.getHref;
}
if (!this._config) {
this._config = config;
}
//validate
if (this.pno < 1) this.pno = 1;
this.total = (this.total <= 1) ? 1 : this.total;
if (this.pno > this.total) this.pno = this.total;
this.prv = (this.pno <= 2) ? 1 : (this.pno - 1);
this.next = (this.pno >= this.total - 1) ? this.total : (this.pno + 1);
this.hasPrv = (this.pno > 1);
this.hasNext = (this.pno < this.total);
this.inited = true;
},
_getHandlerStr: function (n) {
if (this.mode == 'click') {
return 'href="' + this.getHref(n) + '" onclick="return kkpager._clickHandler(' + n + ')"';
}
//link模式,也是默认的
return 'href="' + this.getLink(n) + '"';
},
_clickHandler: function (n) {
var res = false;
if (this.click && typeof this.click == 'function') {
res = this.click.call(this, n) || false;
}
return res;
}
};
static/src/css/kkpager.css
#pager {
clear: both;
color: #999;
font-size: 13px;
padding-bottom: 16px;
}
#pager a {
float: left;
border: 1px solid #ccc;
display: inline;
padding: 3px 10px 3px 10px;
margin-right: 5px;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
cursor: pointer;
background: #fff;
text-decoration: none;
color: #999;
}
#pager span.disabled {
float: left;
display: inline;
padding: 3px 10px 3px 10px;
margin-right: 5px;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border: 1px solid #DFDFDF;
background-color: #FFF;
color: #DFDFDF;
}
#pager span.curr {
float: left;
border: 1px solid #ef2629;
display: inline;
padding: 3px 10px 3px 10px;
margin-right: 5px;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
background: #FFEEE5;
color: #ef2629;
}
#pager a:hover {
border: 1px solid #ef2629;
background-color: #ef2629;
color: #fff;
}
#pager span.normalsize {
}
#pager_gopage_wrap {
position: relative;
left: 0px;
top: 0px;
}
#pager_btn_go {
width: 44px;
height: 18px;
border: 0px;
overflow: hidden;
line-height: 140%;
padding: 0px;
margin: 0px;
text-align: center;
cursor: pointer;
background-color: #ef2629;
color: #FFF;
position: absolute;
left: 0px;
top: -1px;
font-size: 13px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
display: none;
}
#pager_btn_go_input {
width: 36px;
height: 15px;
color: #999;
text-align: center;
margin-left: 1px;
margin-right: 1px;
border: 1px solid #DFDFDF;
position: relative;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
left: 0px;
top: -1.5px;
outline: none;
}
#pager_btn_go_input.focus {
border-color: #ef2629;
}
#pager .pageBtnWrap {
float: left;
}
#pager .infoTextAndGoPageBtnWrap {
float: right;
}
#pager .spanDot {
float: left;
margin-right: 5px;
}
#pager .currPageNum {
color: #ef2629;
}
#pager .infoTextAndGoPageBtnWrap {
padding-top: 5px;
margin-right: 37px;
/*float: left;*/
}