Pagination with Ionic 3 Firestore and Observables - scroll

I am not able to figure out how to do pagination for the following scenario. Please let me know if there is an easier way and I am complicating it. Its using Ionic 3 and firestore.
show.html
<ion-row *ngFor="let item of observable | async">
{{item.value}}
</ion-row>
<ion-infinite-scroll (ionInfinite)="doInfinite($event)">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</ion-content>
show.ts
observable: Observable<any[]>;
this.observable = this.provider.getData(); //in the corstructor method
provider.ts
getData(){
return this.afs.collection('data').limit(10).snapshotChanges().map(a=>{
return a.map(x=> {
return {
value: x.payload.doc.data(),
};
});
});
}
So, I am calling a method from provider.ts to get the collection list as observable in my show.ts file. The observable is getting displayed in the show.html with async pipe
In order to do a pagination for firestore I have to use startAt(dataSnap) or startAfter(dataSnap). To use these methods I need the last data snapshot of the collection. How can I get the last item from an observable?
Any idea how to proceed with this approach.

Related

Returning out of a function the results of an observable

I have a function that I need to return an observable as it is being subscribed to elsewhere, but because my function is making a request asynchronously and then trying to return that data as an observable it is breaking.
An example of how it looks currently:
function userObservable() {
const select = 'select * from users';
this.db.query(select).subscribe(users => {
// apply some logic and filter to users here
// function just returns users as an observable
return asObservable(users);
});
}
function subscribeFunction() {
userObservable().subscribe(users => {
// do whatever
});
}
but basically this is breaking because it seems like it's trying to subscribe in subscribeFunction() before the userObservable() function has returned the observable. Am I missing something in the way to implement this? Can I make it wait to ensure the first function is returning properly?
After you subscribe to an observable it can only return a subscription and no longer be able to return observable
You can however use operators like map/filter to transform filter your user object and return it
return this.db.query(select).pipe(map(users => {
// apply some logic and filter to users here
// function just returns users as an observable
users=user.filter(........)
return users
}));

How Subscribe works

I am learning the rxjs and playing with observable and subscribe. I have the following method in component.ts file which returns true/false from API
this.http.get(apiUrl+"/actionName")
.subscribe(result=>
{
if(result){
//step1
//1.show success message
//2.call the other method
//3.and after returned from here
}else{// not true
//1. show error message
//2. returned from here
}
});
});
//step2
// call another function
}
Whenever I subscribe to an observable it immediately jumps to the next line i.e. step 2 and another method gets called first. Which I don't want to do.
I want to run step1 first until it gets finished completely only then it should go to step2.
Thank you in advance.
You don't say so in your question, but I suspect your
//2.call the other method
line contains a nested subscription or a promise. If that's the case, of course your synchronous code will be run before your asynchronous code is run. JavaScript is a single-threaded environment, so you can never wait for other code to run.
Instead, use RxJS various operators to manage the order of your code for you. How you want to do that depends on what you're doing, though sadly call the other method isn't descriptive enough.
Assuming theOtherMethod and anotherFunction are actually strangely named observables, then you might do something like this:
this.http.get(apiUrl+"/actionName").pipe(
switchMap(result => {
if(result){
return theOtherMethod;
}
return of("There's no result")
}),
switchMap(otherMethodResult => anotherFunction)
).subscribe(anotherFunctionResult => {
/* Ignore result?*/
});

Passing pivot table data from laravel to VueJS component

Im adding VueJs Components to my Laravel App and im struggling with this question:
I have 2 models, Concessions and Brands, with a ManyToMany relationship like this:
Concession Model
public function brands()
{
return $this->belongsToMany('App\Brand');
}
Brand Model
public function concessions()
{
return $this->belongsToMany('App\Concession');
}
in the controller im fetching the data from all the Concessions and returning to the view like this:
public function concessions()
{
$concessions = Concession::all();
return view('MyConcessionsView', compact('concessions'));
}
on my Laravel View i can access to $concessions[0]->brands with no problem and everything is working fine.
So, im adding my Vue component, and i'm passing the prop $concessions to that component:
<concessions-map :con="{{ $concessions }}"></concessions-map>
On my Vue component im accepting the prop 'con' and i can display all the elements from that array. BUT i need access from each brand form each concession and when i try, for example, display con[0].brands[0] or even con[0].brands it gives me the error Cannot read property 'brands' of undefined".
I even pass this prop to State on Mounted() lifecycle method:
mounted() {
this.concessions = this.con;
}
and on Vue DevTools i have the array with 35 objects.. but i can access the property brands from each one.
Any advice?
Thanks (a lot) in advance!
$concessions = Concession::with('brands')
This is what you need.
when you execute
$concessions[0]->brands
You are making a new request to the database.
hi,
you can work with Axios is a better way so
-1 define a function in your concessions controller to get every concessions with its brands
public function getConcessions(){
$concessions= Concession::with('brands');
return $concessions;
}
-2 define a route to fetch this data in your web.php
for example
Route::get('/concessions/getData','ConcessionController#getConcessions');
-3 fetch data with axios from your vuejs component
loadConcessions(){
axios.get("/concessions/getData")
.then(({data}) => {
this.concessions = data.data
});
and do not forget to add this function in your mounted methode
mounted() {
this.loadConcessions()
}
-4 now you can display the data fetched in your vuejs component
in your case you do for example
{{concessions.brands}}
and even you can read it as a loop with v-for for example read it in a table
<tr v-for="concession in concessions.data" :key="concessions.id">
<td class="text-center">{{concession.brands}}</td>
</tr>
so for each concession, it will display its brands

NGXS State documentation

I'm new to NGXS and I'm trying to fully understand the docs so I can start using it knowing what I'm doing.
There is one thing I don't understand in this code snippet from here.
export class ZooState {
constructor(private animalService: AnimalService) {}
#Action(FeedAnimals)
feedAnimals(ctx: StateContext<ZooStateModel>, action: FeedAnimals) {
return this.animalService.feed(action.animalsToFeed).pipe(tap((animalsToFeedResult) => {
const state = ctx.getState();
ctx.setState({
...state,
feedAnimals: [
...state.feedAnimals,
animalsToFeedResult,
]
});
}));
}
}
Just below this code, it says:
You might notice I returned the Observable and just did a tap. If we
return the Observable, the framework will automatically subscribe to
it for us, so we don't have to deal with that ourselves. Additionally,
if we want the stores dispatch function to be able to complete only
once the operation is completed, we need to return that so it knows
that.
The framework will subscribe to this.animalService.feed, but why?
The action, FeedAnimals, uses the injected service, AnimalService to feed the animals passed in the action's payload. Presumably the service is operates asynchronously and returns an Observable. The value of that Observable is accessed via the tap function and is used to update the ZooState state context based on completing successfully.
In order to use NGXS specifically and Angular in general, you really have to understand RxJS... here's my goto doc page for it

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...

Resources