Pagination in keystoneJS can not implement it - express

I have many posts on my blog. I would like to show only 2 per page. I tried to use pagination in keystoneJS. I use https://keystonejs.com/documentation/database/#lists-paginate and should be ok, but I still see all my posts in one page.
Route / View
var keystone = require('keystone')
var Post = keystone.list('Post')
exports = module.exports = function(req, res) {
var view = new keystone.View(req, res)
var locals = res.locals
// Init locals
locals.section = 'blog'
locals.filters = {
post: req.params.post
}
// Load other posts
view.on('init', function(next) {
var q = Post.paginate({
page: req.query.page,
perPage: 2,
maxPages: 10
})
.model.find()
.where('state', 'published')
.sort('-publishedDate')
.populate('author')
// .limit(4)
q.exec(function(err, results) {
locals.posts = results
next(err)
})
})
view.query('blog', keystone.list('Post').model.find())
view.render('blog')
}
blog.pug
.pagination
ul.pagination
li #{posts.totalPages}
a.page-num(href="?page=1") Previous
li
a.page-num(href="?page={{posts.next }}") Next
I see all posts on my page and I cant set a number of page in blog.pug. iI was considering, if I should put any code to my blog.pug. Or only in route? In documentation the code is only in route file.
EDIT
Ok so I found the reason what has happened. I have to remove model.find() from my code in route. Then, in blog.pug I had to add to my loop word 'result'. I had 'each post in posts', and when i remove model.find() I changed my object with data. Now it is 'each post in posts.results' and it is correct.

Related

useInfiniteScroll utility of Vueuse is fetching same items again

Here is a reproducable stackblitz -
https://stackblitz.com/edit/nuxt-starter-jlzzah?file=components/users.vue
What's wrong? -
My code fetches 15 items, and with the bottom scroll event it should fetch another 15 different items but it just fetches same items again.
I've followed this bottom video for this implementation, it's okay in the video but not okay in my stackblitz code:
https://www.youtube.com/watch?v=WRnoQdIU-uE&t=3s&ab_channel=JohnKomarnicki
The only difference with this video is that he's using axios while i use useFetch of nuxt 3.
It's not really a cache issue. useFetch is "freezing" the API URL, the changes you make to the string directly will not be reliably reflected. If you want to add parameters to your API URL, use the query option of useFetch. This option is reactive, so you can use refs and the query will update with the refs. Alternatively, you can use the provided refresh() method
const limit = ref(10)
const skip = ref(20)
const { data: users, refresh: refreshUsers } = await useFetch(
'https://dummyjson.com/users',
{
query:{
limit,
skip
}
}
);
//use the data object directly to access the result
console.log(users.value)
//if you want to update users with different params later, simply change the ref and the query will update
limit.value = 23
//use refresh to manually refresh the query
refreshUsers()
This results in a first API call http://127.0.0.1:8000/api/tasks?limit=10&skip=20 and then a second with the updated values http://127.0.0.1:8000/api/tasks?limit=23&skip=20
You can leave the cache alone, as it is just a workaround, and will not work reliably.
[Updated] The useFetch() documentation is now updated as described below.
The query option is not well documented yet, as discussed in this nuxt issue. I've created a pull request on nuxt/framework to have it reflected in the documentation. Please see a full explanation below:
Using the query option, you can add search parameters to your query. This option is extended from unjs/ohmyfetch and is using ufo to create the URL. Objects are automatically stringified.
const param1 = ref('value1')
const { data, pending, error, refresh } = await useFetch('https://api.nuxtjs.dev/mountains',{
query: { param1, param2: 'value2' }
})
This results in https://api.nuxtjs.dev/mountains?param1=value1&param2=value2
Nuxt3's useFetch uses caching by default. Use initialCache: false option to disable it:
const getUsers = async (limit, skip) => {
const { data: users } = await useFetch(
`https://dummyjson.com/users?limit=${limit}&skip=${skip}`,
{
initialCache: false,
}
);
//returning fetched value
return users.value.users;
};
But you probably should use plain $fetch instead of useFetch in this scenario to avoid caching:
const getUsers = async (limit, skip) => {
const { users } = await $fetch(
`https://dummyjson.com/users?limit=${limit}&skip=${skip}`
);
//returning fetched value
return users;
};

Add Route Parameter to all Razor Pages in an Area

I'm trying to add a route parameter to all pages in a Razor Pages Area so every URL within an Area has an OrgId e.g. /dashboard/{orgId}/{page}/{route}. I can add them using the AddAreaPageRoute as shown below, but I can't help feeling there's a way to apply this to all pages without having to define an entry for every page in the Area. Is there a way to create a route for all pages in an Area?
.AddRazorPages(options =>
{
options.Conventions.AddAreaPageRoute("Dashboard", "/Index", "Dashboard/{orgId}");
options.Conventions.AddAreaPageRoute("Dashboard", "/AddItem", "Dashboard/{orgId}/AddItem");
options.Conventions.AddAreaPageRoute("Dashboard", "/Items", "Dashboard/{orgId}/Items");
options.Conventions.AddAreaPageRoute("Dashboard", "/Items/Index", "Dashboard/{orgId}/Items/{id}");
})
You can change your code like below:
services.AddRazorPages(options =>
{
options.Conventions.AddAreaFolderRouteModelConvention("Dashboard", "/", model =>
{
foreach (var selector in model.Selectors)
{
var c = selector.AttributeRouteModel.Template.ToString();
selector.AttributeRouteModel = new AttributeRouteModel
{
Order = -1,
Template =c.Replace("Dashboard", "Dashboard/{orgId}")
};
}
});
});
You can see the details in the doc.

Vue axios delete request not working. How do I fix it?

Im having issues with delete request, my post, get are working fine.
What am I doing wrong?
removeUser(id) {
axios.delete('https://jsonplaceholder.typicode.com/users' + id)
.then(function(response) {
const user = response.data;
this.users.splice(id, user);
});
if response.status === 204, then delete is succeed.
for the client, here is an axios example, notice there is a ' after users
destroy() {
return request.delete('/api/users/' + id)
}
for the server, here is an Laravel example:
if( $article->delete() ) {
return response()->json(null, 204);
} else {
abort(409);
}
I can see only 1 problem on the code you provided.
You're trying to modify the Vue instance $data users object by executing this.users.splice(id, user);. But you're inside the callback function and this no longer represents the Vue instance.
To fix this & make the users object actually modify after the response comes you'll need to do it like this :
removeUser(id) {
let that = this;
axios.delete('https://jsonplaceholder.typicode.com/users' + id)
.then(function(response) {
const user = response.data;
that.users.splice(id, user);
});
Now , I don't have any code from the back-end so I'll just make some assumptions :
The route might not be well defined > if you're using NodeJS then you should check your routes , it should look like this :
router.route('/users:id').delete(async function(req,res,next){ /* ... */ });
You might have a route problem because / is missing before the user value
1 hint : Again , if you're using NodeJS , you could use this inside your .delete route :
res.status(200).json({ errorCode : null , errorMessage : null , users : [] });
To see if you're receiving it on front-end.
I think you do need to append the trailing '/' to the URL, that way the URL is properly formed, such as "https://jsonplaceholder.typicode.com/users/123" (rather than "users123" at the end).
Aside from that, the first parameter to Array.prototype.splice is the position where item removal should begin. The second (optional) parameter, deleteCount, is the number of items to remove. Beyond deleteCount, you can pass a collection of objects which are to be inserted after the start position and after items have been removed.
You just need to find the object in your this.users array and remove it. If you want to use Array.prototype.splice for that, then you can use Array.prototype.findIndex to find the index of the user in the array then remove it:
// Find the index of the item to remove
const indexOfUserToRemove = this.users.findIndex(u => u.id === id);
// Call splice to remove the item
this.users.splice(indexOfUserToRemove, 1);

How to solve duplicate page in ion-infinite-scroll ionic 4

I am using ionic 4 and I am doing pagination using ion-infinite-scroll. My problem is I always get the duplicate page problem. Can I know how to solve this duplicate problem? Here is my code in home.page.ts:
doInfinite(event) {
this.userService.getData().then(res => {
event.target.complete();
});
}
loadData(event) {
console.log('Load more data');
this.userService.getData().then(res => {
event.target.complete();
});
}
Here is home.html
<ion-infinite-scroll (ionInfinite)="loadData($event)">
<ion-infinite-scroll-content
loadingSpinner="bubbles"
loadingText="loading ...">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
It depends what your userService.getData() looks like.
It doesn't look like you are telling it to start at an offset.
Each time you pull data down, you should assign that list data to some local on-page variable, let's say dataList.
Then use this.dataList.length as the starting index for your next data request.
So some pseudo-code for how this might work would be:
let dataFeed = [];
let startAtRecord = 0;
constructor() {
this.userService.getData(startAtRecord).then(res => {
this.dataFeed = res;
this.startAtRecord = this.dataService.length;
});
}
loadData(event) {
// ask for a batch of records, starting at `startAtRecord`
this.userService.getData(startAtRecord).then(res => {
// add the new res data to the existing dataFeed
this.dataFeed = [...this.dataFeed, ...res];
// keep track of the number of records loaded
this.startAtRecord = this.dataService.length;
event.target.complete();
});
}
Do you see what I'm saying? The data service has to load the next page of data so you don't get the same one back, so it needs to track where its starting the list from.

Pass data-attribute value of clicked element to ajax settings

For an implementation of Magnific Popup, I need to pass a post id to the ajax settings. The post id is stored in a data attribute of the element to which Magnific Popup is bound. I would like this to work:
html element:
<a data-id="412">Clicke me</a>
Javascript:
$('.element a').magnificPopup({
type: 'ajax',
ajax: {
settings: {
url: php_array.admin_ajax,
type: 'POST',
data: ({
action:'theme_post_example',
id: postId
})
}
}
});
Where postId is read from the data attribute.
Thanks in advance.
$('.element a').magnificPopup({
callbacks: {
elementParse: function(item){
postData = {
action :'theme_post_example',
id : $(item.el[0]).attr('data-id')
}
var mp = $.magnificPopup.instance;
mp.st.ajax.settings.data = postData;
}
},
type: 'ajax',
ajax: {
settings: {
url: php_array.admin_ajax,
type: 'POST'
}
}
});
Here is how to do it:
html:
<a class="modal" data-id="412" data-action="theme_post_example">Click me</a>
jquery:
$('a.modal').magnificPopup({
type: 'ajax',
ajax: {
settings: {
url : php_array.admin_ajax,
dataType : 'json'
}
},
callbacks: {
elementParse: function() {
this.st.ajax.settings.data = {
action : this.st.el.attr('data-action'),
id : this.st.el.attr('data-id')
}
}
},
parseAjax: function( response )
{
response.data = response.data.html;
}
});
php
function theme_post_example()
{
$id = isset( $_GET['id'] ) ? $_GET['id'] : false;
$html = '<div class="white-popup mfp-with-anim">';
/**
* generate your $html code here ...
*/
$html .= '</div>';
echo json_encode( array( "html" => $html ) );
die();
}
As this answer was the original question regarding inserting data into Magnific's ajax call, I'll post this here.
After many hours of trying to figure this out, you should know that if you're using a gallery with the ability to move between gallery items without closing the popup, using elementParse to set your AJAX data will fail when you visit an item after already viewing it (while the popup is still open).
This is because elementParse is wrapped up in a check that it makes detect if an item has already been 'parsed'. Here's a small explanation as to what happens:
Open gallery at item index 2.
Item has not been parsed yet, so it sets the parsed flag to true and runs the elementParse callback (in that order). Your callback sets the ajax options to fetch this item's data, all is well.
Move (right) to item index 3.
Same as above. The item has not been parsed, so it runs the callback. Your callback sets the data. It works.
Move (left) back to item index 2.
This time the item has been parsed. It skips re-parsing the item's element for assumed potential performance reasons.Your callback is not executed. Magnific's ajax data settings will remain the same as if it were item index 3.
The AJAX call is executed with the old settings, it returns with item index 3's data instead, which is rendered to the user. Magnific will believe it is on index 2, but it is rendering index 3's data.
To resolve this, you need to hook onto a callback which is always executed pre-ajax call, like beforeChange.
The main difference is that the current item isn't passed through into the callback. Fortunately, at this point, magnific has updated their pointers to the correct index. You need to fetch the current item's element by using:
var data = {}; // Your key-value data object for jQuery's $.ajax call.
// For non-closures, you can reference mfp's instance using
// $.magnificPopup.instance instead of 'this'.
// e.g.
// var mfp = $.magnificPopup.instance;
// var itemElement = mfp.items[mfp.index].el;
var itemElement = this.items[this.index].el;
// Set the ajax data settings directly.
if(typeof this.st.ajax.settings !== 'object') {
this.st.ajax.settings = {};
}
this.st.ajax.settings.data = data;
This answer can also be used as a suitable alternative to the currently highest voted, as it will work either way.
You may use open public method to open popup dynamically http://dimsemenov.com/plugins/magnific-popup/documentation.html#public_methods
postId = $(this).attr('data-id')
$(this) retrieve the current element (the link you clicked on), and attr the value of the specified attribute.