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);
}
Related
Following value is being retrieved in a Cypress custom command,
listOfResults = [{"name":"x","amount":"99"}, {"name":"y","amount":"88"}]
and the command is,
Cypress.Commands.add("getResultList", (keyWord, ...args) => {
var listOfResults = [];
cy.get('[class="result"]')
.each((resultItem) => {
var singleResult = {};
//Retrive Title
cy.wrap(resultItem)
.find('.name')
.invoke("text")
.then((val) => {
const title = val.replaceAll("\n", "");
singleResult.title = title;
});
//Retrive price
cy.wrap(resultItem)
.find('.price')
.invoke("text")
.then((val) => {
const price = val.replaceAll("\n", "");
singleResult.amount = price;
});
cy.then(() => {
listOfResults.push(singleResult);
});
})
.then(() => {
cy.log(listOfResults);//prints. correctly
cy.wrap(listOfResults);
//tried also return cy.wrap(listOfResults);
});
});
and in test, I am trying to access and store it.
//fetch all data in Search Results page and store it
var resultList = cy.getResultList();
cy.log("length:" + resultList.length);
But it is not getting stored and resultList.length logs undefined. How could we make the command to return a value?
I think just adding two returns will be ok,
Cypress.Commands.add("getResultList", (keyWord, ...args) => {
var listOfResults = [];
return cy.get('[class="result"]')
.each((resultItem) => {
var singleResult = {};
//Retrive Title
cy.wrap(resultItem)
.find('.name')
.invoke("text")
.then((val) => {
const title = val.replaceAll("\n", "");
singleResult.title = title;
});
//Retrive price
cy.wrap(resultItem)
.find('.price')
.invoke("text")
.then((val) => {
const price = val.replaceAll("\n", "");
singleResult.amount = price;
});
cy.then(() => {
listOfResults.push(singleResult);
});
})
.then(() => {
cy.log(listOfResults);//prints. correctly
cy.wrap(listOfResults);
return cy.wrap(listOfResults); // this modifies the outer return
});
});
Must be used in test with .then() like any command
cy.getResultList(keyword).then(resultList => {
...
To illustrate better the point Alapan makes, in a custom command the result of the last command is automatically returned.
So this is the minimum needed for your command
Cypress.Commands.add("getResultList", (keyWord, ...args) => {
var listOfResults = [];
cy.get('[class="result"]')
.each((resultItem) => {
var singleResult = {};
//Retrive Title
cy.wrap(resultItem)
.find('.name')
.invoke("text")
.then((val) => {
const title = val.replaceAll("\n", "");
singleResult.title = title;
});
//Retrive price
cy.wrap(resultItem)
.find('.price')
.invoke("text")
.then((val) => {
const price = val.replaceAll("\n", "");
singleResult.amount = price;
});
cy.then(() => {
listOfResults.push(singleResult);
});
})
cy.wrap(listOfResults) // last command, Cypress returns this
});
In custom command you have to do:
return cy.wrap(listOfResults)
Then in your test:
cy.getResultList(keyWord, ...args).then((listOfResults) => {
//Do something here with listOfResults
})
Since wrap() is returning a Cypress.Chainable, we can call .then() on our commands.
To summarize what I want to do:
Update the state depending on the previous state
I have searched in vain for a solution to the above problems. Found 3 solutions, unfortunately without any success.
1)
const Form = (props) => {
const [newValue, setNewValue] = useState(0);
const submitHandler = (e) => {
e.preventDefault();
const incrementOne = {
value: setNewValue((prevState) => {
return {...prevState, newValue: newValue + 1}
})
};
console.log(incrementOne);
};
const submitHandler = (e) => {
e.preventDefault();
const incrementOne = {
value: setNewValue(newValue + 1),
};
console.log(incrementOne);
};
3
const submitHandler = (e) => {
e.preventDefault();
const incrementOne = {
value: setNewValue(prevState => prevState + 1),
};
console.log(incrementOne);
};
Thank you in advance for your time and effort
Sincerely
/ Peter
In all your examples you are creating an object with a value property. You assume that is supposed to get it's value from calling set function returned by useState. However, the result of calling this function is updating the state, and re-rendering. The function itself doesn't return anything (undefined).
const incrementOne = {
value: setNewValue((prevState) => {
return {...prevState, newValue: newValue + 1}
})
};
You should call the setNewValue function when you want to update the value. You can calculate the new state using the previous one:
setNewValue(newValue + 1);
Or use a functional update to avoid depending on the state directly:
setNewValue(prevState => prevState + 1);
Note that the new value is only available after the component re-renders.
Example:
const { useState } = React;
const Form = (props) => {
const [newValue, setNewValue] = useState(0);
const submitHandler = () => {
setNewValue(prevState => prevState + 1);
};
const incrementOne = {
value: newValue,
};
console.log(incrementOne);
return (
<div>
<div>{newValue}</div>
<button onClick={submitHandler}>Submit</button>
</div>
);
}
ReactDOM.render(
<Form />,
root
)
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
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:)
To use selector, I tried to follow this URL reference: https://blog.isquaredsoftware.com/2017/12/idiomatic-redux-using-reselect-selectors/
One of the example is :
const selectSomeData = state => state.someData;
const selectFilteredSortedTransformedData = createSelector(
selectSomeData,
(someData) => {
const filteredData = expensiveFiltering(someData);
const sortedData = expensiveSorting(filteredData);
const transformedData = expensiveTransformation(sortedData);
return transformedData;
}
)
const mapState = (state) => {
const transformedData = selectFilteredSortedTransformedData(state);
return {
data: transformedData
};
}
Question: Within mapState we are calling selectFilteredSortedTransformedData and we are also passing State as parameter. However, the function itself is not taking any parameter, how does it work?
const selectFilteredSortedTransformedData = createSelector(
did you add mapState function in redux connect function ?? something like this.
export default connect(mapState)(Component)
I want to upload few images and I have code as below. It returns the download link, but for only one image. How can I get a list of links to uploaded images?
constructor(private storage: AngularFireStorage, public afs: AngularFirestore, ) {
this.files = this.afs.collection('files').valueChanges();
}
uploadFile(event) {
// reset the array
this.uploads = [];
const filelist = event.target.files;
const allPercentage: Observable<number>[] = [];
for (const file of filelist) {
const filePath = `${file.name}`;
const fileRef = this.storage.ref(filePath);
const task = this.storage.upload(filePath, file);
const _percentage$ = task.percentageChanges();
allPercentage.push(_percentage$);
// observe percentage changes
this.uploadPercent = task.percentageChanges();
// get notified when the download URL is available
task.snapshotChanges().pipe(
finalize(() => {
this.downloadURL = fileRef.getDownloadURL();
})
).subscribe();
// this.downloadURLs.push(this.downloadURL);
}
}
uploadFile(files) {
//console.log(this.uploadService.uploadFile(file));
this.uploadService.uploadFile(files);
}
<ion-item>
<ion-input type="file" (change)="uploadFile($event)" multiple="multiple"></ion-input>
</ion-item>
<button (click)="onAddItem()" ion-button block>Добавить</button>
Easy way: Clear this.downloadURLs before uploaded, then add url in finalize step
uploadFile(event) {
// reset the array
this.uploads = [];
this.downloadURLs = [];
const filelist = event.target.files;
const allPercentage: Observable<number>[] = [];
for (const file of filelist) {
const filePath = `${file.name}`;
const fileRef = this.storage.ref(filePath);
const task = this.storage.upload(filePath, file);
const _percentage$ = task.percentageChanges();
allPercentage.push(_percentage$);
// observe percentage changes
this.uploadPercent = task.percentageChanges();
// get notified when the download URL is available
task.snapshotChanges().pipe(
finalize(() => {
fileRef.getDownloadURL().subscribe((url) => {
this.downloadURLs = this.downloadURLs.concat([url]);
});
})
).subscribe();
// this.downloadURLs.push(this.downloadURL);
}
}
Rxjs way: First combine all latest result, then subscribe to assign results. Note: You can use forkJoin too
import { combineLatest, from } from 'rxjs';
import { map, filter } from 'rxjs/operators';
...
uploadFile(event) {
// reset the array
this.uploads = [];
const filelist = event.target.files;
const allPercentage: Observable<number>[] = [];
const downloadUrls$ = filelist.map((file) => {
const filePath = `${file.name}`;
const fileRef = this.storage.ref(filePath);
const task = this.storage.upload(filePath, file);
const _percentage$ = task.percentageChanges();
allPercentage.push(_percentage$);
// observe percentage changes
this.uploadPercent = task.percentageChanges();
// get notified when the download URL is available
return task.snapshotChanges().pipe(
filter((task) => task.state === this.storage.TaskState.SUCCESS)
switchMap(() => from(fileRef.getDownloadURL()))
)
});
combineLatest(...downloadUrls$)
.subscribe((urls) => this.downloadURLs = urls)
}