Hello, Pagekitters!
I'm sure everybody likes "Load more" button with lazy loading and rendering small portions of the data from MySQL on request.
Here I will show you how I managed to create this button in my Pagekit extension.
I think this post will be interesting for beginners (like me), but I hope even an experienced amature will find something useful. Let's go!
What do I have:
- A MySQL table containing many items separated in several columns
What do I want:
- To render a 4x2 grid of the most recently added items (8 items in total)
- To render a "Load more" button which send next 8 items to my grid when clicked
- To destroy the button when there's nothing left to load
- Not to show the button if initial total number of items is 8 or less
1 step: SiteController and views/index.php
- /**
- * @Route("/")
- */
- public function siteAction()
- {
- $itemquery = App::db()->createQueryBuilder();
- $offset = (int) 0; // start from the 1st item in database
- $limit = (int) 8; // number of items to be displayed
- $query = $itemquery // query all items
- -> select(['*'])
- -> from('@table')
- -> orderby('id', DESC) // show the most recently added items. Id is an integer with autoincrement option
- -> offset($offset)
- -> limit($limit)
- -> get();
- $total = (int)ceil($salequery->count('id')/$limit) - 1; // count all items in table and calculate the number of "Load more" clicks we need to render all of them
- return
- [
- '$data' => $query, // this would be our v-for="item in items" in the frontend
- '$total' => $total, // we will use it to render and destroy "Load more" button with v-if
- '$view' => [
- 'title' => 'Скидки и распродажи',
- 'name' => 'myapp:views/index.php',
- ],
- ];
- }
- <?php $view->script('items', 'myapp:app/bundle/items.js', 'vue') ?>
- <?php $view->style('style', 'myapp:app/bundle/items.css') ?>
- <article>
- <div id="items">
- <div class="uk-grid uk-grid-medium uk-grid-match">
- <div class="uk-width-large-1-4 uk-width-medium-1-2 uk-width-small-1-1 uk-grid-margin" v-for="item in items">
- <!-- HERE GOES YOUR TEMPLATE -->
- </div>
- </div>
- <hr class="uk-grid-divider">
- <span v-if="showbutton">
- <button class="uk-button-large uk-button-success uk-align-center" @click="loadmore">Show more</button>
- </span>
- </div>
- </article>
Great! Now first 8 items are loaded and rendered! Let's create a "loadmore" method for our button!
First I will define a function, that sends a POST request to our controller, then I will deal with the response.
2nd step - Vuejs methods, POST request and SiteController response:
- methods: {
- loadmore: function() {
- this.$http.post('/items/loaditems', {newoffset: this.newoffset}).then(function(response){ // creates a POST request, then starts a function on response
- this.items.push.apply(this.items, response.data.$newitems); // add items from "response" array to our existing array (concat will not work, it creates a new array instead of pushing items from second to first)
- this.clicked = this.clicked + 1; // now the button is clicked once and this value will be increased on each next click
- this.newoffset = this.newoffset + 8; // we rendered next 8 items, so next time we'll have to query items from the db starting with +8 row
- if (this.clicked === $total) { // check if we have reached our "click limit" to load all the items from the db
- this.showbutton = false; // this will destroy our button when we don't need it anymore as all items would have been loaded
- };
- }, function(){console.log("Error")}); // log in the console if we had incorrect response from our Controller
- }
- },
- /**
- * @Request({"newoffset": "integer"}, csrf=true) // the POST request must contain a "newoffset" variable
- */
- public function loaditemsAction($newoffset = 0)
- {
- $newitemquery = App::db()->createQueryBuilder(); // create new query
- $offset = $newoffset; // this is a variable from our POST request
- $limit = (int) 8;
- $newquery = $newitemquery
- -> select(['*'])
- -> from('@table')
- -> orderby('id', DESC)
- -> offset($offset)
- -> limit($limit)
- -> get();
- return [ '$newitems' => $newquery ]; // this is a response to our request containing an array of 8 new loaded items
- }
Works! Now a final note. If our initial number of items is 8 or less (nothing more to load) we don't want a button to be rendered at all. Let's add one more function to our items.js. We will use "created" option for that.
Now everything should work well!
Now we have:
- A button that loads 8 more items in a lazy way
- The button disappears when there is nothing more to load
- The button does not appear if the initial number of items is 8 or less
Thank you for reading this quite long post