Meteor js custom pagination - meteor

I kindly need a support. I have written a pagination following a YouTube tutorial, which works fine with the exception of when going backwards again. It has only 2 buttons, previous and next, when the next button is clicked it works fine but the previous button only go backwards once. Let's say I have 20 records in the collections paginated to display 5 at a time, the next button could go to the end to the fourth page but the previous button would not go pass one step backwards. Please what am I to do to have pagination experience? The previous button is suppose to navigate to the last page as long as the user clicks.
Events for the pagination buttons:
Template.myviews.events({
'click .previous': function () {
if (Session.get('skip') > 5 ) {
Session.set('skip', Session.get('skip') - 5 );
}
},
'click .next': function () {
Session.set('skip', Session.get('skip') + 5 );
}
});
Publication:
Meteor.publish('userSchools', function (skipCount) {
check(skipCount, Number);
user = Meteor.users.findOne({ _id: this.userId });
if(user) {
if(user.emails[0].verified) {
return SchoolDb.find({userId: Meteor.userId()}, {limit: 5, skip: skipCount});
} else {
throw new Meteor.Error('Not authorized');
return false;
}
}
});
Subscription:
Session.setDefault('skip', 0);
Tracker.autorun(function () {
Meteor.subscribe('userSchools', Session.get('skip'));
});
Blaze pagination buttons:
<ul class="pager">
<li class="previous">Previous </li>
<li class="next">Next </li>
</ul>
Template helper:
RenderSchool: function () {
if(Meteor.userId()) {
if(Meteor.user().emails[0].verified) {
return SchoolDb.find({userId: Meteor.userId()}).fetch().reverse();
} else {
FlowRouter.go('/');
Bert.alert('Please verify your account to proceed', 'success', 'growl-top-right');
}
}
}

You have 6 documents total with 2 docs per page, 3 pages in total.
Your if condition in previous button click handler prevents you from going to the first page:
if (Session.get('skip') > 2 /* testing */ ) {
...
}
For 2nd page skip will equal 2 and on the next click this condition will be false, preventing from going back.
When you're on the 3rd page — you can go on 2nd only, creating an impression that button works only once.
It should be like this:
if (Session.get('skip') > 0 ) {
...
}

Related

Saleor Shopping Cart Sometimes Wrong/Older Version - Using GraphQL and Next.js

I am using Saleor shopping cart GraphQL, and actually Stepzen to merge it with a Strapi GraphQL. So all Saleor mutations and queries go through the Stepzen GraphQL.
I have followed Saleors tutorial here:
https://learn.saleor.io/checkout/displaying-cart-content/
This works 90% of the time so generally the code should be OK, but I must be missing something because occasionally when adding to the cart the contents are different. The item is not added. In fact, it's possible the cart seen is an older version but am not sure. I could add to cart 4 times and it updates to the correct products and quantities each time, then the 5th time it shows the cart from the last time or even time before.
After a few seconds, if you refresh the page, the cart displays correct.
EDIT: I have just noticed, if you refresh the page several times, the cart sometimes changes, and a refresh changes back, then a refresh it changes to something else and just keeps changing on refreshes!
When the cart works, there is a little delay adding it, but when it doesn't work the cart page loads much faster, almost instantly. It's as if when it works it waits for Saleors response for the cart items list, but when it doesn't it doesn't wait for the response. Just a hunch.
This is my query code to get cart items from Saleor. It does work, 90% of the time.
const Cart = () => {
const [token] = useCookies('["token"]')
const { data, loading, error } = useCheckoutFetchByTokenQuery({
variables: { checkoutToken: token.token },
skip: !token.token,
});
const [CheckoutremoveProduct] = useCheckoutRemoveProductMutation();
if (loading) return <div>Loading...</div>;
if (error) return <div>Error</div>;
if (!data || !data.saleorcheckout) return null;
const products = data.saleorcheckout?.lines || [];
return (
<Layout title="Cart">
.....
</Layout>
);
}
The actual saleorcheckout query:
query CheckoutFetchByToken($checkoutToken: saleorUUID!) {
saleorcheckout(token: $checkoutToken) {
id
email
lines {
id
quantity
totalPrice {
gross {
amount
currency
}
}
variant {
id
product {
id
name
slug
thumbnail {
url
alt
}
cmsContent {
SaleorID
Title
Featured_image
{
data
{
attributes
{
url
width
height
alternativeText
}
}
}
}
}
pricing {
price {
gross {
amount
currency
}
}
}
name
}
}
totalPrice {
gross {
amount
currency
}
}
}
}
Interestingly the saleorcheckoutLinesAdd mutation which adds lines to the cart returns with the correct products in the cart, after the new one is added, but on the cart page that uses saleorcheckout to fetch them returns a lower amount, until you refresh later.
The actual saleorcheckoutLinesAdd mutation:
mutation ProductAddVariantToCart($checkoutToken: saleorUUID!, $variantId: ID!) {
saleorcheckoutLinesAdd(
token: $checkoutToken
lines: [{ quantity: 1, variantId: $variantId }]
) {
checkout {
id
lines {
id
quantity
variant {
id
name
product {
name
}
}
}
}
errors {
message
}
}
}
Thanks
UPDATE:
My first post above included this below, but it may be a red herring as I have added the variant ID in the query now, and dont get this error below anymore, but the problem above still exists.
Previously when it doesn't work the following error was in console:
react_devtools_backend.js:4026 Cache data may be lost when replacing the variant field of a saleorCheckoutLine object.
To address this problem (which is not a bug in Apollo Client), either ensure all objects of type saleorProductVariant have an ID or a custom merge function, or define a custom merge function for the saleorCheckoutLine.variant field, so InMemoryCache can safely merge these objects:
existing: {"__typename":"saleorProductVariant","product":{"__ref":"saleorProduct:UHJvZHVjdDoxNjY="},"pricing":{"__typename":"saleorVariantPricingInfo","price":{"__typename":"saleorTaxedMoney","gross":{"__typename":"saleorMoney","amount":100,"currency":"USD"}}},"name":"UHJvZHVjdFZhcmlhbnQ6NDAy"}
incoming: {"__typename":"saleorProductVariant","name":"UHJvZHVjdFZhcmlhbnQ6NDAy","product":{"__typename":"saleorProduct","name":"52-00 Base Plate"}}
I have looked this up, and the solution is to set typePolicies the ApolloClient to state to merge the new and old carts. BUT I cannot get this to work, if it is the solution. Online some examples of typePolicies and merge have console.log() outputs but mine are just not firing. So I am assuming I have not set the typePolicies up correct perhaps.
This is my typePolicies code, although I have tried many variations, and the fact lines is nested may be complicating it.
const client = new ApolloClient({
uri: "https://my-stepzen-graphql-url.com/__graphql",
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
saleorcheckoutLinesAdd: {
lines: {
merge(existing, incoming, { mergeObjects }) {
console.log(existing)
console.log(incoming)
return mergeObjects(existing, incoming);
},
},
},
},
},
},
}),
})
I have tried many versions of the above typePolicies, but can never get console.log to fire, so assume I have fond the correct format.

fullcalendar when render resources, all-day lines don't lined up

Previous post which lead to this issue: Fullcalendar using resources as a function with select menu
Based on my previous post, I have an issue using fullcalendar 4. When I am using resources as a function, my all-day blocks do not line up with my scheduler time slots. You can see it in the picture.
Here's my resources function:
resources: function(fetchInfo, successCallback, failureCallback) {
// Filter resources by whether their id is in visibleResourceIds.
var filteredResources = [];
filteredResources = resourceData.filter(function(x) {
return visibleResourceIds.indexOf(x.id) !== -1;
});
successCallback(filteredResources);
},
Here's my toggleresource function:
// menu button/dropdown will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
visibleResourceIds = [];
//if select all... see if undefined from loading on initial load = true
if ((resourceId == '') || (resourceId === undefined)) {
$.map( resourceData, function( value, index ) {
visibleResourceIds.push(value.id);
});
}
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
calendar.refetchResources();
Other related code (when the menu changes, the resources of the selected menu item show only in fullcalendar):
var resourceData = [];
var visibleResourceIds = [];
$.getJSON('ajax_get_json.php?what=schedule_providers_at_location',
function(data) {
$.each(data, function(index) {
resourceData.push({
id: data[index].value,
title: data[index].text
});
});
});
$('#toggle_providers_calendar').change(function() {
toggleResource($('#toggle_providers_calendar').val());
});
The resources show/hide just fine based on the selected menu resource, but look at the allday blocks - they don't line up after the resources are refetched for some reason. They correct themselves as the user navigates the scheduler though!
UPDATE BELOW
After looking around it looks like when refetchevents is called, the class .fc-week loses the following css:
style="border-right-width: 1px; margin-right: 20px;"
Here's a full pic of the calendar on initial load:
After I click a one of the navigation arrow, the all-day lines meet up with the rest of the calendar times because that style is applied to .fc-week.
I don't have any special css applied to the calendar and I am not using any themes that would get rid of this: at least not that I see now.
Here's the html that houses the calendar:
<div class="portlet-body">
<div class='loader'></div>
<div class="row">
<div id="calendar_full" style="padding-left: 10px; padding-right: 15px;"></div>
</div>
</div>
In order to fix this, I can add this following line after the resources are refetched in my toggleResources function:
$('#calendar_full .fc-week').css('border-right-width', '1px').css('margin-right', '20px');
I am going to keep looking as to why this css disappears after the resources are refetched. I wonder if it could be a glitch?

WordPress Customizer API - Toggle second checkbox if first is selected: Not saving

I have 2 checkboxes. When 1 is selected then the other one is un-checked. I have used JS to accomplish this and it works fine. But then when I click save, the option that is un-checked does not get saved. If I actually un-check the checkbox with a mouse click then it does save. What's the best way to accomplish the above situation?
PHP Action and JS that I am using:
add_action( 'customize_controls_print_footer_scripts', 'my_customizer_custom_scripts' );
function my_customizer_custom_scripts() { ?>
<script type="text/javascript">
jQuery(document).ready(function() {
/* This one shows/hides the an option when a checkbox is clicked. */
jQuery('#customize-control-checkbox1 input').click(function() {
if (jQuery('#customize-control-checkbox2 input:checked').val() !== undefined) {
jQuery('#customize-control-checkbox2 input').attr('checked', false);
}
});
jQuery('#customize-control-checkbox2 input').click(function() {
if (jQuery('#customize-control-checkbox1 input:checked').val() !== undefined) {
jQuery('#customize-control-checkbox1 input').attr('checked', false);
}
});
});
</script>
<?php
}
Figured it out have to use trigger('click') instead of the attr('checked', false).
So
jQuery('#customize-control-checkbox1 input').attr('checked', false);
Becomes
jQuery('#customize-control-checkbox1 input').trigger('click');

Meteor reactive variables: Got my reactive computation + reactive source, but not reacting

I'm trying to re-render a collection of words in a column every time a word is submitted or deleted. It re-renders when the word is deleted but not when a word is submitted.
Here's my template:
<template name = "wordColumn">
{{#each words}}
<button class = "label label-default" draggable="true">
{{word}}
</button>
{{/each}}
</template>
Here's my helper:
Template.wordColumn.helpers ( {
words: function () {
var words;
var wordIds = Session.get ( "wordIds" );
words = Words.find( { '_id': { $in: wordIds } }, { sort: { createdAt: -1 } } );
return words;
},
} )
And here is the word submit code within the template events. When I do Words.find(), it's clear that the underlying database has changed so Words.find() above should be reactive and update the column automatically:
"submit .new-word": function ( e ) {
var text = e.target.word.value;
Meteor.call ( "addWord" , text, function ( err, data ) {
Session.set ( "displayInMainBox", Words.find( data ).fetch()[0] );
} );
e.target.word.value = "";
return false;
}
Yet no cigar - the word I just submitted doesn't display automatically unless I refresh the page.
You can check out the deployed app here: contextual.meteor.com, and submit a new word. It'll show in the main box but not the list of all words on the right-hand side.
You need to have a Session.set( "wordIds"... somewhere in that callback. If that doesn't change, your cursor in your helper won't change. Since your current cursor includes all the current docs, when you delete something it will react.
PS, try doing away with the session var all together. Handle that logic in the subscription if possible (or sub to all, from the look of the app).

Knockout 2 way foreach binding DOM changes update Model

If I have a list on a page, and it is using knockout's foreach binding to display list items, then something else updates the DOM to add an extra list item. If there any way I can get knockout to detect that DOM change and update its model to add the new item to the observableArray?
Here is a fiddle which shows the problem...
http://jsfiddle.net/BGdWN/1/
function MyViewModel() {
this.items = ko.observableArray([
{ name: 'Alpha' }, { name: 'Beta' }, { name: 'Gamma' }, { name: 'Delta' }
]);
this.simpleShuffle = function() {
this.items.sort(function() {
return Math.random() - 0.5; // Random order
});
};
this.simpleAdd = function() {
$("#top").append("<li>New item</li>");
}
}
ko.applyBindings(new MyViewModel());
It has 2 lists bound to the same observableArray, click the addItem button and you can see that the DOM is updated to include the new list item in the top list, but I would like the second list to be updated too, all via the model.
It seems that knockout ignores DOM elements that it didnt render, you can see this by clicking the shuffle button, it leaves the new items there. I would have expected it to remove them and do a full re-render.
Please don't answer with "Just add the item to the observableArray"
Take a look at the first link and the second link Interface MutationEvent
See Fiddle
$('#top').bind('DOMNodeInserted DOMNodeRemoved', function () {
alert('Changed');
});
I hope it helps.

Resources