So when I'm on a Book Page, and click a button to delete it, it should redirect to my User Profile and delete the Book from the database. But what happens is ComponentWillMount is called again for the Book and it can't find it. Maybe I don't have a clear understanding of these lifecycle methods but I thought ComponentWillMount will only be called once when first rendering? Also using Firebase for the database.
Here's the relevant code (where Book == WIP):
WIP.js
componentWillMount() {
this.WIPRef.on('value', snapshot => {
let WIP = snapshot.val()
this.setState({
title: WIP.title ? WIP.title : "",
wordCount: WIP.wc ? WIP.wc : "",
logline: WIP.logline ? WIP.logline : "",
draft: WIP.draft ? WIP.draft : "",
language: WIP.language ? WIP.language : "",
disclaimers: WIP.disclaimers ? WIP.disclaimers : "",
improvementAreas: WIP.improvementAreas ? WIP.improvementAreas : "",
blurb: WIP.blurb ? WIP.blurb : "",
additionalNotes: WIP.additionalNotes ? WIP.additionalNotes : "",
writer: WIP.writer ? WIP.writer : "",
genres: WIP.genres ? WIP.genres : [],
types: WIP.types ? WIP.types : []
});
var promises = []
var writerRef = firebaseDB.database().ref(`/Users/${WIP.writer}`)
promises.push(writerRef.once('value'));
Promise.all(promises).then((snapshots) => {
snapshots.forEach((snapshot) => {
var writer = snapshot.val()
this.setState({
writerName: writer.displayName ? writer.displayName : ""
})
})
})
})
}
removeWIP(WIPId) {
this.setState({ redirect: true })
const usersWIPRef = firebaseDB.database().ref(`/Users/${this.state.writer}/WIPs/${this.state.wipId}`)
this.deleteWIPIndexRecord(this.state.wipId)
usersWIPRef.remove();
this.WIPRef.remove();
}
deleteWIPIndexRecord(wipId) {
const WIPRef = firebaseDB.database().ref(`WIPs/${wipId}`);
WIPRef.on('value', snapshot => {
// Get Algolia's objectID from the Firebase object key
const objectID = snapshot.key;
// Remove the object from Algolia
wipsIndex
.deleteObject(objectID)
.then(() => {
console.log('Firebase object deleted from Algolia', objectID);
})
.catch(error => {
console.error('Error when deleting contact from Algolia', error);
process.exit(1);
});
})
}
render() {
if (this.state.redirect === true) {
return <Redirect to= {{pathname: '/user/' + this.state.writer}} />
}
return(
<Button className="black-bordered-button"
onClick={() => this.removeWIP(WIP.id)}
>
Remove Item
</Button>
)
}
But then when I delete the book I get this error:
Uncaught TypeError: Cannot read property 'title' of null from this line in ComponentWillMount title: WIP.title ? WIP.title : ""
Related
Currently im trying to switch over from useQuery to useInfiniteQuery but for some reason now it returns no data. The reason why I need to make the switch is to do infinite pagination. The 'details' paramater is an interface I made. This is what gets my data.
const getVendors = async (details: VendorSearchInterface) => {
// console.log()
try {
const axiosConfig: AxiosRequestConfig = {
method: 'GET',
url: `${config.apiHost}/vendors/getAllActiveVendors`,
headers: {
'Content-Type': 'application/json'
},
params: {
sameDayRelease: details.isSameDayRelease || null,
region: details.region === 'all' ? null : details.region,
name: details.name || null,
address: details.address || null,
// topVendors: details.region === 'TOP' ? 1 : null
page: details.page === false ? null : details.page,
count: details.count === false ? null : details.count
}
}
const resp: AxiosResponse = await axios(axiosConfig)
const data = resp.data.data?.map((vendor: any) => ({
vendorId: vendor.providerId,
displayName: vendor.name,
// Formatting phone numbers per Joey's request (US/CA only)
phone:
vendor.phone && vendor.region !== 'EU'
? vendor.phone.replace(/[^0-9]/g, '').replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3')
: vendor.phone,
line1: formatString(vendor.line1),
line2: formatString(vendor.line2),
city: formatString(vendor.city),
cityState: `${vendor.city} ${vendor.state} ${vendor.postalCode}
${vendor.station ? vendor.station : ''} ${vendor.stateFullName}`,
state: vendor.state.toUpperCase(),
postalCode: vendor.postalCode,
station: vendor.station,
region: vendor.region,
isSameDayRelease: !!vendor.isSameDayRelease,
addressId: vendor.addressId,
brand: vendor.brandCode,
isTopVendor: vendor.isTopVendor,
clientAcceptCreditCard: vendor.clientAcceptCreditCard
}))
if (data?.length === 0) {
handleError({ error: 'Unable to retreive list of vendors', title: '' })
return data
}
const vendors: VendorDetailsInterface[] = data
return vendors
} catch (error) {
if (axios.isAxiosError(error)) {
const { message } = error
handleError({ error: message, title: 'Error vendor details' })
return []
}
handleError({ error, title: 'Error vendor details' })
return []
}
}
function formatString(string: string) {
return string
? string
.split(' ')
.map((word: string) => {
const lowerCaseWord = word.toLowerCase()
return lowerCaseWord.charAt(0).toUpperCase() + lowerCaseWord.slice(1)
})
.join(' ')
: null
}
export default getVendors
This is my new useInfiniteQuery function im trying to receive the data from.
export const useGetVendorsQuery = (details: VendorSearchInterface) =>
useInfiniteQuery(
['vendorDetails',
{
isSameDayRelease: details.isSameDayRelease,
region: details.region,
name: details.name,
address: details.address,
count: details.count,
page: details.page
// topVendors: details.topVendors,
}],
() => getVendors(details),
{
getNextPageParam: (lastPageData) => lastPageData?.data.next_page_url
}
)
I would be glad if I can get some help with my first question. I am an Android developer and I am currently doing a project which requires Firebase cloud functions, which I am new to.
I am calling an external API with Axios, and saving the JSON response into multiple Firestore documents. 4 out of 5 times that I call the function, it executes without error, but it does not write to Firestore, and the 1 time it writes to Firestore, it takes as long as 8 to 12 seconds to write the data.
I read something about promises and I applied to the function(to the best of my knowledge) but the problem persists. Please let an expert tell me what I am doing wrong. Below is my cloud function:
const functions = require("firebase-functions");
const axios = require("axios");
const admin = require("firebase-admin");
const api_token = "---------";
const includes = "------------";
const url = "------------" + api_token + includes;
exports.liveScores = functions.https.onRequest((req, res) => {
return axios.get(url)
.then(response => {
var data = response.data.data;
data.forEach(function(obj){
admin.firestore().collection("fixtures").doc(obj.id.toString()).set({
id : obj.id,
league_id : obj.league_id,
venue : obj.venue_id,
localteam_id : obj.localteam_id,
visitorteam_id : obj.visitorteam_id,
round_id : obj.round_id,
round_name : obj.round ? obj.round.data.name : null,
stage_id : obj.stage_id,
localteam_score : obj.scores.localteam_score,
visitorteam_score : obj.scores.visitorteam_score,
status : obj.time.status,
timestamp : new admin.firestore.Timestamp(obj.time.starting_at.timestamp, 0),
real_date : obj.time.starting_at.date,
minute : obj.time.minute,
injury_time : obj.time.injury_time,
localteam_formation : obj.formations.localteam_formation,
visitorteam_formation : obj.formations.visitorteam_formation,
visitorTeam : obj.visitorTeam.data.name,
localTeam : obj.localTeam.data.name,
local_team_logo : obj.localTeam.data.logo_path,
visitor_team_logo : obj.visitorTeam.data.logo_path,
season_id : obj.season_id,
referee : obj.referee ? obj.referee.data.fullname : null,
aggregate : obj.aggregate ? obj.aggregate.data.result : null,
venue_name : obj.venue && obj.venue.data.name ? obj.venue.data.name : null,
weather_type : obj.weather_report && obj.weather_report.type ? obj.weather_report.type : null,
weather_icon : obj.weather_report && obj.weather_report.icon ? obj.weather_report.icon : null,
temperature : obj.weather_report && obj.weather_report.temperature_celcius ? obj.weather_report.temperature_celcius.temp : null
}).then(() => {
console.log("Fixtures table complete...");
})
var no_of_events = obj.events.data.length;
for(var i=0; i < no_of_events; i++ ){
admin.firestore().collection("events").doc(obj.events.data[i].fixture_id.toString()).collection("all_events").doc(obj.events.data[i].id.toString()).set({
type : obj.events.data[i].type,
team_id : obj.events.data[i].team_id,
fixture_id : obj.events.data[i].fixture_id,
var_result : obj.events.data[i].var_result,
player_id : obj.events.data[i].player_id,
player_name : obj.events.data[i].player_name,
related_player_id : obj.events.data[i].related_player_id,
related_player_name : obj.events.data[i].related_player_name,
minute : obj.events.data[i].minute,
reason : obj.events.data[i].reason,
id : obj.events.data[i].id
}).then(() => {
console.log("Events table complete...");
})
}
for(var y=0; y < obj.lineup.data.length; y++){
admin.firestore().collection("lineup").doc(obj.lineup.data[y].fixture_id.toString()).collection("lineup").doc(obj.lineup.data[y].player_id.toString()).set({
fixture_id : obj.lineup.data[y].fixture_id,
team_id : obj.lineup.data[y].team_id,
player_id : obj.lineup.data[y].player_id,
player_name : obj.lineup.data[y].player_name,
number : obj.lineup.data[y].number,
position : obj.lineup.data[y].position,
formation_position : obj.lineup.data[y].formation_position,
captain : obj.lineup.data[y].captain
}).then(() => {
console.log("Lineup table complete...");
})
}
for(var x=0; x < obj.bench.data.length; x++){
admin.firestore().collection("bench").doc(obj.bench.data[x].fixture_id.toString()).collection("bench").doc(obj.bench.data[x].player_id.toString()).set({
fixture_id : obj.bench.data[x].fixture_id,
team_id : obj.bench.data[x].team_id,
player_id : obj.bench.data[x].player_id,
player_name : obj.bench.data[x].player_name,
number : obj.bench.data[x].number,
position : obj.bench.data[x].position,
formation_position : obj.bench.data[x].formation_position,
captain : obj.bench.data[x].captain
}).then(() => {
console.log("Bench table complete...");
})
}
for(var c=0; c < obj.stats.data.length; c++){
admin.firestore().collection("stats").doc(obj.stats.data[c].fixture_id.toString()).collection("all_stats").doc(obj.stats.data[c].team_id.toString()).set({
fixture_id : obj.stats.data[c].fixture_id,
team_id : obj.stats.data[c].team_id,
total_shots : obj.stats.data[c].shots && obj.stats.data[c].shots.total ? obj.stats.data[c].shots.total : null,
ongoal : obj.stats.data[c].shots && obj.stats.data[c].shots.ongoal ? obj.stats.data[c].shots.ongoal : null,
total_passes : obj.stats.data[c].passes && obj.stats.data[c].passes.total ? obj.stats.data[c].passes.total : null,
attacks : obj.stats.data[c].attacks && obj.stats.data[c].attacks.attacks ? obj.stats.data[c].attacks.attacks : null,
fouls : obj.stats.data[c].fouls,
corners : obj.stats.data[c].corners,
offsides : obj.stats.data[c].offsides,
possessiontime : obj.stats.data[c].possessiontime,
yellowcards : obj.stats.data[c].yellowcards,
redcards : obj.stats.data[c].redcards,
injuries : obj.stats.data[c].injuries,
saves : obj.stats.data[c].saves,
tackles : obj.stats.data[c].tackles
}).then(() => {
console.log("Stats table complete...");
})
}
})
return res.status(200).json(
console.log("successful")
)
})
.catch(err => {
console.log(err);
return res.status(500).json({
error: err
})
})
});
I would appreciate any kind of help.
Thank you.
You should probably not launch all those firestore queries simultaneously, but wait for each one to complete before launching the next one.
This is easier when you make the onRequest callback function async:
exports.liveScores = functions.https.onRequest(async (req, res) => {
try {
let response = await axios.get(url);
var data = response.data.data;
for (let obj of data) {
await admin.firestore().collection("fixtures").doc(obj.id.toString()).set({
id: obj.id,
/* ... */
});
console.log("Fixtures table complete...");
for (let eventData of obj.events.data){
await admin.firestore().collection("events").doc(eventData.fixture_id.toString()).collection("all_events").doc(eventData.id.toString()).set({
type: eventData.type,
/* ... */
});
}
console.log("Events table complete...");
for (let lineupData of obj.lineup.data) {
await admin.firestore().collection("lineup").doc(lineupData.fixture_id.toString()).collection("lineup").doc(lineupData.player_id.toString()).set({
fixture_id: lineupData.fixture_id,
/* ... */
});
}
console.log("Lineup table complete...");
/* ... etc ... */
}
console.log("successful");
return res.status(200).json({ message: "successful" });
} catch(error) {
console.log(error);
return res.status(500).json({ error });
}
});
NB: don't pass console.log("successful") as an argument to .json. You'll want to pass an object as argument.
i'm working on a bot application using react js and botframework webchat. The thing is that i want to clear the text input box (from where msgs are sent) after sending the message - which is selected from the suggestion. The Suggestion list(or autocomplete component) is a custom coded one. And What i mean is that if i type "hr" the suggestion list popup will come and if i click on one option from the suggestion, say 'hr portal', it will be sent, but what i wrote ie "hr" remains there in the input field and i want to clear that. And please note that If i type something and send its working fine. The problem is only when i type something and select something from the suggestion. Everything else is fine. How can i clear that. Any help would be really appreciated.
please find the below image for more understanding.
here's my code;
import React from 'react';
import { DirectLine, ConnectionStatus } from 'botframework-directlinejs';
import ReactWebChat from 'botframework-webchat';
import './ChatComponent.css';
var val;
var apiParameters = [];
var currentFocus = -1;
export default class extends React.Component {
constructor(props) {
super(props);
this.state = {
token: '',
conversationId: '',
directLine: {},
view: false,
feedBack: null,
value: '',
popupContent: '',
storeValue: '',
suggestions: [],
suggestionCallback: '',
suggestionTypedText: "",
typingChecking: "false",
};
this.handleTokenGeneration = this.handleTokenGeneration.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleSaveFeedback = this.handleSaveFeedback.bind(this);
this.handleSuggestion = this.handleSuggestion.bind(this);
this.handleClose = this.handleClose.bind(this);
this.handleSuggestionClick = this.handleSuggestionClick.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.moveHighlight = this.moveHighlight.bind(this);
this.getSuggestionHtml = this.getSuggestionHtml.bind(this);
}
getSuggestionHtml(suggestion) {
const lowerCaseSuggestion = suggestion.toLowerCase();
return {
__html: lowerCaseSuggestion.includes(this.state.suggestionTypedText) ? lowerCaseSuggestion
.replace(this.state.suggestionTypedText, `<b>${this.state.suggestionTypedText}</b>`) : lowerCaseSuggestion
};
}
handleTokenGeneration = async () => {
console.log("11111");
const response = await fetch(`api/TokenGenerationService/GetToken`);
const data = await response.json();
this.setState({
token: data.categoryObject.token, conversationId:
data.categoryObject.conversationId
});
this.state.directLine = new DirectLine({ token: this.state.token });
this.setState({ view: true });
this.setState({ typingChecking: "true" });
console.log("conversationId");
};
async handleSuggestion(val, store) {
if (val === "") {
this.setState({
suggestions: []
});
}
else {
apiParameters = [];
var valuess = null;
const response = await fetch(`api/TokenGenerationService/GetAzureSearch?myparam1=${val}`);
const data = await response.json();
var values = ["Hello", "yes", "no", "exit", "Welcome", "Thank You", "Approve", "Apply leave", "Reject", "Absence Balance", "Leave Balance", "Upcoming Holidays", "Apply Comp-Off", "Approve Leave", "Raise Incident Tickets", "Project Allocation Info", "Reporting Manager Change", "Reporting Manager Approval", "Approve Isolve Tickets", "My Manager", "My Account Manager", "Generate Salary Certificate", "Isolve Ticket Status", "Internal Job Posting", "My Designation", "My Joining Date", "RM Approval", "RM Change", "Resource Allocation", "ESettlement Approval", "SO Approval", "Cash advance Approval", "Purchase Request Approval", "Referral status", "HR Ticket", "Platinum Support"];
valuess = values.filter(s =>
s.toLocaleLowerCase().startsWith(val.toLowerCase())
);
valuess = valuess.concat(data.az_search);
this.setState({
suggestions: valuess,
suggestionCallback: store,
suggestionTypedText: val.toLowerCase()
});
// alert(data.az_search);
var totCount = data.az_search;
console.log("kkkkkk" + totCount);
}
}
moveHighlight(event, direction) {
event.preventDefault();
const { highlightedIndex, suggestions } = this.state;
if (!suggestions.length) return;
let newIndex = (highlightedIndex + direction + suggestions.length) % suggestions.length;
if (newIndex !== highlightedIndex) {
this.setState({
highlightedIndex: newIndex,
});
}
}
keyDownHandlers = {
ArrowDown(event) {
this.moveHighlight(event, 1);
},
ArrowUp(event) {
this.moveHighlight(event, -1);
},
Enter(event) {
const { suggestions } = this.state;
if (!suggestions.length) {
// menu is closed so there is no selection to accept -> do nothing
return
}
event.preventDefault()
this.applySuggestion(suggestions[this.state.highlightedIndex]);
},
}
handleKeyDown(event) {
// console.log("lokkkkkkkkkkkk")
if (this.keyDownHandlers[event.key])
this.keyDownHandlers[event.key].call(this, event)
}
async handleSuggestionClick(event) {
await this.applySuggestion(event.currentTarget.textContent);
}
async applySuggestion(newValue) {
//newValue = null;
await this.setState({ typingChecking: "false", suggestions: [], highlightedIndex: 0 });
this.state.suggestionCallback.dispatch({
type: 'WEB_CHAT/SEND_MESSAGE',
payload: {
text: newValue
}
});
await this.setState({ typingChecking: "true" });
}
getSuggestionCss(index) {
var HIGHLIGHTED_CSS = "HIGHLIGHTED_CSS";
var SUGGESTION_CSS = "SUGGESTION_CSS";
return index === this.state.highlightedIndex ? HIGHLIGHTED_CSS : SUGGESTION_CSS;
}
handleClose(elmnt) {
var x = document.getElementsByClassName("autocomplete-items");
for (var i = 0; i < x.length; i++) {
if (elmnt !== x[i]) {
x[i].parentNode.removeChild(x[i]);
}
}
}
async componentDidMount() {
try {
await this.handleTokenGeneration();
const store =
window.WebChat.createStore(
{},
({ getState }) => next => action => {
this.state.directLine.connectionStatus$
.subscribe(connectionStatus => {
if (connectionStatus === ConnectionStatus.ExpiredToken) {
console.log("expired");
}
if (action.type === 'WEB_CHAT/SET_SEND_BOX') {
const val = action.payload.text;
if (this.state.typingChecking === "true") {
this.setState({
highlightedIndex: -1,
});
console.log(this.state.typingChecking);
this.handleSuggestion(val, store);
}
}
if (action.type === 'DIRECT_LINE/DISCONNECT_FULFILLED') {
console.log("final" + connectionStatus);
console.log("finalexpired" + ConnectionStatus.ExpiredToken);
console.log("final");
this.handleTokenGeneration();
}
});
return next(action)
}
);
this.setState({ storeValue: store });
} catch (error) {
console.log("error in fetching token");
console.log(error);
}
this.state.directLine.activity$
.filter(activity => activity.type === 'message')
.subscribe(function (activity) {
//console.log("oooooooooooooooooooooo");
}
// message => console.log("received message ", message.text)
);
}
handleSaveFeedback(ans) {
// console.log(this.state.conversationId);
// console.log(this.state.feedBack);
var userID = "C94570";
var feedbackmsg = this.state.value;
var feedbacktype = this.state.feedBack;
var convId = this.state.conversationId;
fetch('api/Feedback/SaveFeedback',
{
method: "POST",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ Uid: userID, FeedbackMessage: feedbackmsg, Convid: convId, FeedbackType: feedbacktype })
}).
then(response => response.text())
.then(data => {
console.log(data.getResult);
});
this.setState({ value: '' });
}
feedback(ans) {
this.setState({ feedBack: ans });
if (ans === "Send") {
this.handleSaveFeedback(ans);
}
else if (ans === "Yes") {
this.setState({ popupContent: "How was your experience?" });
// console.log(this.state.value)
}
else if (ans === "No") {
this.setState({ popupContent: "What went wrong?" });
// console.log(this.state.value)
}
}
handleChange = (event) => {
this.setState({ value: event.target.value });
}
styleOptions = {
bubbleBackground: 'rgba(0, 0, 255, .1)',
bubbleFromUserBackground: 'rgba(0, 255, 0, .1)',
botAvatarInitials: 'DIA',
userAvatarInitials: 'ME'
}
render() {
if (!this.state.view) {
return
<div />
} else {
const filteredSuggestions = this.state.suggestions.filter(
suggestion =>
suggestion.toLowerCase().indexOf(this.state.suggestionTypedText.toLowerCase())
> -1
);
// console.log(this.state.view);
return (
<div className="react-container webchat" >
<div onKeyDown={this.handleKeyDown.bind(this)}>
<div >
<ReactWebChat styleOptions={this.styleOptions} directLine={this.state.directLine} webSocket={true} userID='C94570' username='Thomas' store={this.state.storeValue} sendTypingIndicator={true} />
</div>
</div>
<div className="SuggestionParent" id="Suggestion1">
{this.state.suggestions.map((suggestion, index) => (
<div className={this.getSuggestionCss(index)} key={index} onClick={this.handleSuggestionClick} >
{suggestion
.toLowerCase()
.startsWith(this.state.suggestionTypedText) ? (
<div>
<b>{this.state.suggestionTypedText}</b>
{suggestion
.toLowerCase()
.replace(this.state.suggestionTypedText, "")}
</div>
) : (
<div dangerouslySetInnerHTML={this.getSuggestionHtml(suggestion)} />
)}
</div>
))}
</div>
<footer className="chat-footer" >
<div className="foot-footer">
Was I helpful ?
<span className="feedback" onClick={() => this.feedback("Yes")} >Yes</span><span>|</span><span className="feedback" onClick={() => this.feedback("No")}>No</span>
{
this.state.feedBack === "Yes" || this.state.feedBack === "No" ?
(
<div className="dialog" id="myform">
<div id="textfeedback">
<span id="closeFeedback" onClick={() => this.feedback("Close")}>X</span>
<p>{this.state.popupContent}</p>
<input type="text" id="feedbacktxtbox" required name="textfeedback" placeholder="Pleasure to hear from u!"
onChange={this.handleChange}
value={this.state.value} />
<button type="button" id="btnfeedback" onClick={() => this.feedback("Send")}>send</button>
</div>
</div>
) : null
}
</div>
</footer>
</div>
);
}
}
}
The chat input box is called the send box in Web Chat. Clearing the send box is just setting the send box with an empty string. This is done automatically when you click on the send button normally. You can see in the submit send box saga that submitting the send box means performing two actions: sending the message and setting the send box.
if (sendBoxValue) {
yield put(sendMessage(sendBoxValue.trim(), method, { channelData }));
yield put(setSendBox(''));
}
This means that if you use the SUBMIT_SEND_BOX action then the send box will be cleared automatically. Of course, if you want that to work with your autocomplete component then you'll need to set the send box with the autocompleted text before you submit it. Your other option is to just use the SET_SEND_BOX action with an empty string after you send the message.
I want to update the list of "Reservations" with some new variables. But I can't make it work because the function ends before updating. I know the problem is because I make asynchronous calls but I can't solve it. How can I use Promise in this case?
I edited the code using Promise but still does not update the "Reservations"
Returns {"data": null} to my website console
In the firebase console does not generate any errors. Return the message "reservations actualized"
exports.actualizeReservations = functions.https.onCall((data, response) => {
var promisesUpdateReservations = [];
return admin.database().ref(Constants.RESERVATIONS).once("value")
.then((snapshot) => {
console.log("RESERVATIONS: " + snapshot)
snapshot.forEach((reserveSnap) => {
var reserve = reserveSnap.val();
if(reserve.reservationId && reserve.restoId) {
admin.database().ref(`${Constants.RESTAURANTS}/${reserve.restoId}`).once("value")
.then((restoSnap) => {
if(restoSnap.exists() && restoSnap.val() !== null) {
var resto = restoSnap.val();
if(resto.address && !resto.address.fullAddress) {
var restoAddress = resto.address ? {
street: resto.address.street ? resto.address.street : "",
fullAddress: resto.address.fullAddress ? resto.address.fullAddress : "",
city: resto.address.city ? resto.address.city : "",
country: resto.address.country ? resto.address.country : "",
postalCode: resto.address.postalCode ? resto.address.postalCode : "",
province: resto.address.province ? resto.address.province : "",
l: resto.l ? resto.l : null,
g: resto.g ? resto.g : null,
} : null;
var restoUserIDs = `${reserve.restoId}/${reserve.userId}`;
const promiseUpdateReservation = admin.database().ref(`${Constants.RESERVATIONS}/${reserve.reservationId}`).update({
restoAddress,
restoUserIDs
})
promisesUpdateReservations.push(promiseUpdateReservation)
}
}
})
.catch((err) => {
console.log("resto not found")
});
}
});
Promise.all(promisesUpdateReservations)
.then(() => {
console.log("reservations actualized");
return { code: 0, result: "successful", description: "reservations actualized" };
})
})
.catch((err) => {
console.log("get restaurants error: " + err);
return { code: 1, result: "successful", description: "reservations not loaded" };;
});
})
This function is going to complete immediately every time it's invoke, and likely perform none of its work. This is because it's not returning anything at all from the top-level function callback. Instead, you need to return a promise from the top level of the function that resolves with the data to send to the client only after all the async work is complete (and you're kicking off a lot of it here). Proper handling of promises is critical to making Cloud Functions work correctly.
I'm trying to lazy load firebase items to later on load more of them whenever user reaches end of div container. When i remove .endAt() and .startAt() i'm receiving the 15 items though they are not beeing incremented and it's stuck at these 15 items.
When i keep .endAt() and .startAt() i'm receiving firebase warning
Using an unspecified index. Consider adding ".indexOn": "title" at /items even though .indexOn is set. I'm confused by that warning. Thanks in advance for any help.
Firebase structure
{
"items" : {
"-Kk6aHXIyR15XiYh65Ht" : {
"author" : "joe",
"title" : "Product 1"
},
"-Kk6aMQlh6_E3CJt_Pnq" : {
"author" : "joe",
"title" : "Product 2"
}
},
"users" : {
"RG9JSm8cUndpjMfZiN6c657DMIt2" : {
"items" : {
"-Kk6aHZs5xyOWM2fHiPV" : "-Kk6aHXIyR15XiYh65Ht",
"-Kk6aMTJiLSF-RB3CZ-2" : "-Kk6aMQkw5bLQst81ft7"
},
"uid" : "RG9JSm8cUndpjMfZiN6c657DMIt2",
"username" : "joe"
}
}
}
Security rules
{
"rules": {
".read": true,
".write": "auth != null",
"users":{
"$uid": {
".write": "$uid === auth.uid"
"items":{
".indexOn": "title",
"$itemId": {
"title": {".validate": "...}
"type": {".validate": "...}
}
}
}
}
}
}
}
Code structure for lazy load
let _start = 0,
_end = 14,
_n = 15;
function lazyLoadItems(){
firebase.database().ref('items')
.orderByChild('title')
.startAt(_start)
.endAt(_end)
.limitToFirst(_n)
.on("child_added", snapshot=> console.log(snapshot.val()));
_start += _n;
_end += _n;
}
You're misunderstanding how Firebase queries work. It's easiest to see if you use hard-coded values:
firebase.database().ref('items')
.orderByChild('title')
.startAt(0)
.endAt(14)
.limitToFirst(15)
There is no item with title=0 or title=14, so the query doesn't match anything.
Firebase Database queries match on the value of the property you order on. So when you order by title the values you specify in startAt and endAt must be titles. E.g.
ref.child('items')
.orderByChild('title')
.startAt("Product 1")
.endAt("Product 1")
.limitToFirst(15)
.on("child_added", function(snapshot) { console.log(snapshot.val()); });
See for the working sample of this: http://jsbin.com/hamezu/edit?js,console
To implement pagination, you'll have to remember the last item of the previous page and pass that in to the next call: startAt(titleOfLastItemOnPreviousPage, keyOfLastItemOnPreviousPage).
startAfter is the last element
each time you have to send the last element:
const lastAlert = querySnapshot.docs[querySnapshot.docs.length - 1]
here an example:
export const fetchAlerts = (createdForId, startAfter) => {
return new Promise((resolve, reject) => {
firebase
let query = firebase
.firestore()
.collection('alerts')
.where('generated_for.uid', '==', createdForId)
.where('read', '==', false)
.orderBy('created_at', 'desc')
.limit(25)
if (startAfter) {
query = query.startAfter(startAfter)
}
query
.get()
.then(querySnapshot => {
const lastAlert = querySnapshot.docs[querySnapshot.docs.length - 1]
const alerts = querySnapshot.docs.map(doc => ({
...doc.data(),
uid: doc.id,
}))
resolve(alerts)
resolve({ alerts, lastAlert })
})
.catch(error => {
console.log(error.message)