How can I get #ngrx/data to retrieve data only when the entityCache is empty? - ngrx

I am customising an entity service in #ngrx/data, and I would like to have the store cache the data, such that it only retrieves the data from the API once, when it hasn't loaded yet, but skips any subsequent calls to the API.
So far, I can't seem to get a method override working:
load(options?: EntityActionOptions | undefined): Observable<Product[]> {
return this.loaded$.pipe(
tap(loaded => {
if (!loaded) {
super.load(options);
}
}),
filter(loaded => loaded),
switchMap(() => this.entities$)
);
}

Related

Component method fails to assign data that has not yet been fetched by the service

officeView.component.ts
setSelectedPerson(id:number)
{
this.pservice.getPerson(id);
localStorage.setItem("selectedPerson", JSON.stringify(this.pservice.person));
}
person.service.ts
getPerson(id:number)
{
this.http.get(personUrl + id).subscribe(response => this.person = response )
}
person:Person;
I'm executing setSelectedPerson method from OfficeViewComponent and here's what I'm hoping to happen:
I ask PersonService to fetch the data from api and assign it to it's variable - PersonService.person;
Now that the response is assigned to the service person variable, I'm expecting it to be stringified and saved in localStorage.
But here's what actually happens:
I ask PersonService to fetch the data, PersonService reacts and proceeds with the request, but by the time PersonService.getPerson() is finished, localStorage has already attempted to collect the data from PersonService.person, which - at that time - was unassigned.
I know there is a way to wait until the service method finishes it's work, but I don't know exactly what should I use.
Return the subscription from the service and use it to set data inside it. You don't need any variable inside your service.
officeView.component.ts :
setSelectedPerson(id:number){
this.pservice.getPerson(id).subscribe(
response => {
localStorage.setItem("selectedPerson", JSON.stringify(response));
},error => {
console.log('Error :',error.error)
}
)
}
person.service.ts :
getPerson(id:number) : Observable<any>{
return this.http.get(personUrl + id);
}
You're right, you should wait until the result is ready.
By then you can call the setSelectedPerson func.
//service func
getPerson(id:number) {
return this.http.get(personUrl + id);
}
//component func
setSelectedPerson(id:number){
this.pservice.getPerson(id).subscribe(data => {
localStorage.setItem("selectedPerson", JSON.stringify(data ));
});
}
The problem with you code is the early subscribe in service itself, Ideally it should be in the component (at the caller)
officeView.component.ts :
setSelectedPerson( id : number ){
this.pservice.getPerson(id).subscribe(
response => {
localStorage.setItem("selectedPerson", JSON.stringify(response));
},error => {
console.log( 'Error :',error.error )
}
)
}
person.service.ts :
getPerson( id : number ) : Observable< any >{
return this.http.get( personUrl + id );
}
person.service.ts
getPerson(id:number) {
return this.http.get(personUrl + id);
person:Person;
and when you calling the HTTP service you should use subscribe
setSelectedPerson(id:number){
this.pservice.getPerson(id).subscribe(data=>{
console.log(data);
});
}

Access to the latest state from redux-observable epic after api response

I have read this Accessing the state from within a redux-observable epic, but it doesn't helps me finding the answer.
I am using redux-observable in my react-redux app, I have an epic will trigger an API invoke, code as below:
const streamEpicGet = (action$: Observable<Action>, state$) => action$
.pipe(
ofType(streamActions.STREAM_ITEM_GET),
withLatestFrom(state$),
mergeMap(([action, state]) => {
return observableRequest(
{
failureObservable: error => {
const actions = []
return actions
},
settings: {
body: queryParams, // I can access to the state data here to organize the query params I need for calling the API
url: '/path/to/api',
},
successObservable: result => {
const { pushQueue } = state
// here I want to access to the latest state data after API response
}
})
}),
)
In the above code, I use withLatestFrom(state$) so I can access to the latest data when executing the mergeMap operator code, that is to say I can access to the state data here to organize the query params for API.
However, during the time after API request sends out and before it responses, there are other actions happening, which are changing the state's pushQueue, so after the API response, I want to read the latest state pushQueue in my successObservable callback.
My problem is I always get the same state data as when preparing the query param, i.e.: I cannot get the latest state data after API response in successObservable callback.
Let me know if you need more info, thanks.
const streamEpicGet = (action$: Observable<Action>, state$) => action$
.pipe(
ofType(streamActions.STREAM_ITEM_GET),
withLatestFrom(state$),
mergeMap(([action, state]) => {
return observableRequest(
{
failureObservable: error => {
const actions = []
return actions
},
settings: {
body: queryParams, // I can access to the state data here to organize the query params I need for calling the API
url: '/path/to/api',
},
successObservable: result => {
const { pushQueue } = state$.value // Use state$.value to always access to the latest state
// here I want to access to the latest state data after API response
}
})
}),
)
So the answer is to use state$.value.
I get this by reading Accessing state in redux-observable Migration document

Google:assistance the final response did not set

we have Google Assistance project, that is working fine, for every intent,
now i want to get dynamic data from web service and return, for that, I am using request module, but its giving error
the final response did not set
below is my code
app.intent('doctor_list', (conv, {doctor}) => {
Request.get("url", (error, response, body) => {
if(error) {
con.ask('data return')
}
con.ask('err')
});
})
You aren't clear in what your Request object is, but I suspect the problem is that you're not returning a Promise object from your call. Network calls are handled asynchronously in node.js, and when you make an async call, you need to return a Promise object.
The easiest way to handle this is using the request-promise-native package. Your code might look something like this:
const Request = require('request-promise-native');
app.intent('doctor_list', (conv,{doctor}) => {
return Request.get('url')
.then( body => {
conv.ask( 'data return' );
})
.catch( err => {
console.log( err );
conv.close( 'error' );
});
});

Firebase: Get notified just after the firebase-function trigger promise completed

In my app I pushed some object to my firebase-database and immediately after that (after the then-promise fully filled) I fetch the object (with the returned key) from the database (with the on-value method).
In addition, I make some changes on the pushed object using the firebase-functions.
How can I receive the object (in the app) just after the changes and not before? (like other ordinary backend services)
I hope this helps you, I have not tested this piece of code but it should help you in the right direction.
Also dont use this exact code in production, there is plenty room for improvement, this is just an example code.
exports.testFunction = functions.https.onRequest((req, res) => {
if (req && req.body) {
if (
req.body.hasOwnProperty('name') &&
req.body.hasOwnProperty('age')
) {
const person = {
name: req.body['name'],
age: req.body['age']
}
// Make some changes to the person object
person['hobby'] = 'Programmer';
// Add object to FireStore
admin
.firestore()
.collection('/persons')
.add(person)
.then((success) => {
// Return the added & changed person
res.status(201).send(JSON.stringify(person));
})
.catch((error) => {
// Error
console.error('Something broke', error)
res.status(500).send();
});
}
else {
// Error
res.status(500).send({err: 'Missing property'});
}
}
else {
// Error
res.status(500).send({err: 'Missing something'});
}
});

Get data from Firebase in Ionic 2

I have written my app with ionic 2 and followed the tutorial of Josh Morony, but I don't know how I can get a specific element from my firebase database.
For example I have this tree :
user
|__ (user_id)
|_ name : 'toto'
And so on...
I tried this way:
elt: FirebaseListObservable<any[]>;
this.elt = af.database.list('/user');
But how can I work with the selected data?
I found this solution which is similar than yours :
af.database.list('/user', { preserveSnapshot: true})
.subscribe(snapshots=>{
snapshots.forEach(snapshot => {
console.log(snapshot.key, snapshot.val());
this.items.push({
id: snapshot.key,
name: snapshot.val().name
});
});
});
In order to get the data as an array from a provider you need to return a promise which will be returned once the firebaseListObservable event is triggered with return data.
In your provider .ts
getData(fbPath): Promise<any> {
return new Promise(resolve => {
this.db.list(fbPath).subscribe(data => {
resolve(data);
})
})
}
Here the promise resolves once the data is populated and returns an array with easy access to the $value and $key properties. Which is ideal for creating conditionals or complex queries or a provider service with generic properties ( as opposed to querying the snapshot of the firebaseListObservable directly )
In your controller you can then write something like
this.providerName.getData('users').then(data => {
console.log('data',data);
})
This will return an object literal with the values
$exists
$key
$value
So now if you want a match conditional you can loop through the data with the match condition on the $key of the table users
if(myUserIdVar === data.$key){ // do something here };
A tidier syntax can be found using a library like lodash Where for example if you want a condition to match a stored id, say firebase.auth().currentUser.uid you can do a simple _.find
import { find } from 'lodash';
import * as firebase from 'firebase'; // Or just the firebase auth stuff
...
let filteredUser = find(data, ['$key', firebase.auth().currentUser.uid])
The $key value will be equal to the |__ (user_id) value
I think, af.database.list('/user') returns an Observable. You need to subscribe to it. Like this:
af.database.list('/user')
.subscribe(data => {
console.log("Data is : ",data);
},
(ex) => {
console.log('Found exception: ', ex);
});
Also, if this is in your provider and you want to return the data, you can create a new Observable and return it. If need help, could edit my answer to that also.

Resources