Update cached data using rxjs publishReplay: Angular 5 - rxjs

I have a list of users that I want to cache so that different component in my Angular 5 app does not hit the web service, and rather return cached response. To do this I did the following:
getAllUsers() {
return this.getUncachedUsersList().publishReplay().refCount();
}
getUncachedUsersList() {
return this.http.get('https://......');
}
In the above code snippet, I have two methods. I call getAllUsers inside all the components that needs users list, except in the case where let say I am adding a user and then I need an updated list. In that case I call 'getUncachedUsersList' to get the latest.
The problem is, when I call 'getUncachedUsersList', I expect 'getAllUsers' to cache the new list, but instead it return the same old list that was cached before adding a new user. So I would like to know how can I clear the cached response and save the new response that I get from 'getUncachedUsersList' and return the new response when 'getAllUsers' is called.

Rathar than doing like this, you should considering maintain a cacheable Subject.
// behavior subject do cache the latest result
// each subscribe to userList$ get the latest
userList$ = new BehaviorSubject([]);
// each time getNewUserList get call
// userList$ get the new list by calling next
getNewUserList() {
this.http.get(`http://...`).subscribe(list => this.userList$.next(list));
}

Related

ReactiveCrud, how can I delay for a moment without blocking IO before deleting data

So suppose I have created an api for booking hotel room by making a ticket and then I will use the information from the ticket (ex. size, bed etc.) to find available room, but if it could not find any available room I will need to delete this ticket later. So for a better user experience I don't want to suddenly delete the ticket, but I want to change the ticket status to be reject or something else and then delete it after 30second. So here what I have tried.
val ticket = ticketRepostiory
.findById(request.ticketId)
.map { it.copy(status = TicketStatus.REJECT) } // mapping new status
.flatMap { ticketRepostiory.save(it) } // save
.then(Mono.delay(Duration.ofSeconds(30))) // display to user for a 30s.
.flatMap { ticketRepostiory.deleteById(request.ticketId) } // delete it
.block()
notificationService.notify(...etc) // notification service notify status
But I found the problems that this code has blocking the other code for 30s (suppose another user wants to create a new ticket it won't create or save any data to db until 30s.) So how can I delete the data after 30s without blocking other request
So I fixed it now by using .subscribe() method instead of .block()

Multiple Observables waiting for single Observable completion on RetryWhen

I am trying to build an app using Oauth2.
There are multiple (GET) calls to API from the frontend and I deal with them like so:
// Call API for data
this.apiGet('/foo?id=bar).pipe(
// If it fails, use refresh token to obtain new access token and try again
retryWhen(x => {
// If there is existing request (Observable), use it
if (this.refreshTokenObservable !== null) {
return this.refreshTokenObservable;
}
// Otherwise make new request (fills this.refreshTokenObservable)
return this.getAccessByRefreshToken();
})
)
This works fine when there is only one apiGet(), however, when using multiple calls one right after another, the refreshTokenObservable variable do not fill quickly enough, causing multiple calls and, subsequently, errors.
Is there any way to prevent this?

Race condition between componentWillReceiveProps and componentDidMount

I have a component that takes some data in the props and make an ajax request with them.
var ItemList = React.createClass({
propTypes: {
filters: React.PropTypes.object.isRequired,
},
getInitialState: function() {
return {items: []};
},
componentDidMount: function() {
this.ajaxFetchItems(this.props.filters);
},
componentWillReceiveProps: function(nextProps) {
this.ajaxFetchItems(nextProps.filters);
},
ajaxFetchItems: function(filter) {
....
this.setState({items: data});
}
}
The problem is that the props are changed almost immediately, and sometimes the ajax call in componentDidMount is slightly slower than the one in componentWillReceiveProps, so the initial state is written after the first update.
How can I avoid that a slow componentDidMount will overwrite a fast componentWillReceiveProps?
There are better ways to handle the lifecycle of a react component that downloads its data?
You could put a timestamp in state for the latest update processed.
And somehow make sure that the timestamp of the original Ajax request is included in the Ajax results.
And add a shouldComponentUpdate() to check if the received results have a timestamp that is later than the timestamp in state. If not: return false, and your component will ignore the results.
By the way: componentDidMount and componentWillReceiveProps can by definition only be run in that order. I suspect that your first Ajax call takes long to return result, and your second call is fast. So you get the Ajax results back in the wrong order.
(Not due to slow react functions).
UPDATE:
Using shouldComponentUpdate is the react-way of dealing with this case: Its purpose is to allow for comparison of the new state with the old state, and based on that comparison, not rerender.
The issue is (most likely) generated by the order in which ajax responses come in:
Ajax call 1 (fired in componentDidMount in this example)
Ajax call 2 (fired in componentWillReceiveProps, trigger by parent of component)
Response from call 2 comes in
Response from call 1 comes in.
So a more generic question/ solution would be for "How to handle ajax responses coming back in the wrong order".
The timestamp (in shouldComponentUpdate) is one way to do it.
An alternative (described here) is to make the second request (in componentWillReceiveProps) abort the first ajax request.
Revisit:
After giving it some further thought (the calls in componentDidMount and componentWillReceiveProps did not feel right), a more general react-like way to approach your component would probably be as follows:
Your component's job is basically to:
receive filter via prop,
use filter to fetch list with ajax,
and render ajax reponse = list.
So it has 2 inputs:
filter (= prop)
list (= ajax response)
and only 1 output = list (which may be empty).
Workings:
The first time component receives filter as prop: it needs to send out ajax request, and render an empty list or some loading state.
all subsequent filters: component should send out a new ajax request (and kill possible outstanding old requests), and it should NOT re-render (!).
whenever it receives an ajax response, it should re-render the list (by updating state).
Setting this up with react would probably look something like this:
getInitialState() {
this.fetchAjax(this.props.filter); // initiate first ajax call here
return { list : [] }; // used to maybe display "loading.." message
}
componentWillReceiveProps(nextProps) {
this.fetchAjax(nextProps.filter); // send off ajax call to get new list with new filter
}
shouldComponentUpdate(nextProps, nextState) {
return (this.state.list != nextState.list); // only update component if there is a new list
// so when new props (filter) comes in there is NO rerender
}
render() {
createChildrenWith(this.state.list);
}
fetchAjax(filter) {
killOutStandingRequests(); // some procedure to kill old ajax requests
getListAsync…
request: filter // request new list with new filter
responseHandler: this.handleResponse // add responseHandler
}
responseHandler(data) {
this.setState({ list : data }); // put the new list in state, triggering render
}
The original timestamp in state would solve the question posted above, but I thought I'd share the revised react component as a bonus...

Reload page with new context in express

I have a page that lists events, in which admins are can delete individual items with an AJAX call. I want to reload the page when an event is deleted, but I am having trouble implementing it with my current understanding of express' usual req, res, and next.
Here is my current implementation (simplified):
Simple jQuery code:
$(".delete").click(function(e){
$.post("/events/delete",{del:$(this).val()})
})
in my routes file:
function eventCtrl(req,res){
Event.find({}).exec(function(err,events){
...
var context = {
events:events,
...
}
res.render('events',context);
});
}
function deleteCtrl(req,res,next){
Event.findById(req.param("del")).exec(function(err,event){
// delete my event from google calendar
...
event.remove(function(err){
...
return next();
});
});
}
app.get('/events',eventCtrl);
app.post('/events/delete',deleteCtrl,eventCtrl);
When I make a post request with AJAX all the req handlers are called, the events are deleted successfully, but nothing reloads. Am I misunderstanding what res.render() does?
I have also tried using a success handler in my jQuery code when I make the post request, with a res.redirect() from deleteCtrl, but my context is undefined in that case.
on the client side, you are using
$(".delete").click(function(e){
$.post("/events/delete",{del:$(this).val()})
})
this code does not instruct the browser to do anything when the response from the post is received. So nothing visible happens in the browser.
You problem is not located server side ; the server answers with the context object. You are simply not doing anything with this answer.
Try simply adding a successHandler.
Generally speaking this would not be a best practice. What you want to do is reconcile the data. If the delete is successful, then just splice the object out of the array it exists in client-side. One alternative would be to actually send back a refreshed data set:
res.json( /* get the refreshed data set */ );
Then client-side, in the callback, you'd actually just set the data source(s) back up based on the result:
... myCallback(res) {
// refresh the data source(s) from the result
}

Updating existing Parse object in Cloud Code

I am using Parse for my backend and want to search for existing friend requests and update those instead of creating new ones (if there is already an existing one).
I thought I figured out how to do it but when I submit new friend requests they get created as new objects instead of updating the old one, even though I found an existing request.
Here is the code I am using:
Parse.Cloud.beforeSave("FriendRequest", function(request, response) {
//search for an existing friend request with the same "from" and "to"
var query = new Parse.Query("FriendRequest");
query.equalTo("from", request.object.get("from"))
.equalTo("to", request.object.get("to"));
query.find({
success: function(results) {
if(results.length > 0)
{
var result = results[0];
//the new request id is undefined as expected
console.log("request id: " + request.object.id);
//the result id is valid for an object in the db as expected
console.log("result id: " + results[0].id);
//set the id of the request to the id of the existing db object
request.object.id = results[0].id;
//the valid id is now in the request object id
console.log("request id: " + request.object.id);
//after response.success, the database shows a new entry
//with a different id
//instead of updating the existing entry
response.success();
}
}
});
});
There isn't a lot going on here. The query does come back successful with the correct entry in the database. I can confirm that I get the correct objectId for the existing item in the database. Any thoughts or ideas are appreciated!
You can't manually set the objectId of an object.
If you want beforeSave to NOT create a new object (which is what you're about to do when beforeSave is called), you need to manually update the existing object and then respond with a failure. If you respond with response.success(), the object will be saved normally.
In your code, you don't seem to make any changes to the existing object. All you really need to do is to return response.error (https://parse.com/docs/cloud_code_guide#functions-onsave)
Of course, you should also handle this in your code somehow. Either by alerting the user, or handling it silently.
However; why does your code attempt to save a new friend request if one already exist? Your app should know that one exists and disable the friend request button or whatever the UI offers.

Resources