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.
Related
I am creating a new site using NextJS, the issue i am having is in regards to a password reset verification endpoint.
When a user triggers a password reset, it goes to the API, does all the processing and then returns them to the NextJS frontend at /verifyreset, which saves a code into localstorage, does a small bit of processing and then forwards them onto another page.
The issue is that there is a Default layout wrapping the component in my _app.js like so;
function MyApp({ Component, pageProps }) {
return (
<DefaultLayout><Component {...pageProps} /></DefaultLayout>
);
}
which means that the layout shows on the /verifyreset endpoint, and I only want that endpoint to process data.
Is there any way around this? to have an endpoint that can access localstorage but not be a 'page' so to speak
I did partially understand your question, it would have been clear if you had attached more code snippets in the question.
Anyway, from your statement below:
When a user triggers a password reset, it goes to the API, does all
the processing and then returns them to the NextJS frontend at
/verifyreset, which saves a code into localstorage, does a small bit
of processing and then forwards them onto another page.
what I understood is:
User triggers a password reset [lets say from PageA]
API is invoked; some processing happen
API then, redirects user to /verifyreset page [lets say it PageB]
Navigating to the page, information is saved into localstorage
Once that is completed, user is redirected to another page [lets say it PageC]
Correct me if I am wrong, so your question is, how could you actually skip users to view /verifyreset page but do the things like save to localstorage and other background operations.
Answer 1: The api is being invoked from PageA (see 1). Instead of the api redirecting user to /verifyreset page on the frontend, send some data (JSON or XML) to the calling function (in PageA components..). Based on that data, do the processing and once every thing is complete, redirect the user to PageC. [no need to worry about PageB i.e. /verifyreset page]. Please find the code snippet below:
**API End Point**
async resetPassword(req, res) {
try {
const model = model.body || {};
let data = await PasswordBusiness.reset(model);
//data needs to have information that you require on frontend
return res.json({success: true, data: data});
} catch (error) {
return res.json({success: false, error: error});
}
}
** Frontend - pageA **
import Router from 'next/router';
const resetPassword = (model) => {
callApiEndPoint(model).then(data) {
// do what you want to do with data
//finally navigate to page c
Router.push('url-to-page-c');
});
};
return <button onClick={resetPassword}> Reset </button>
Answer 2: If you require redirecting to the page any how from the API [I think you don't necessary require this], once operation/processing is completed on API end, redirect the user directly to the pageC with some query params with data (if they are not security vulnerable data). e.g. /pagec?token=sometokens&otherinfos=otherinfos and do things on pageC itself. Once completed, remove the query string from the page without refreshing the page.
You have to put /verifyreset at the api folder.
This is what Next.js said in their documentation :
Any file inside the folder pages/api is mapped to /api/* and will be treated as an API endpoint instead of a page.
Reference : https://nextjs.org/docs/api-routes/introduction
What is the "best" (common) way to make sure that my Angular HTTP request only returns the newest response data. (I am using Angulars HttpClient)
Lets say the user submits a new request before the response of the previous request is returned. My backend needs more time to process the first request so the second request returns before the first one -> the view get's updated and later the first request returns. Now the view gets updated with old data. Thats obviously not what I want.
I found this solution to cancel the request but is that really the only way? Are there any build in functionalities to achive that?
if ( this.subscription ) {
this.subscription.unsubscribe();
}
this.subscription = this.http.get( 'myAPI' )
.subscribe(resp => {
// handle result ...
});
I know there is the switchMap() operator which only returns the latest data but as far as I know you can only use it inside of an observable. In my case I am not observing any input changes. I simply call a function on form submit via (ngSubmit)="doRequest()" directive in HTML-Template.
// get called on submit form
doRequest() {
this.http.get('myAPI')
.subscribe(resp => {
// handle result ...
});
}
Is there a way to use the switchMap operator or do you have any other solutions? Thanks for taking your time :)
PS: Of course in my real project I have an api service class with different functions doing the requests and just returning http response observables. So I subscribe to such an observable in doRequest() function.
you just make the clicks / requests a stream that you can switch off of... the user's requests / clicks are a stream of events that you can observe and react to.
private requestSource = new Subject();
request() {
this.requestSource.next();
}
ngOnInit() {
this.requestSource.switchMap(req => this.http.get('myApi'))
.subscribe(response => console.log(response, "latest"));
}
modern client programming rule of thumb: everything is a stream, even if it doesn't look like one right away.
Edit: In the case of rxjs 6 or latest version, add pipe operator with the above code.
this.requestSource.pipe(switchMap(...));
Suppose i have this component that will fetch all data from API periodically.
// on parent component
constructor(props) {
super(props);
this.state = {
datas: [],
}
this.apiCall = this.apiCall.bind.this
}
apiCall(){
//fetch data here
this.setState({
datas: //result from fetch
})
}
ComponentDidMount(){
this.interval = setInterval(() => this.apiCall(), 10000);
}
handleEditData(){
//api call, edit one data in server using POST
}
...
<ChildComponent onEdit={this.handleEdit}/>
What should i do when child component edit data? or what should i put in handleEditData?
Should i edit one data by sending POST to api, and then update local state manually by using setState inside handleEdit?
Or should i sending POST to api, and then call apiCall function to get new datas, those change its state and re-render?
thanks
This is an interesting question and it is less a question that is regarding react, and more of a resource management question. The question to ask is what the data is doing. If you have data that needs to be logged, you should store your data using the API. If the data is going to be reused in your app and you already have it, why not use it in situ? It reduces your calls (and therefore makes the app more efficient) and it creates one less point of breakage in the process.
To reiterate: When you design a system, you should look locally before you involve more moving parts. I would simply bubble up the data and use setState with the local object.
Sometimes the client network connection would be lost and if you updated the data in the client side either before sending the request or after receiving the failure response from the server since that's not be updated in the back-end!!! and after refresh the browser, the consumer will see the data that have not been updated.
So I suggest you, update the data after receiving the successful response from the server.
First you should call api for edit one data in server using POST and on the success of this api's response you can set state
I'm trying to build a contacts list app to teach myself reactjs, and I am learning fluxible now.
1) A new contact is entered. Upon submit, a newContact object is created that holds:
firstName
lastName
email
phone1 (can add up to 3 phones)
image (right now its just a text field, you can add a URL..)
2) This newContact object is sent as a payload to my createNewContactAction, and dispatcher is "alerted" that a new contact has been made.
3) At this point, ContactStore comes into play.. This is where I am stuck.
I have gotten my object to this point. If I want to save this object to my database, is this where I would do that?
I'm a bit confused as to what to do next. My end goal would be to show all the contacts in a list, so I need to add each new contact somewhere so I can pull all of them.
Can someone point me in the right direction?
I would make a request to the server to save the newContact object before calling the createNewContactAction function. If the save is successful, then you can call the createNewContactAction to store the newContact object in the ContactStore. If it isn't successful, then you can do some error handling.
To understand why I think this pattern is preferable in most cases, imagine that you saved the contact in the store and then tried to save it in the database, but then the attempt to save in the database was unsuccessful for some reason. Now the store and database are out of sync, and you have to undo all of your changes to the store to get them back in sync. Making sure the database save is successful first makes it much easier to keep the store and database in sync.
There are cases where you might want to stash your data in the store before the database, but a user submitting a form with data you want to save in the database likely isn't one of those cases.
I like to create an additional file to handle my API calls, having all of your xhttp calls in your store can clutter things very quickly. I usually name it with my store, so in this case something like "contacts-api.js". In the api file I export an object with all of the api methods I need. eg using superagent for xhttp requests:
module.exports = {
createNewContact: function(data, callback) {
request
.post('/url')
.send(data)
.end(function(res, err) {
if (callback && typeof callback === 'function') {
callback(res, err);
}
});
}
}
I usually end up creating 3 actions per request. First one is to trigger the initial request with data, next is a success with the results and last is one for errors.
Your store methods for each action might end up looking something like this:
onCreateNewContactRequest: function(data) {
api.createNewContact(data, function(res, err) {
if (err) {
ContactsActions.createNewContactError(err);
} else {
ContactsActions.createNewContactSuccess(res);
}
});
},
onCreateNewContactSuccess: function(res) {
// save data to store
this.newContact = res;
},
onCreateNewContactError: function(err) {
// save error to store
this.error = err;
}
DB calls should ideally be made by action creators. Stores should only contain data.
I am still a little confused about the way Ember fetches data from remote API and save them in the browser.
So I have created a REST Adapter to get sets of records with an Ajax call, a serializer, and the corresponding model. Suppose they are posts, and I have successfully made a posts index page where the user can click on any post to get into the post detail page.
The Ajax call happens on the index page, and using the Ember inspector, it is clear that all the records are stored in the local store.
But when I click the "back link" which is available on every post detail page, it does redirect to '/posts/' but it seems to make the ajax call again. So all the posts are fetched from the API once again making the page much less responsive.
Here's my questions:
How does that part of Ember work and how do I make Ember simply get the records from the local store without making Ajax call again and again? (unless the user refresh the browser)
If I make a GET request to 'post/1' , no data will be available since in this route no Ajax call should be made. But how do I let the data show? Should I set up another REST adapter for individual post or is it possible to get the record from the local store if an Ajax call has been made?
Hope that makes sense and thanks in advance!
Update:
My post adapter:
App.PostAdapter = DS.RESTAdapter.extend({
findAll: function(store, type, sinceToken) {
var url = 'THE URL TO GET JSON BACK';
return $.getJSON(url).then(function(data) {
return posts;
})
}
});
My Post and Posts routes:
App.PostRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('post', params.postId);
}
})
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.find('post');
}
})
Regarding your first question: It depends on the model callback of your route. If you use the all method of the store, the Ajax Request won't be made again. (But: You'd be responsible to get the data the first time around. You way want to sideload it somewhere or may want to call find if all didn't return anything. It depends on your application.
Regarding your second question: The RESTAdapter should be available for single data items as well as for lists. You can implement a model hook in the route using find. If you link-to this route with an object (instead of an ID), this hook won't be called. So the hook would only be called when needed.