Share FireStoreCollection to serveral views - google-cloud-firestore

im having an ionic project with a firestore backend using 'angularfire2/firestore'.
To share a collection between views i created a firebaseservice provider:
interface if_friend {
name: string;
email: string;
id?: any;
}
friendsCollection: AngularFirestoreCollection<if_friend>;
friends$: Observable<if_friend[]>;
constructor(public afStore: AngularFirestore) {
console.log('Hello FirebaseServiceProvider Provider');
this.friendsCollection = afStore.collection('/testmail#googlemail.com/Friends/friends/');
this.friends$ = this.friendsCollection.valueChanges();
}
getFriends(){
return this.friends$;
}
On the constructor of the views im getting the Obserable like this:
public friends$: Observable<if_friend[]>;
constructor(public navCtrl: NavController, public firebaseService: FirebaseServiceProvider) {
this.friends$ = this.firebaseService.getFriends();
}
and using it in the html file with the async pipe:
<ion-list>
<ion-item *ngFor="let friend of friends$ | async">
This works well for the first view im entering.
The problem im facing is, that the data will not appear on the secound view (im doing the same there).
I think i have to unsubscribe from the observable, if im leaving the first view or maybe i need to rewrite the data into a separte array.
Can anyone please tell me how to share the observable correctly between two views?
Thanks and best regards!

i needed to return the collection from the service and use the valueChanges in the views.
Seems like the async pipe now works correctly.
Best regards

Related

get last view Ionic 4

good morning. Does anyone know the import of the NavController? I am looking at the docs of Ionic 4 docs and it says there is a method of getPrevious() however when I do:
import { NavController } from ‘#Ionic/angular’;
contructor (private navCtrl: NavController){}
private func(): void{
this.navCtrl.getPrevious();
}
I get an error saying property getPrevious does not exist on type NavController however in the docs it says that it exists. anyone aware of the proper import since it isn't documented?
previously you were able to do this with Ionic 2-3 but not Ionic 4.
side note* method last() doesn't exist either.
What do you want to do? If you want to go back, than you could use the ion-back-button.
<ion-buttons slot="start">
<ion-back-button color="light"></ion-back-button>
</ion-buttons>
Also you can use the NavController like this to get forward:
constructor(private navCtrl: NavController) { }
GotoImpressum() {
this.navCtrl.navigateForward('impressum');
}

Get and show live data from cordova plugin in ionic 4 page

im trying to code a simple step counter app, im using this plugin (https://github.com/leecrossley/cordova-plugin-pedometer) in android to get the step counter data, so i want to get this data and show it on my ionic page (front end view), i tryed to use ngZone to refresh the object while the method subscribe to the startPedometerUpdates function, but it takes many seconds to work and sometimes stucks few seconds and then start to show the counter again...
this is my code:
steps: any = []
constructor(private ngZone: NgZone, private pedometer: Pedometer) { }
ngOnInit() {
}
getSteps(){
this.pedometer.startPedometerUpdates()
.subscribe((data: IPedometerData) => {
this.ngZone.run(() => this.steps.push(data))
});
}
my html is simple for debug the data:
<ion-content>
{{this.steps | json}}
<ion-button (click)="getSteps()">Show steps</ion-button>
</ion-content>
so i want to show the data in "real time" with something simple as posible...
thanks in advance
you can't push data in subscribe even if you using NgZone bcoz push is a function ngZone doesn't work in the function
steps: any = []
constructor(private ngZone: NgZone, private pedometer: Pedometer) {
this.getSteps();
setInterval(()=>{
console.log('read in a sec')
},1000)
}
ngOnInit() {
}
getSteps(){
this.pedometer.startPedometerUpdates()
.subscribe((data: IPedometerData) => {
this.ngZone.run(() => this.steps.push(data))
});
}

Ionic / Firebase - error splice is not a function (reorderArray)

I have beat my head for a couple days...trying to use the (ionItemReorder)="reorderItems($event)" to reorder a list. I have a list of songs I'm getting from FireBase. When I fire the reOrderItems click event I get an error: TypeError: array.splice is not a function at reorderArray
I assume it's probably something very simple in the way I'm defining "songs". I have tried several different ways...but at this point I'm just grasping at straws.
Any suggestions would be greatly appreciated. Thank you! ER
Typescript:
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams, reorderArray } from 'ionic-angular';
import { AngularFireModule} from 'angularfire2';
import { AngularFireDatabase } from 'angularfire2/database';
#IonicPage()
#Component({
selector: 'page-songs',
templateUrl: 'songs.html',
})
export class SongsPage {
//songs: any = {};
//songs = {};
//songs = [];
//songs: any = [];
songs: any;
btnName: any = 'Reorder';
flag: any = false;
constructor(
public navCtrl: NavController,
public navParams: NavParams,
public afd: AngularFireDatabase
)
{
this.songs = this.afd.list('/songs/').valueChanges();
}
//Button in navbar to toggle reordering the list of songs
actionBtn(){
if (this.btnName == 'Reorder') {
this.btnName = 'Done';
this.flag = true;
}
else{
this.btnName = 'Reorder';
this.flag = false;
}
};
reorderItems(indexes){
//let element = this.songs[indexes.from];
//this.songs.splice(indexes.from, 1);
//this.songs.splice(indexes.to, 0, element);
this.songs = reorderArray(this.songs, indexes);
};
showChords(song){
this.navCtrl.push('ChordsPage', song)
}
}
HTML:
<ion-header>
<ion-navbar>
<ion-title>Songlist</ion-title>
<ion-buttons end>
<button ion-button small clear (click)="actionBtn();">
{{btnName}}
</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list reorder="{{flag}}" (ionItemReorder)="reorderItems($event)">
<ion-item-sliding *ngFor="let song of songs | async ; let i = index">
<ion-item>
<h2>{{i+1}}. {{ song.Title}}</h2>
<p>{{song.Artist}}</p>
</ion-item>
<ion-item-options side="right">
<button ion-button (click)="showChords(song)">Chords</button>
</ion-item-options>
<ion-item-options side="left">
<button ion-button color="danger" (click)="removeSong(song)">Delete
<ion-icon name="trash"></ion-icon>
</button>
</ion-item-options>
</ion-item-sliding>
</ion-list>
</ion-content>
Subscribe to the Observable and map the items to be pushed into the songs array. Also, use the lifecycle hook ionViewDidLoad instead of the constructor for doing things at initialization.
import { AngularFireDatabase } from 'angularfire2/database';
import { ISubscription } from 'rxjs/Subscription';
import { OnDestroy } from '#angular/core';
import { Observable } from 'rxjs/Observable';
...
export class ... implements OnDestroy {
songslist: Observable<any[]>;
subscription: ISubscription;
songs: any[];
constructor(...
ionViewDidLoad() {
this.songslist = this.afd.list<Item>('songs').valueChanges();
this.subscription = songslist.subscribe(items => {
return items.map(item => { this.songs.push(item); });
});
}
...
reorderItems(...
...
ngOnDestroy {
this.subscription.unsubscribe(); // Make sure to unsubscribe, don't leave them open.
}
}
Trying to accomplish something very similar - except with Firestore instead of the RealTime Database - had me wanting to bang my head against the wall, but finally got it working so hopefully it's helpful to you or others even though some things will be slightly different.
The array.splice is not a function error comes up when you are trying to perform the function on something that isn't an array, in this case the observable which for reasons beyond my understanding seems to be returned as a single object.
To get things working I declared each of these variables separately:
photoCollectionRef:AngularFirestoreCollection < PhotoSlide>;
photo$: Observable < PhotoSlide [] >;
photo = {} as PhotoSlide;
photoArray: any=[];
Then set each value as follows
this.photoCollectionRef = this.afs.collection('posts').doc(this.postId).collection('photos/', ref => ref.orderBy('index'));
this.photo$ = this.photoCollectionRef.valueChanges();
this.photo$.take(1).subscribe((pictures) => {
pictures.forEach(p=>{
return this.photoArray.push({index:p.index, photoURL: p.photoURL, id:p.id })
})
});
In the template file *ngFor loops over photo of photoArray (without an async pipe) and the reorderItem function called from the item group works perfectly with standard single line syntax from the ionic docs.
Then to update the indexes in the Firestore Collection, I call the below function from a button that's visible once the list order has been modified:
saveNewOrder(){
this.photoArray.forEach((element, i) => {
this.photoCollectionRef.doc(element.id).update({
index: i
});
});
}
I should note that I also always use the .createId() method and then .doc(newId).set({id:newId etc}) when adding new documents rather than just .add because I find it a lot less annoying to do that than be stuck using .snapshotChanges and map function rather than just .valueChanges() - which is why the above functions work with the syntax shown
Also if you don't use the rxjs take(1) operator before the subscription it all looks fine at first but after the saveNewOrder function's executed the list repeats several times
I am late getting back to this but here is my attempt at explaining how I got this working.
**Caveat - I am a hobbyist coder so I apologize in advance if I'm explaining using wrong terms or if I just understand it wrong..the code works:).
The goal is to get a list from Firebase using the NEW version of Firebase and pull it into an array so that I can present it to the screen and then use the reorderArray function to keep the array indexes in order after moving items around in a list. So if I have an array of songs...I'll call it songList (think no Firebase yet) and assume this is my HTML:
<ion-list reorder="true" (ionItemReorder)="reorderItems($event)">
<ion-item *ngFor="let song of songList; let i = index">
{{i+1}}. {{song.title}} - {{song.artist}}
</ion-item>
</ion-list>
Then I have the standard function to reorder the array:
reorderItems(indexes) {
this.songList = reorderArray(this.songList, indexes);
};
Add the 'reorderArray' to the import to enable this:
import { NavController, AlertController, reorderArray } from 'ionic-angular';
After looking into this a little I think the reorderArray function does a few .splice commands to get the indexes moved around in the array.
So fast forward to replacing my array with a Firebase list. See all the above code from the 1st post...all that works to get the list to show up on the HTML page. But as soon as the reorderArray is fired I get "splice" errors thrown. As it turns out the Firebase list at this point is an object and the reorderArray expects an array. I got the below code from a Youtube video:
//Set up the songList array
var x = this.afDatabase.list('/songs');
x.snapshotChanges().subscribe(item => {
this.songList = [];
item.forEach(element => {
var y = element.payload.toJSON();
y["fbKey"] = element.key;
this.songList.push(y);
})
})
I will try to explain as best I can what this is doing. I set x to be a ref to my Firebase list /songs. I invoke snapShotChanges() to get my list values and the key. I then subscribe to the list to walk through the items. I declare setList as an array. I iterate over the list of items and I guess 'payload' is a special property that gets all the object data?? I think I cast all that object data into an array. I add a new field to the list I think so I can get back at the .key value from a field? I then push all that data into an array.
Again I'm not sure how all this magic works but it does. Now I have an array in songList that holds all my data...and now I can use the reorderArray function to keep the indexes straight on the client side after a reorder.
But...new problem. There is no client side representation of that index value out in the Firebase list.
The rest of this code is a little hazy as when things started working I was all over the map and adding lots of stuff to see it work. Right now I'm having Ionic Serve issues and can't get this running right now without deploying it up to Firebase or Ionic View...so I have to go by memory.
So here is what my final HTML looks like:
<ion-list reorder="true" (ionItemReorder)="reorderItems($event)">
<ion-item *ngFor="let song of songList | orderBy: 'sortOrder'; let i = index">
{{i+1}}. {{song.title}} - {{song.artist}} ({{song.sortOrder}})
</ion-item>
</ion-list>
The only real difference here is that I have an orderBy on a new field called sortOrder.
Here's how all this works:
reorderItems(indexes) {
var luTitle = '';
var luArtist = '';
this.songList = reorderArray(this.songList, indexes);
this.songList.forEach( song => {
this.afDatabase.database.ref('songs/' + song.fbKey + '/sortOrder').set(this.songList.indexOf(song));
this.afDatabase.database.ref('songs/' + song.fbKey)
.on('value', function(snapshot) {
luTitle = snapshot.child('title').val();
luArtist = snapshot.child('artist').val();
})
console.log("Index: " + this.songList.indexOf(song));
// console.log("Title: " + song.title);
console.log("LU Title: " + luTitle);
console.log("LU Artist: " + luArtist);
console.log("FB Key: " + song.fbKey);
console.log("Sort Order: " + song.sortOrder);
})
};
A lot of this is just logging stuff to the console but the real work is this:
The first thing I do is run the reorderArray over the this.songList and it gets all the indexes in the right place on the client side. Then I iterate over all the items in that array and I create a reference to the FB list using the song.fbKey that we set when we converted the initial FB list to an array. Then I .set a sortOrder field for that song equal the the current index of that song as it exists on the client side at that moment in time. Everything else I think after that is me logging stuff to the console to look at values. The LU (Look Up) stuff was just me figuring out how to get a value back in from Firebase.
Now the orderBy in the ngFor immediately orders everything by the sortOrder field that basically comes in real time from FB. I can't remember but I think if the list is brand new from FB and there is no sortOrder field yet it defaults to sorting by the key...which is fine...the first time reorder is fired all the sortOrders get set.
I'm sure there are some bugs I will discover when I get back to this...but it's working code as of now.
Thanks for reading if you made it this far.
ER

Go back to the previous state

I am trying various solutions from Google but all of them seems to be for Ionic 1 and other versions of Ionic and Angular.
HTML
<button class="edit" (click)="goBackToEnhancementPage();">Edit</button>
On button click I want to goto to the previous state in the history
TypeScript
This is the current state
export class BookingConfirmationPage {
//Some properties
constructor(public navCtrl: NavController, public navParams: NavParams ) {
//Some codes
}
goBackToEnhancementPage(){
canGoBack();
}
}
Previous State
export class BookingEnhancementPage {
//Some code
constructor(public navCtrl: NavController, public navParams: NavParams, public loadingCtrl: LoadingController, private formBuilder: FormBuilder ) {
//This is previous state
}
}
This doesn't work. Please advise what am I doing wrong?
I'm guessing from your question you are trying to use navController to go back to your previous state, aka "back" function.
The way ionic navigation works is like a stack, new pages will be pushed to the top of the stack via "push" via pages will be removed from the top of the stack via "pop"
To go back to your previous state, u can use :
this.navCtrl.pop();
But before that make sure you have push your previous page into navController or you have setRoot your "BookingConfirmationPage" page.
You might want to read up on : https://ionicframework.com/docs/v2/api/navigation/NavController/
If you want your previous details in BookingEnhancementPage to be filled with your user's previously entered data, you might want to use a combination of localstorage and onPageBeforeEnter/onPageWillEnter to populate the fields.

Rxjs workflow for MongoDB Document References

I am developing an application on Ionic2/rc0. I got a ReplaySubject on a singlenton service that keeps the current user consistent across the whole app. It all works fine, I can subscribe to it and get a User object as easy as
this._user.Current.subscribe(user=>{ console.log(user)});
The User object looks like this
User {
confirmed:true
devices:["57f65werwe343bn8843f7h","7yr3243h5429hf2hjd"]
friends:["t245y53h65346htyh","356ytrer75dfhg43we56df"]
email:"francescoaferraro#gmail.com"
id:"57f6525e926bbc7615fc5c5c"
notification:false
password="$2a$04$.Fk/8eMj18ZrkfurbbdP4uT3yOs7Lb9db74GkNfgtABVY.ez2Q0I."
picture:"https://api.cescoferraro.xyz/kitty"
role:"master"
username:"cesco"
}
As you can see my backend is using MongoDB with One-to-Many Relationships with Document References as described here.
I have created a devices tab where I want to display all data about those user devices, but I need to call this._devices.info for each one of current.devices and concat the result back to TrueDevices
#Component({
template: `
<ion-header>
<ion-navbar>
<ion-title>Tabs</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<h2>Device:list</h2>
<h2 *ngFor="let item of devices | async">{{item}}</h2>
<button ion-button (click)="readDevice()">Read Random Device</button>
</ion-content>
`
})
export class DeviceComponent {
devices: Observable<string[]>;
TrueDevices: Observable<Device[]>;
constructor(public _user: UserService, public _device: DeviceService) {
this._user.Current.subscribe(user=>{ this.devices = Observable.of(user.devices)});
// Get current User
// call this._devices.info for each one of current.devices
// concat the result back to TrueDevices
this._user.Current
.subscribe((result) => { console.log(result) });
}
readDevice(){
console.log(this.devices);
this._device.info(this.devices.value[0]).subscribe(data=>console.log(data))
}
}
I will need to repeat the same procedure to the friends tab and so on. I am pretty sure there are a couple operators that would do the magic, but I am fairly new to rxjs and not familiar with all of them. Whats the right approach?
this._user.Current
.switchMap(user => Observable.from(user.devices)) // after this line, you have an Observable<string>
.mergeMap(device => this._device.info(device)) // each device will be mapped to another observable(or stream), and all the streams will be merged together
.toArray() // wait for all the streams to complete and reduce all the results into an array.
.subscribe(array => console.log(array));
or go to the gitter room:
https://gitter.im/Reactive-Extensions/RxJS