WIX: Sort Function on Repeater Works in Preview but not Live Site - velo

I have a repeater that is connected to a dataset. The dataset is told to sort by a numerical ranking (high to low). When I preview, it works great. When I go live, I can see that it loads correctly, and then reverts to sorting by date (newest to oldest). Any idea why the Sort Function is getting overwritten?
import wixData from 'wix-data';
const collectionName = 'Projects';
const fieldToFilterByInCollection = 'tags';
$w.onReady(function () {
setRepeatedItemsInRepeater(100);
loadDataToRepeater(100);
$w('#selectionTags1').onChange((event) => {
const selectedTags = $w('#selectionTags1').value;
loadDataToRepeater(selectedTags);
})
});
function loadDataToRepeater(selectedCategories = []) {
let dataQuery = wixData.query(collectionName);
if (selectedCategories.length > 0) {
dataQuery = dataQuery.hasSome(fieldToFilterByInCollection, selectedCategories);
}
dataQuery
.find()
.then(results => {
const itemsReadyForRepeater = results.items;
$w('#repeater1').data = itemsReadyForRepeater;
const isRepeaterEmpty = itemsReadyForRepeater.length === 0
if (isRepeaterEmpty) {
$w('#container1').show();
} else {
$w('#container1').show();
}
})
}
function setRepeatedItemsInRepeater() {
$w('#repeater1').onItemReady(($item, itemData) => {
$item('#container1').src = itemData.projectimage;
$item('#container1').tooltip = itemData.projectimage;
})
}

The Wix Data Query function overrides the sort parameter and resets it to sort descending by created data. When you are building the Query before you find, use the descending function or ascending function to keep it set to your desired field.
wixData.query("collectionName").descending("fieldName").find()

Related

MERN Pagination & Sorting

hopefully, you guys will be able to help me out please.
I can't get the pagination to work properly. It always counts the total documents and ignores the filter data. For example, there are 24 total documents, but when filters by a particular item it returns one, but still returns the total amount of pages (which is 3 as I have pageSize set to 9).
Please find my code below:
router.get('/', async (req, res) => {
try {
const pageSize = 9;
const page = Number(req.query.page) || 1;
let query;
const queryObject = { ...req.query };
const excludeFields = ['page', 'sort', 'limit', 'fields'];
excludeFields.forEach((el) => delete queryObject[el]);
let queryString = JSON.stringify(queryObject);
queryString = queryString.replace(
/\b(gte|gt|lte|lt)\b/g,
(match) => `$${match}`
);
query = Vehicle.find(JSON.parse(queryString));
if (req.query.sort) {
const sortBy = req.query.sort.split(',').join(' ');
query = query.sort(sortBy);
} else {
query = query.sort('-createdAt');
}
const count = await Vehicle.countDocuments();
const vehicles = await Vehicle.find(JSON.parse(queryString))
.limit(pageSize)
.skip(pageSize * (page - 1));
if (!vehicles) {
return res.status(200).json({ success: true, data: [] });
}
res
.status(200)
.json({ vehicles, page, totalPages: Math.ceil(count / pageSize) });
} catch (error) {
console.error(error.message);
res.status(500).send('Server Error');
}
});
How would you go about adding functioning sorting into this also please, for some reason sorting doesn't work at all for me?
Thanks very much, G.
To get the count to include the filtered data you need to add the query parameter in the countDocuments:
const count = await Vehicle.countDocuments(JSON.parse(queryString));
To add sorting functionality you can append .sort() onto the end of you find query:
const vehicles = await Vehicle.find(JSON.parse(queryString))
.limit(pageSize)
.skip(pageSize * (page - 1))
.sort(<your sort query>)
https://www.mongodb.com/community/forums/t/sorting-with-mongoose-and-mongodb/122573

How can I capture all the values of the dropdown list in Cypress?

I have a dropdown box that displays the list of States. There are around 40 States in the list.
Every time when I scroll down the list, the List displays only 15 to 20 States at a time.
I want to capture all the values of the list and save them in the string array. And then check alphabet sorting.
How can I do it using Cypress? Currently, It captures only the top 15 items from the list.
This is my code:
const verifySortOrdering = (key: string) =>
getSingleSelectList(key).then(dropdown => {
cy.wrap(dropdown).click();
if (dropdown.length > 0) {
const selector = 'nz-option-container nz-option-item';
let NumOfScroll = 1;
const unsortedItems: string[] = [];
const sortedItems: string[] = [];
cy.get(selector).then((listItem) => {
while (NumOfScroll < 7) {
sortAndCheck(selector, unsortedItems, sortedItems);
if (listItem.length < 15) {
break;
}
NumOfScroll++;
}
});
}
});
const sortAndCheck = (selector: string, unsortedItems: any, sortedItems: any) => {
cy.get(selector).each((listItem, index) => {
if (index === 15) {
cy.wrap(listItem).trigger('mousedown').scrollIntoView().last();
}
unsortedItems.push(listItem.text());
sortedItems = unsortedItems.sort();
expect(unsortedItems, 'Items are sorted').to.deep.equal(sortedItems);
});
};
Here's a working example based off of what you provided. Added an additional check to ensure the list has the right amount of options. You may or may not want that. Deep copying the unsorted list so it doesn't get sorted due to a shallow copy. Added validations after the unsortedItems list gets built so we can validate once instead of for every item in the list.
var unsortedItems = new Array()
var expectedListCount = 32
cy.get('#myselect>option').should('have.length', expectedListCount)
.each(($el) => {
unsortedItems.push($el.text());
}).then(() => {
var sortedItems = [...unsortedItems]; // deep copy
sortedItems.sort();
expect(unsortedItems).to.deep.equal(sortedItems)
})
Another example based on your revised sample but I can't verify it without having a working example of your DDL. This builds up the unsortedItem list first and then does the comparison.
const verifySortOrdering = (key: string) =>
getSingleSelectList(key).then(dropdown => {
cy.wrap(dropdown).click();
if (dropdown.length > 0) {
const selector = 'nz-option-container nz-option-item';
let NumOfScroll = 1;
const unsortedItems: string[] = [];
const sortedItems: string[] = [];
cy.get(selector).then((listItem) => {
while (NumOfScroll < 7) {
unsortedListBuilder (selector, unsortedItems, sortedItems);
if (listItem.length < 15) {
break;
}
NumOfScroll++;
}
});
var sortedItems = [...unsortedItems];
sortedItems.sort();
expect(unsortedItems).to.deep.equal(sortedItems);
}
});
const unsortedListBuilder = (selector: string, unsortedItems: any, sortedItems: any) => {
cy.get(selector).each((listItem, index) => {
if (index === 15) {
cy.wrap(listItem).trigger('mousedown').scrollIntoView().last();
}
unsortedItems.push(listItem.text());
});
};

Filter collection by multi-reference field

I'm trying to filter a collection by a multireference field before the function does its job.
I used this wix example but I don't want it to filter the whole collection https://www.wix.com/corvid/example/filter-with-multiple-options
I'm new at this and probably doing it wrong this is what i managed to figure out
import wixData from 'wix-data';
const collectionName = 'Blog/Posts'
//const collectionName = wixData.query('Blog/Posts').contains("categories", ["O -Fitness"]);
const fieldToFilterByInCollection = 'hashtags';
$w.onReady(function () {
setRepeatedItemsInRepeater()
loadDataToRepeater()
$w('#tags').onChange((event) => {
const selectedTags = $w('#tags').value
loadDataToRepeater(selectedTags)
})
});
function loadDataToRepeater(selectedCategories = []) {
let dataQuery = wixData.query(collectionName)//.contains("categories", ["O -Fitness"]);
if (selectedCategories.length > 0) {
dataQuery = dataQuery.hasAll(fieldToFilterByInCollection, selectedCategories)
}
dataQuery
.find()
.then(results => {
const itemsReadyForRepeater = results.items
$w('#Stories').data = itemsReadyForRepeater;
const isRepeaterEmpty = itemsReadyForRepeater.length === 0
if (isRepeaterEmpty) {
$w('#noResultsFound').show()
} else {
$w('#noResultsFound').hide()
}
})
}
function setRepeatedItemsInRepeater() {
$w('#Stories').onItemReady(($item, itemData) => {
$item('#image').src = itemData.coverImage;
$item('#title').text = itemData.title;
if ($item("#title").text.length > 40){
$item("#title").text =$item("#title").text.slice(0, 40) + '...' ;}
$item('#excerpt').text = itemData.excerpt;
if ($item('#excerpt').text.length > 100){
$item('#excerpt').text =$item('#excerpt').text.slice(0, 100) + '...' ;}
})
}
its this commented bit I'm trying to add
const collectionName = wixData.query('Blog/Posts').contains("categories", ["O -Fitness"]);
Thanks in advance
You used 'hasAll' to filter multi-refernce field. 'hasSome' working on multi-refernce but 'hasAll' isnt working on this field type.
you can use:
selectedCategories.map(category => {
dataQuery = dataQuery.hasSome(fieldToFilterByInCollection, category)
})
which is the same as hasAll - hasSome(x) & hasSome(Y) = hasAll(x,y) - but beacuse 'hasSome' is working on multirefernce it will work:)

Angularfire2 & Firestore – retrieve all subcollection content for a collection list

I try to retrieve datas in a subcollection based on the key received on the first call.
Basically, I want a list of all my user with the total of one subcollection for each of them.
I'm able to retrieve the data from the first Payload, but not from pointRef below
What is the correct way to achieve that?
getCurrentLeaderboard() {
return this.afs.collection('users').snapshotChanges().map(actions => {
return actions.map(a => {
const data = a.payload.doc.data()
const id = a.payload.doc.id;
const pointRef: Observable<any> = this.afs.collection('users').doc(`${id}`).collection('game').valueChanges()
const points = pointRef.map(arr => {
const sumPoint = arr.map(v => v.value)
return sumPoint.length ? sumPoint.reduce((total, val) => total + val) : ''
})
return { id, first_name: data.first_name, point:points };
})
})
}
I tried to put my code in a comment, but I think it's better formated as a answer.
First you need subscribe your pointRef and you can change your code like this.
getCurrentLeaderboard() {
return this.afs.collection('users').snapshotChanges().map(actions => {
return actions.map(a => {
const data = a.payload.doc.data()
const id = a.payload.doc.id;
const pointRef: Observable<any> = this.afs.object(`users/${id}/game`).valueChanges() // <--- Here
const pointsObserver = pointRef.subscribe(points => { //<--- And Here
return { id, first_name: data.first_name, point:points };
})
})
}
....
//Usage:
getCurrentLeaderboard.subscribe(points => this.points = points);
And if you going to use this function alot, you should start to denormalize your data.

How to batch additions to arrays/lists returned by rxjs observables?

have an observable that returns arrays/lists of things: Observable
And I have a usecase where is is a pretty costly affair for the downstream consumer of this observable to have more items added to this list. So I'd like to slow down the amount of additions that are made to this list, but not loose any.
Something like an operator that takes this observable and returns another observable with the same signature, but whenever a new list gets pushed on it and it has more items than last time, then only one or a few are added at a time.
So if the last push was a list with 3 items and next push has 3 additional items with 6 items in total, and the batch size is 1, then this one list push gets split into 3 individual pushes of lists with lengths: 4, 5, 6
So additions are batched, and this way the consumer can more easily keep up with new additions to the list. Or the consumer doesn't have to stall for so long each time while processing additional items in the array/list, because the additions are split up and spread over a configurable size of batches.
I made an addAdditionalOnIdle operator that you can apply to any rxjs observable using the pipe operator. It takes a batchSize parameter, so you can configure the batch size. It also takes a dontBatchAfterThreshold, which stops batching of the list after a certain list size, which was useful for my purposes. The result also contains a morePending value, which you can use to show a loading indicator while you know more data is incomming.
The implementation uses the new requestIdleCallback function internally to schedule the batched pushes of additional items when there is idle time in the browser. This function is not available in IE or Safari yet, but I found this excelent polyfill for it, so you can use it today anyways: https://github.com/aFarkas/requestIdleCallback :)
See the implementation and example usage of addAdditionalOnIdle below:
const { NEVER, of, Observable } = rxjs;
const { concat } = rxjs.operators;
/**
* addAdditionalOnIdle
*
* Only works on observables that produce values that are of type Array.
* Adds additional elements on window.requestIdleCallback
*
* #param batchSize The amount of values that are added on each idle callback
* #param dontBatchAfterThreshold Return all items after amount of returned items passes this threshold
*/
function addAdditionalOnIdle(
batchSize = 1,
dontBatchAfterThreshold = 22,
) {
return (source) => {
return Observable.create((observer) => {
let idleCallback;
let currentPushedItems = [];
let lastItemsReceived = [];
let sourceSubscription = source
.subscribe({
complete: () => {
observer.complete();
},
error: (error) => {
observer.error(error);
},
next: (items) => {
lastItemsReceived = items;
if (idleCallback) {
return;
}
if (lastItemsReceived.length > currentPushedItems.length) {
const idleCbFn = () => {
if (currentPushedItems.length > lastItemsReceived.length) {
observer.next({
morePending: false,
value: lastItemsReceived,
});
idleCallback = undefined;
return;
}
const to = currentPushedItems.length + batchSize;
const last = lastItemsReceived.length;
if (currentPushedItems.length < dontBatchAfterThreshold) {
for (let i = 0 ; i < to && i < last ; i++) {
currentPushedItems[i] = lastItemsReceived[i];
}
} else {
currentPushedItems = lastItemsReceived;
}
if (currentPushedItems.length < lastItemsReceived.length) {
idleCallback = window.requestIdleCallback(() => {
idleCbFn();
});
} else {
idleCallback = undefined;
}
observer.next({
morePending: currentPushedItems.length < lastItemsReceived.length,
value: currentPushedItems,
});
};
idleCallback = window.requestIdleCallback(() => {
idleCbFn();
});
} else {
currentPushedItems = lastItemsReceived;
observer.next({
morePending: false,
value: currentPushedItems,
});
}
},
});
return () => {
sourceSubscription.unsubscribe();
sourceSubscription = undefined;
lastItemsReceived = undefined;
currentPushedItems = undefined;
if (idleCallback) {
window.cancelIdleCallback(idleCallback);
idleCallback = undefined;
}
};
});
};
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
let testSource = of(
[1,2,3],
[1,2,3,4,5,6],
).pipe(
concat(NEVER)
);
testSource
.pipe(addAdditionalOnIdle(2))
.subscribe((list) => {
// Simulate a slow synchronous consumer with a busy loop sleep implementation
sleep(1000);
document.body.innerHTML += "<p>" + list.value + "</p>";
});
<script src="https://unpkg.com/rxjs#6.5.3/bundles/rxjs.umd.js"></script>

Resources