How to implement a Pager
Hi,
I have a custom plugin that creates a custom content. I already have pages that implements add, edit, delete and view( single record only) functionalities . Now I want to implement another page that will show all records with a pager. Can someone point me into a tutorial or documentation on how to do this properly.
Thanks
0          
             
         
            
Comments
A start
https://www.w3schools.com/css/css3_pagination.asp
There is no tutorial on that and the pager is quite complex, thow you do not need to do much to get it going. After you have set up a working pager, I recommend taking a closer look at the class.pagermodule.php to find out more about its possibilities.
The best way to get going is to look at a page where you can find a pager, e.g. one of the discussions lists (recent discussions, bookmarked, mine etc.) You can find that code in /applications/vanilla/controllers/class.discussionscontroller.php
Let's look at the mine() method, which is quite short, but I stripped out what is not necessary to get a working example:
public function mine($page = 'p1') { ... // Set criteria & get discussions data list($offset, $limit) = offsetLimit($page, c('Vanilla.Discussions.PerPage', 30)); ... $countDiscussions = $this->setData('CountDiscussions', $discussionModel->getCount($wheres)); ... // Build a pager $pagerFactory = new Gdn_PagerFactory(); $this->EventArguments['PagerType'] = 'MorePager'; ... $this->Pager = $pagerFactory->getPager($this->EventArguments['PagerType'], $this); $this->Pager->MoreCode = 'More Discussions'; $this->Pager->LessCode = 'Newer Discussions'; $this->Pager->ClientID = 'Pager'; $this->Pager->configure( $offset, $limit, $countDiscussions, 'discussions/mine/%1$s' ); $this->setData('_PagerUrl', 'discussions/mine/{Page}'); $this->setData('_Page', $page); $this->setData('_Limit', $limit); ... // Deliver JSON data if necessary if ($this->_DeliveryType != DELIVERY_TYPE_ALL) { $this->setJson('LessRow', $this->Pager->toString('less')); $this->setJson('MoreRow', $this->Pager->toString('more')); $this->View = 'discussions'; } ... }The main element in there is the following block:
The pager module needs to be configured with those information: where does the current page starts (offset), how much elements does it show (limit), how much elements are there in total (countDiscussions) and where should the previous and next page links should point to.
But look at method code step by step:
public function mine($page = 'p1') {In your plugin this will most probably be
public function pluginController_foo_create($sender, $args) {The $args array will contain everything that comes after yourforum.com/plugin/foo/...
Therefore, if your url scheme for e.g. the third page should be /plugin/foo/p3, you should have the following code for getting the page for building the pager:
$page = $args[0] ?? 'p1';If your url scheme is /plugin/foo/bar/baz/p3, the required code would be
$page = $args[2] ?? 'p1';list($offset, $limit) = offsetLimit($page, c('Vanilla.Discussions.PerPage', 30));This line does a lot! You should look up the function
offsetLimitin functions.general.php. What's happening here is that the variables $offset and $limit are filled based on the $page variable and the config value for Vanilla.Discussions.PerPage. I would encoure to make your view configurable in the same way.list($offset, $limit) = offsetLimit($page, Gdn::config('Plugins.FooBar.PerPage', 30));And if it makes sense, you can directly use a Vanilla PerPage setting as default!
list($offset, $limit) = offsetLimit( $page, Gdn::config( 'Plugins.FooBar.PerPage', Gdn::config( 'Vanilla.Discussions.PerPage', 30 ) ) );The following lines in the snippet are split because that way a plugin author can influence the look and feel of the pager of the mine() method.
You can condense it to this line
$this->Pager = $pagerFactory->getPager('MorePager', $this);By the way: in most cases when you look at Vanilla code and see
$this, you will have to replace that with$sender(if you stick to Vanillas conventions)Frankly spoken, I'm not sure if you need this block:
$this->setData('_PagerUrl', 'discussions/mine/{Page}'); $this->setData('_Page', $page); $this->setData('_Limit', $limit);ANd you have to decide by yourself if that makes sense for you:
// Deliver JSON data if necessary if ($this->_DeliveryType != DELIVERY_TYPE_ALL) { $this->setJson('LessRow', $this->Pager->toString('less')); $this->setJson('MoreRow', $this->Pager->toString('more')); $this->View = 'discussions'; }In your view, a simple PagerModule::write() should be sufficient.
the code works, I replaced $page = $args[0] ?? 'p1' with $page = $_GET["Page"] ?? 'p1'.
What Is best way to hide the next button at the end of the last record when pager reach the last page?
Thanks
I found out that I need to set the options of PagerModule::write() in the view to make the url route work properly and the show the pager buttons with numbers
$PagerOptions =
['Wrapper' => '<span class="PagerNub"> </span><div %1$s>%2$s</div>', 'RecordCount' => $this->data('count'), 'CurrentRecords' => $this->data('count')];
if ($this->data('_PagerUrl')){
$PagerOptions['Url'] = $this->data('_PagerUrl');}
PagerModule::write($PagerOptions);
What is the best way to add dropdown filter with this?
Thanks
Instead of having [previous] 1...7 [next] you want to have [__3__|\/]? Then the pager module isn't suitable. The good thing is that you now already know what you need!
This is the base info needed to build a pager, no matter what html you choose.
Vanilla has a Form class which can help you build dropdowns. It should look similar to this:
// $page = Gdn::request()->get('page'); // $recordCount = (new Gdn_Model('yourTable')->getCount()); // Set example values. $page = 'p2'; $recordCount = 30; // Get offset and limit list($offset, $limit) = offsetLimit($page, 12); // Calculate number of pages $pageCount = ceil($recordCount / $limit); // Build the array for the dropdown element // Text: "Page X", value: "pX" $pagerContent = []; for ($p = 1; $p <= $pageCount; $p++) { $pagerContent["p{$p}"] = sprintf(Gdn::translate('Page %s'), $p); } // Calculate current page for setting the current value in the dropdown $currentPage = $offset / $limit + 1; $form = new Gdn_Form(); echo '<div class="PageControls MyPager">'; echo $form->open(['action'=>'/plugin/foo/','id'=>'MyPagerForm']); echo $form->dropDown( 'MyPager', // The form elements name $pagerContent, // The array for value and text in dropdown ['Value' => "p{$currentPage}"] // The current value ); echo $form->button('GO!',['type' => 'submit']); echo $form->close(); echo '</div>';You would need to add some js that will catch click events on the GO! button (or directly track changing the dropdown) and reload the current page with the appropriate page. I would try to change the forms action attribute accordingly whenever the dropdown changes.