Filter collection by multi-reference field - velo

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:)

Related

How to get results using a loop inside of switch map

I have the next code, and it was working properly. to execute a request to my method fetchDropdownDataByFederationId, but now I have a requirement to execute the same method x number of times.
fetchInProgress(queryString?): Observable<IPerson[]> {
let PersonList: IPerson[] = [];
return this.getItems<IPerson[]>('', queryString).pipe(
take(1),
switchMap((wls: IPerson[]) => {
PersonList = [...wls];
//const createdbyIds = [...new Set(wls.map((f) => f.createdBy))];
return this.teamPageService.getInformation(wls.createdBy);
}),
map((teams:any) => {
console.log('> teams', teams);
for (let i = 0; i < PersonList.length; i++) {
//update information
}
//console.log('> Final value: ', PersonList);
return PersonList;
})
);
}
But, I'm not finding a way to execute my SwitchMap x number of times and get the results back to use them in my map method to parse the information.
I just moved my SwitchMap to mergeMap, something like this:
mergeMap((wls: IWalklist[]) => {
//let allIds = wls.contact.map(id => this.getSingleData(id._id) );
let drops: Dropdown[] = [];
walklistList = [...wls];
const allIds = [...new Set(wls.map((f) => f.createdBy))];
return forkJoin(...allIds).pipe(
map((idDataArray) => {
drops.push(
this.teamPageService.getInformation('');
);
return drops;
})
)
}),
But still no luck.
Can some help me? how can I fix it?

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());
});
};

local storage value gets overwritten

I have initiated 2 localstorage variables within a typescript function. The second variable holding the value overwrites the first variable. How do I fix it?
OnSelectedOppPropStatsChange(TypeId: string, StrSelectedValue: string): void {
this.appService.SetLoadingShow(true);
var url = this.configs.DashboardDemandStatsURL();
var Input = {
"TypeId": TypeId,
"PS_No": this.user.PS_No,
"StrSelectedValue": StrSelectedValue
};
localStorage.setItem(this.strTypeSelected, StrSelectedValue);
localStorage.setItem(this.strTypeIdValue, TypeId);
this.appService.GetDataFromAPIPost(url, Input)
.then(response => {
this.appService.SetLoadingShow(false);
if (response.ResponseCode == this.configs.RetCodeFailure()) {
this.ShowDemandDetails = false;
this.errorMessage = response.ResponseData;
this.appService.ShowMessagePopup(this.configs.MESSAGETYPEERROR(), this.errorMessage);
}
else {
this.OpenDemandStatsDetails = JSON.parse(response.ResponseData.strDemandStatsOpen);
this.TeamFulfilledStatsDetails = JSON.parse(response.ResponseData.strDemandStatsTeam);
this.RPMFulfilledStatsDetails = JSON.parse(response.ResponseData.strDemandStatsRPM);
this.TotalRRCount = this.OpenDemandStatsDetails.length + this.TeamFulfilledStatsDetails.length + this.RPMFulfilledStatsDetails.length;
}
},
error => { this.errorMessage = <string>error; this.appService.SetLoadingShow(false) });
}
OnClickOpenRRNavigate(): void {
let SelectedItem = localStorage.getItem(this.strTypeSelected);
let SelectedType = localStorage.getItem(this.strTypeIdValue);
this.appService.SetLoadingShow(true);
var url = this.configs.DashboardDemandStatsTableURL();
var Input = {
"TypeId": SelectedType,
"PS_No": this.user.PS_No,
"StrSelectedValue": SelectedItem,
"strRRAllocationValue": this.StrOpenValue
};
this.appService.GetDataFromAPIPost(url, Input)
.then(response => {
this.appService.SetLoadingShow(false);
if (response.ResponseCode == this.configs.RetCodeFailure()) {
this.errorMessage = response.ResponseData;
this.appService.ShowMessagePopup(this.configs.MESSAGETYPEERROR(), this.errorMessage);
}
else {
this.DemandTableDetails = JSON.parse(response.ResponseData.strDemandStatsTable);
this.ShowDemandTable = true;
}
},
error => { this.errorMessage = <string>error; this.appService.SetLoadingShow(false) });
}
In the function OnClickOpenRRNavigate() , the SelectedType and SelectedItem holds the same value. How can I fix it?

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 avoid infinite loop while updating self in a BehaviorSubject?

Declared like this:
public panel$: BehaviorSubject<any> = new BehaviorSubject<any>(false);
Used like this
togglepanel() {
this.panel$.subscribe(
(x) => {
if (x) {
this.panel$.next(false);
} else {
this.panel$.next(true);
}
});
}
It creates an endless cycle trying to update self.
You can update it by taking only one(latest) value from the panel$ Observable:
this.panel$.take(1).subscribe(...)
But it is better to model your state a bit differently, like this:
// const onToggle$ = new Rx.Subject();
var toggle$ = Rx.Observable.fromEvent(document, 'click');
const initialValue = true;
const state$ = toggle$
.scan(state => !state, initialValue)
.startWith(initialValue);
const htmlSubscription = state$.subscribe(state => {
document.getElementById('toggle').innerText = 'toggle: ' + state;
});
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>
<button id="toggle">loading...</button>
EDIT:
Angular version of this code is:
public toggle$ = new Subject();
public state$ = this.toggle$.scan(state => !state, true)
.startWith(true)
.subscribe((x) => console.log('x:' + x));
togglepanel() {
this.toggle$.next(null);
}

Resources