I have two pages and when use clicks continue on page1, I am calling an API and browser opens page2.
Meanwhile async process is happening and I am trying to save results of API response in the window object. After that I am trying to get access to those values on window object, which are undefined.
What am I doing wrong in my async function?
const {input} = this.state;
this.props.history.push('/step2');
generateBook(payload)
.then(res => {
input.id= res.data.bookId;
input.color= res.data.color;
window.input = input;
})
On page2 I am trying to get access to the window.input.id ad window.input.color
Plese use the following way to pass data to step2
this.props.router.push({
pathname: '/step2',
state: {
inputData:input
}
})
you can retrive it via this.props.location.state in step2 route
Related
I am using cryptocomare API to get crypto coins data within a Nextjs App. What i doing is that when a user clicks on a perticular symbol, i redirect it to the coin details page where i try to extract the clicked symbol with getServerSideProps as follows and then dynamically put in the API call and send it to the API server.
`
export const getServerSideProps = async (context) => {
const res = await fetch(
`https://min-api.cryptocompare.com/data/pricemultifull?tsyms=USD&fsyms=${context.params.symbol}`
);
const icon = await res.json();
return {
props: {
icon,
},
};
};
`
This call returns a json object of nested objects and it goes to 2-3 levels deep. On Top it looks like following:
API call response
Inside my code, I want to access the data Object -> RAW -> (whatever the user clicked on). But, Since the Symbol or coin queried by the user is dynamic (means i can't predict what is clicked) I never know what to query. SO i tried this to access the data object.RAW[0]
In principal it should give me the whatever object is inside the object.RAW But it returns undefined
Can please someone guide me , how can i get the data inside object.RAW without knowing what is inside?
Thanks!
I have tried object.RAW[0] to access the data...,....
You can use Object.values(object.RAW) to get an array of the values inside RAW (assuming RAW is not undefined)
Doc: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values
Currently I want to pass data from edit page to add page. So i pass the query using {path: '/test/admin/clinicvisit/add', query:{id:this.$route.params.id}}. I was able to pass the data to add page using the query however sometimes the data may not present when it refreshes a few times. Is there any possible way to make the data consistent stay in the add page ?
add.vue
async testRoute(){
let visitId = this.$route.query.id
if (visitId){
let clinicVisit = await this.api.medical.viewClinicVisit(visitId)
this.inputs = await this.api.medical.optionsClinicVisit()
this.$set(this.inputs.staff, "item_text", "username")
this.$set(this.inputs.staff, "item_value", "id")
this.$set(this.inputs.staff, "items", await this.api.profile.listStaff({}))
if(clinicVisit.staff){
this.$set(this.inputs.staff, "value", clinicVisit.staff)
this.$set(this.inputs.staff, "tmp_value", clinicVisit.staff_detail.name)
}
mounted()
{
this.testRoute()
}
This can be done in multiple ways.
Using a global store, You can use a library like Vuex to share the state between the components.
Using the Local Storage, if you want to preserve the data and keep saved after hard refreshing the page.
Using Session Storage, if you want to preserve the data and keep saved during the user session, but whenever the user close the browser tab it will be gone.
When you observe that the data is not present after a few refreshes, does it dissapear from the URL in your browser, or does it just not load?
If you want the data to stay more consistently, consider using localStorage
localStorage.setItem('item_text', 'username') //Save variable in browser data
let item_text = window.localStorage.getItem('item_text') //Get variable from browser data
EDIT:
If the URL is still present in the browser window, that sounds like a weird loading bug, where your code runs before the route is parsed by Vue.
You can try using a watcher instead of the "mounted" function:
watch: {
$route: {
immediate: true,
handler() {
this.testRoute()
}
}
}
I solved this error by setting a timeout function in my edit.vue.
handleCreate(event){
event.preventDefault();
this.$router.push({path: '/medical/admin/clinicvisit/add', query:{id:this.$route.params.id}},2000)
// now 'this' is referencing the Vue object and not the 'setTimeout' scope
this.loadClinicVisitData = true;
setTimeout(() => this.loadClinicVisitData = false,1000)
}
I am currently facing a problem with the authentication flow and the navigation file. Basicly I want to have the authentication logic inside my navigation file, so I can conditionaly say if I want to show the login or not.
Like this:
const { id: contextId } = useId()
const { token } = useToken()
const _id = contextId : getIdParam(contextId)
const globalParams = { initialParams: { _id } }
And then I use the _id to conditionally show or not the login. The problem with this implementation is, that I am using two ways to access the id param in the url:
ContextAPI (I set the id after login so it is available in that session)
getIdParam function, to access the id param via location.path, whenI try to reload a screen without passing in login.
Is there any way I can access params in the navigation file, I tried to use useRoute hook, but I am not allowed to use it outside of a screen.
Any help? Thanks
Well I made this Library app, where an user can login and add books. So, when a user login the app fetch data from a firestore collection, cool. The problem exists when the user login once, logout and then login again without refreshing the app. If the user do this twice, the fetch twice, if thrice, the fetch thrice. The code that executes multiple times its the fetchBooks(), the signInWithGoogle() only executes once. Here's the code involved:
function signInWithGoogle(){
const provider = new firebase.auth.GoogleAuthProvider()
auth.signInWithPopup(provider)
.then(result => {
// Create the new user document in firestore
createNewUserDocument(result.user)
// fetch feed data
auth.onAuthStateChanged(user =>{
user? fetchBooks() : null
})
}).catch(err => {
console.log(err)
})
signUpForm.reset()
signUpModal.hide()
signInForm.reset()
signInModal.hide()
}
function fetchBooks() {
const docRef = db.collection('users').doc(auth.currentUser.uid).collection('books')
docRef.get().then(querySnapshot =>{
console.log(querySnapshot)
querySnapshot.forEach(doc => {
const data = doc.data()
console.log(doc.data());
addCardToHTML(data.title, data.author, data.pages, data.description, data.read)
})
})
}
onAuthStateChanged is a subscription that triggers itself when there's a change in the user's authentication state.
So it will trigger when you log in, when you log out, etc.
So ideally you'd want to wait until the user logs in, and then call the fetchBooks() function, but if you keep doing it inside of the subscriber the function will trigger any time the subscriber emits a new value.
I would recommend starting with a restructure of your code to have functions that do individual things. Right now, you have a function signInWithGoogle. That function should only sign the user in with Google and return a promise with the result of that sign in. Instead, you have it signing in the user, fetching books (which itself is also fetching books AND modifying the DOM), and calling methods on your signUp elements.
Restructuring this to have some other top-level function would likely help you handle your problem easier. Specifically, try something like this:
function handleSignIn() {
signInWithGoogle()
.then(fetchBooks)
.then(books => {
books.forEach(book => addCardToHTML(...))
})
}
This is a good start because now it's clear what each individual function is doing. So now to handle your specific issue, I'll assume that the problem you're facing is that you're seeing the books be added multiple times. In that case, I would think what you'd want to happen is that:
When a user is signed in, you want to load their books and display them on the page.
When they log out, you want the books to be unloaded from the screen
When they log back in, the books are re-loaded and displayed.
If all of those assumptions are correct, then your problem wouldn't be with the code you have, but rather the signout functionality. When the user signs out, you need to add a function that will remove the books from the HTML. That way, when they sign back in after signing out, the handleSignIn function will kick off again and the addCardToHTML function will be running on a blank HTML page rather than a page that already has the cards.
Example:
function handleSignOut() {
signOut()
.then(clearBookCards)
}
function clearBookCards() {
// Manipulate DOM to remove all of the card HTML nodes
}
I have a very large request that takes 15 seconds to return the data. I am wanting to call that request when user logs in to reduce the time it takes when they go to the route where that data is loaded (they could get there after it has completed which would show them data instantly, or it might not have completed, but only make them wait a few seconds at that point).
So when the user logs in and gets success I make request for the large dataset:
this.getDataService.getAsyncData(data.LinkToken); // This loads request from data service
Then I save that data into local storage when it is returned which is then available to retrieve when the user lands on the route that makes that request from ngOnOnit()
getAsyncData(linkToken){ //this is background request that is loading while user navigates through app
this.httpC.get(this.baseUrl + "/AccountInfo/Data?linkToken=" + linkToken + "&deliveryTypeId=" + 0 + "&pickupLocationId=" + 0 + "&paymentTypeId=" + 0).map((res:Response) => res.json()).subscribe(res => {
this.asycCollection = res;
this.globalService.setData(this.asycCollection) //This passes this data to service that stores in local storage
console.log(this.asycCollection);
})
}
Then that data can be returned as a promise request from the component loaded when that route loads
//This sets local storage with the data
setData(refillObject:any){
this.refillObj = refillObject;
window.localStorage.setItem("refillData", JSON.stringify(this.refillObj))
}
//This gets promise of that background async call
getData(){
let refillInformation:any = window.localStorage.getItem("refillData");
return new Promise<any>((resolve, reject) => resolve(refillInformation));
}
Then from my route component I want to retrieve this data BUT ONLY when it has finished loading the data, otherwise it comes back null and of course nothing works. So, if the user quickly navigates to this page, then it returns null (as the request has not finished loading data) and blows up, but if the user comes back after the request has finished, it all works as designed.
How can I wait and get data when it has finished loading? Keep in mind that this was a background async request from when the user logged in that I'm retrieving from local storage and not making a new request to the REST Svc.
Component Code:
getAsyncRefills(success){
this.globalService.getData().then(data => { //everything below this blows up if the page is loaded before the data request has finished loading to local storage.
this.orderData = JSON.parse(data);
this.currentDeliveryType = this.orderData.DeliveryTypes.find((item) =>
item.DeliveryTypeId == this.orderData.DeliveryTypeId);
this.currentPaymentArr = this.currentDeliveryType.PaymentTypes;
this.currentPickupLocations = this.currentDeliveryType.PickupLocations;
this.setOptions();
this.buildOrders();
})
}
I would approach this problem in the following way.
I would build a service that has the responsibility to
fires the execution of the heavy query on the back end
expose as a public property an Observable that emits when the result of the query has arrived
once the data arrive from the back end, it emits the data using the Observable exposed as public property
This service is injected, via Dependency Injection, into the component that performs the login and the component that needs to show the data.
The component that does the login, once the login is successful, calls the method of the service that fires the query.
The component that needs to show the data can use the Observable exposed as public property by the service to show the data once they have arrived. There are several ways to do this. You can use the async pipe from Angular which allows you to reference the Observable directly in the template, or you can subscribe to the Observable and then use the function defined in the subscription logic to fill the required variables.
For something like this I wouldn't use local storage but would use a service (sorry don't have an IDE so code may not be perfect).
#Injectable()
export class ExpensiveDataStore {
private expensiveData_: ConnectableObservable<ExpensiveThing>;
private connection: Subscription;
get expensiveData(): Observable<ExpensiveThing> {
if(!this.expensiveData_) {
throw Error('Must initialize store first!');
}
return this.expensiveData_;
}
initialize() {
this.expensiveData_ = this.goFetchExpensiveData().publishLast();
this.connection = this.expensiveData.connect();
}
void reset() {
this.connection.unsubscribe();
this.connection = this.expensiveData.connect();
}
}
In your app.component.ts or some high level component you can call initialize(). In your component that needs the data you can just subscribe to expensiveData.