If/else condition with Realtime firebase value - javascript

I wanted to ask, is it possible to use if/else on firebae's realtime results, which is:
enter image description here
if (Recent === waktu) {
Hasil : "1";
} else {
Hasil : "0";
}
This is my failed code:
// Eksekusi Jadwal
var waktu_jadwal = firebase.database().ref();
return waktu_jadwal.on('value').then((snapshot)=>{
return snapshot.forEach(jadwal_waktu=>{
if (jadwal_waktu.child("waktu").val() === (jadwal_waktu.child("Recent").val()) {
waktu_jadwal.update ({
Keadaan: {
Hasil: 1
}
});
} else {
waktu_jadwal.update ({
Keadaan: {
Hasil: 0
}
});
}
return console.log("added");
})
})

You can use an if/else as you have, as this is a callback function and will run like any other part of your script. Just be aware that you are using .on() which is an active firebase listener, if you intend on getting the value once, there is .once().
I would be cautious of shadowing variable names
try a more relative name such as 'Key'
return snapshot.forEach(Key=>{
if (Key.child("waktu").val() === (Key.child("Recent").val()) {
waktu_jadwal.update ({
You can also convert the object from snapshot data to a son object with
const data = snapshot.toJSON();

Related

why my React setstate is not updating immediately?

This a my function
onSelectAction = (x, o) => {
var { takeActionsOptions } = this.props.main
console.log(o, "onSelectAction")
var tempAction=_.cloneDeep(takeActionsOptions)
_.keys(tempAction).map(a => {
if(a === x.processCode) {
tempAction[a].map((b)=>{
b.isSelected = b.id === o.id
})
}
else{
tempAction[a].map((b)=>{
b.isSelected = b.id === 0
})
}
})
StoreActions.setState({ takeActionsOptions:tempAction});
this.onClickTakeAction(o, x)
}
where tempAction is changing the property like the i wanted to. But when i m trying update the store... this { takeActionsOptions:tempAction} is not getting updated for the first time. After 2-3 clicks on the desired location this is getting updated. i want to update immediately in the store because there is another function which fetches data from the store and does another operation.
this is my other function which is using the take "takeActionsOptions " from store. so if that function is not updating then this function isnt working properly
onClickTakeAction = (o, x) => {
var { takeActionsOptions=[] } = this.props.main
var selectedAction = takeActionsOptions[x.processCode].find(a => a.isSelected)
if (selectedAction.id === 0) {
hydro.msg.info("Please select an option.")
return;
}
var tempAction=_.cloneDeep(takeActionsOptions)
_.keys(tempAction).map(a => {
tempAction[a].map((b)=>{
b.isSelected = b.id === 0
})
})
this.setState({takeActionsOptions:tempAction})
switch (selectedAction.id) {
case 1:
var userName = somecode.userName;
if (userName.toUpperCase() === x.userName.toUpperCase()) {
Actions.deleteSelectedProcess(x);
}
else {
somecode.info("Not your Process")
}
break;
case 2:
Action.downloadLogs(x);
break;
}
}
var tempAction=_.cloneDeep(takeActionsOptions)
What the cloneDeep function is doing here? If it does any API calling/Getting data from the server, you need to wait for a moment to get the data. Meanwhile, you can disable the button and show some loaders for interactivity.
If you're using the loadash to deep copy the object, up to my knowledge loadash functions, takes a long time to complete based on the CPU or object you are trying to copy. So try to wait for a minute and check whether it's updating or not. If it is updating, then you should disable the button until then.

can't use state value right after setState()

Currently i'm doing a quiz composed by multiple categories that can be chosen by the user and i wanna check if the user responded to all questions. For doing that, i compared the number of questions he answered with the number of questions gived by the api response. The problem is that i have an "submit answers" button at the end of the last question, with that onClick function:
const sendAnswers = (e, currentQuiz) => {
setQuizzes({...quizzes, [currentQuiz]:answers});
setAnswers([])
var answeredToAllQuestions = true
DataState.map(function (quiz) {
if(quiz.category in quizzes){
if(Object.keys(quiz.questions).length !== Object.keys(quizzes[quiz.category]).length){
answeredToAllQuestions=false;
}
}
});
if(answeredToAllQuestions === false){
setAlertTrigger(1);
}
else{
setNumber(number+1);
}
}
in that function i use setState on this line: setQuizzes({...quizzes, [currentQuiz]:answers}); to upload the answers he checked on the last question before checking if he answered to all questions. The problem is that state of quizzes is not updated imediatly and it s not seen by the if condition.
I really don't know how am i supposed to update the state right after setting it because, as i know, react useState updates the state at the next re-render and that causes trouble to me..
Considering that quizzes will be equal to {...quizzes, [currentQuiz]:answers} (after setQuizzes will set it), there is no reason to use quizzes in if condition. Replace it with a local var and problem will be solved.
const sendAnswers = (e, currentQuiz) => {
let futureValueOfQuizzes = {...quizzes, [currentQuiz]:answers}
setQuizzes(futureValueOfQuizzes);
setAnswers([])
var answeredToAllQuestions = true
DataState.map(function (quiz) {
if(quiz.category in futureValueOfQuizzes){
if(Object.keys(quiz.questions).length !== Object.keys(quizzes[quiz.category]).length){
answeredToAllQuestions=false;
}
}
});
if(answeredToAllQuestions === false){
setAlertTrigger(1);
}
else{
setNumber(number+1);
}
}
I would like to take this opportunity to say that these type of problems appear when you use React state for your BI logic. Don't do that! Much better use a local var defined in components body:
const Component = () => {
const [myVar , setMyVar] = useState();
let myVar = 0;
...
}
If myVar is used only for BI logic, use the second initialization, never the first!
Of course sometimes you need a var that is in BI logic and in render (so the state is the only way). In that case set the state properly but for script logic use a local var.
You have to either combine the useState hook with the useEffect or update your sendAnswers method to perform your control flow through an intermediary variable:
Using a temporary variable where next state is stored:
const sendAnswers = (e, currentQuiz) => {
const newQuizzes = {...quizzes, [currentQuiz]:answers};
let answeredToAllQuestions = true
DataState.map(function (quiz) {
if(quiz.category in newQuizzes){
if (Object.keys(quiz.questions).length !== Object.keys(newQuizzes[quiz.category]).length){
answeredToAllQuestions = false;
}
}
});
setQuizzes(newQuizzes);
setAnswers([]);
if (answeredToAllQuestions === false) {
setAlertTrigger(1);
} else {
setNumber(number+1);
}
}
Using the useEffect hook:
const sendAnswers = (e, currentQuiz) => {
setQuizzes({...quizzes, [currentQuiz]:answers});
setAnswers([]);
}
useEffect(() => {
let answeredToAllQuestions = true
DataState.map(function (quiz) {
if(quiz.category in quizzes){
if (Object.keys(quiz.questions).length !== Object.keys(quizzes[quiz.category]).length){
answeredToAllQuestions = false;
}
}
});
if (answeredToAllQuestions === false) {
setAlertTrigger(1);
} else {
setNumber(number+1);
}
}, [quizzes]);

If-else condition with Realtime firebase

Is it possible to use if/else on firebase's realtime results? My database looks like this:
Previously I've tried it and it works if the result of both "waktu" is same, == recent.
but what I want is if one of the values "waktu" == recent, then "Hasil" = 0, if both of "time" nothing is equal to "Recent", then the value "Hasil" = 0.
if ("Recent" == (one of) "waktu") {
Hasil : "1";
} else {
Hasil : "0";
}
This is my code before, which value "Result" = 1, if both "waktu" values == recent. but i want if one values of "waktu" == Recent.
var recent = "";
var root = firebase.database().ref();
root.child("Time").on('value', (snapshot) => {
recent = snapshot.val().Recent;
});
root.child("jadwal-pakan").on('value', (snapshot) => {
snapshot.forEach(jadwal_waktu => {
if (jadwal_waktu.child("waktu").val() == recent) {
root.update({
Keadaan: {
Hasil: 1
}
});
} else {
root.update({
Keadaan: {
Hasil: 0
}
});
}
})
})
Thank you :)
The problem is in the timing of when the code executes. Since all data is loaded from Firebase asynchronously, your recent = snapshot.val().Recent may run after if (jadwal_waktu.child("waktu").val() == recent) { has run.
To ensure this is guaranteed, you'll need to nest the listeners like this:
root.child("Time").on('value', (snapshot) => {
recent = snapshot.val().Recent;
root.child("jadwal-pakan").on('value', (snapshot) => {
snapshot.forEach(jadwal_waktu => {
if (jadwal_waktu.child("waktu").val() == recent) {
root.update({
Keadaan: {
Hasil: 1
}
});
} else {
root.update({
Keadaan: {
Hasil: 0
}
});
}
})
})
});
Now there's no chance of the statements executing out of order, and the if (jadwal_waktu.child("waktu").val() == recent) { will work as expected.

Await Async when looping through large json file

The json file is large around 20mb.
I want to wait until a result is returned or the entire file is looped through, before sending back the age. Currently it returns 0 even if the age is not 0
const app = express()
const genesis = require('./people.json');
app.get('/', function (req, res) {
res.setHeader('Content-Type', 'application/json');
let age = getAge(req.query.name)
res.json({
“name”: req.query.name,
“age”: age, // this is always 0
});
});
function getAge(name) {
genesis.balances.forEach(element => {
if (element.name == name) {
// console here shows correct age
return element.person[0].age;
}
});
return 0;
}
app.listen(3000)
As I said in the comment, the problem was in your getAge method, it was always returning 0.
The return inside the forEach doesn't return the value off of the loop.
Please have a look at the following approach
function getAge(name) {
const person = genesis.balances.find((elm)=> elm.name === name);
return person ? person.age : 0;
}
See code comment below
function getAge(name) {
genesis.balances.forEach(element => { // forEach doesn't return anything
if (element.name == name) {
// console here shows correct age
return element.person[0].age;
}
});
return 0;
}
You probably want instead something like:
function getAge(name) {
const res = genesis.balances.filter(element => element.name == name);
if (res.length === 0) return 0; // not found
return res[0].person[0].age;
}
read more about forEach
Comment: having a person-array under element with "name" is a weird choice, why should a single person-name be mapped to multiple persons?

How to make sync call in forEach loop Angular 6

I am trying to check my all 4 images is uploaded to server without any error, then redirect to another page so i am trying to perform some sync checking in my code (I have total 4 images in my imgResultAfterCompress array). below is my code:
if(Boolean(this.updateImage(data.AddId))===true)
{
this.router.navigate(['/job-in-hotels-india-abroad']);
}
updateImage(AddId:number):Observable<boolean>
{
this.cnt=0;
this.uploadingMsg='Uploading Images...';
this.imgResultAfterCompress.forEach( (value, key) => {
if(value!=='')
{
this.itemService.updateImage(this.employer.ID,AddId,key,value).subscribe(data=>{
if(data && data.status == 'success') {
this.uploadingMsg=this.uploadingMsg+'<br>Image No - '+(key+1)+' Uploaded.';
this.cnt++;
}
else
this.alertService.error(data.message);
});
}
if(this.cnt==4)
this.uploadingDone= true;
else
this.uploadingDone= false
});
return this.uploadingDone;
}
Every time i am getting cnt value is 0, i want its value = 4 (completely uploaded all images) then redirection will occurred.
The easier way is to wrap your observables into a single one, using zip operator
https://rxjs-dev.firebaseapp.com/api/index/function/zip
Thus once every request is finished successfully your zipped Observable will be fulfilled.
UPDATE:
This is how I think it should look like. I could miss something specific, but the global idea should be clear
redirect() {
this.updateImages(data.AddId).subscribe(
() => this.router.navigate(['/job-in-hotels-india-abroad']),
error => this.alertService.error(error.message)
)
}
updateImages(AddId: number): Observable<boolean[]> {
this.uploadingMsg = 'Uploading Images...';
const requests: Observable<boolean>[] = [];
this.imgResultAfterCompress.forEach((value, key) => {
if (!value) {
return;
}
requests.push(
this.itemService.updateImage(this.employer.ID, AddId, key, value)
.pipe(
tap(() => this.uploadingMsg = this.uploadingMsg + '<br>Image No - ' + (key + 1) + ' Uploaded.'),
switchMap((data) => {
if (data && data.status == 'success') {
return of(true)
} else {
throwError(new Error('Failed to upload image'));
}
})
)
)
});
return zip(...requests);
}
Finally got the desire result by using forkJoin
Service.ts:
public requestDataFromMultipleSources(EmpId: number,AddId:number,myFiles:any): Observable<any[]> {
let response: any[] = [];
myFile.forEach(( value, key ) => {
response.push(this.http.post<any>(this.baseUrl + 'furniture.php', {EmpId: EmpId, AddId:AddId,ImgIndex:key,option: 'updateAdImg', myFile:value}));
});
// Observable.forkJoin (RxJS 5) changes to just forkJoin() in RxJS 6
return forkJoin(response);
}
my.component.ts
let resCnt=0;
this.itemService.requestDataFromMultipleSources(this.employer.ID,AddId,this.imgResultAfterCompress).subscribe(responseList => {
responseList.forEach( value => {
if(value.status=='success')
{
resCnt++;
this.uploadingMsg=this.uploadingMsg+'<br>Image No - '+(value.ImgIndex+1)+' Uploaded.';
}
else
this.uploadingMsg=this.uploadingMsg+'<br>Problem In Uploading Image No - '+(value.ImgIndex+1)+', Please choose another one.';
});
if(resCnt === this.imgResultAfterCompress.length)
{
this.alertService.success('Add Posted Successfully');
this.router.navigate(['/job-in-hotels-india-abroad']);
}
else
this.alertService.error('Problem In Uploading Your Images');
});
You shouldn't try to make sync call within a loop. It is possible using async/await, but it's bad for app performance, and it is a common anti-pattern.
Look into Promise.all(). You could wrap each call into promise and redirect when all promises are resolved.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Categories