In ionic how to read and display the information from firestore - firebase

In ionic, I want to get and display information from firestore from its specific fields like Name there, but the problem is that it is displaying other documents' field Names too.
ngOnInit(){
this.authService.readc().subscribe(data => {
this.datas = data.map(e => {
return {
Name: e.payload.doc.data()['Name'],
};
})
console.log(this.datas);
});
}
}
name() {
var counter = this.firestore.doc(`info/${this.authService.userDetails().uid}`);
counter.set({
Name: this.Name
})
}
In authService
readc() {
return this.firestore.collection('info').snapshotChanges();
}

Something like this maybe?
home.page.ts
export class HomePage {
names: any;
constructor(auth: AuthService) {
auth.readc().subscribe(data => {
this.names = data.map(person => ({ name: person.name }));
});
}
}
auth.service.ts
export class AuthService {
people: any;
constructor(db: AngularFirestore) {
this.people = db.collection('people').valueChanges();
}
readc(){
return this.people;
}
}
Check out the angularfire docs for more detailed information on using Collections and Documents in Firebase.
Hope this helps.

Related

How to extend EntityCollectionReducerMethods NgRx Data

I need to parse the data field before adding it to the store.
I was hoping to parse the data field from the override getAll().
This code doesn't work can someone explain why ?
export interface Alert {
id: string;
data: any;
}
const entityMetadata: EntityMetadataMap = {
Alert: {}
};
#Injectable({providedIn: 'root'})
export class AlertService extends EntityCollectionServiceBase<Alert> {
constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
super('Alert', serviceElementsFactory);
}
getAll(options?: EntityActionOptions): Observable<Alert[]> {
return super.getAll(options)
.pipe(
map(alerts => {
alerts = alerts.map((alert: any) => ({...alert, data: JSON.parse(alert.data)}));
return alerts;
})
);
}

Expected Argument Error for .doc() when called on Firestore Collection

I want to create a new user document in my Cloud Firestore database whenever a new user logs in. Each doc should have a unique id and I want a "uid" property for each user to match the unique auto-generated id for the doc. At first, I just always ran an update on the user, but I figured it could be helpful to separate my create and update logic. As you can see I haven't worked out how to query if a user exists, but I figured I should test the createUser function before continuing.
Anyway, while I was testing my createUser function I ran into a compilation error.
ERROR in src/app/services/auth.service.ts(64,22): error TS2554:
Expected 1 arguments, but got 0.
UPDATE:
When I try to run the function from localhost after compilation I get this error in the console.
Function CollectionReference.doc() requires its first argument to be
of type string, but it was: undefined
Here is my proposed solution:
import { Injectable } from '#angular/core';
import { User } from './../models/user.model';
import { PermissionsService } from './permissions.service';
import { auth } from 'firebase/app';
import { AngularFireAuth } from 'angularfire2/auth';
import {
AngularFirestore,
AngularFirestoreDocument,
AngularFirestoreCollection,
} from 'angularfire2/firestore';
import { Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
#Injectable({
providedIn: 'root',
})
export class AuthService {
usersCollection: AngularFirestoreCollection<User> = null;
user: Observable<User>;
constructor(
private afAuth: AngularFireAuth,
private db: AngularFirestore,
private permissionsService: PermissionsService,
) {
this.usersCollection = db.collection('users');
this.user = this.afAuth.authState.pipe(
switchMap((user) => {
if (user) {
return this.db
.doc<User>(`users/${user.uid}`)
.valueChanges();
} else {
return of(null);
}
}),
);
}
loginGoogle() {
const provider = new auth.GoogleAuthProvider();
return this.oAuthLogin(provider);
}
loginFacebook() {
const provider = new auth.FacebookAuthProvider();
return this.oAuthLogin(provider);
}
loginTwitter() {
const provider = new auth.TwitterAuthProvider();
return this.oAuthLogin(provider);
}
oAuthLogin(provider) {
return this.afAuth.auth.signInWithPopup(provider).then((credential) => {
//if(the user exists already)
//this.updateUserData(credential.user);
//else
this.createUser();
});
}
createUser() {
const newUserRef = this.usersCollection.doc<User>(); // Error here
let newUser: User;
this.user.subscribe((userData) => {
newUser = {
uid: newUserRef.id,
email: userData.email,
photoURL: userData.photoURL,
displayName: userData.displayName,
roles: {
member: true,
},
permissions: this.permissionsService.memberPermissions;
};
});
newUserRef
.set(newUser)
.then(() => {
console.log('created user');
})
.catch((err) => {
console.log('Error adding user: ' + err);
});
}
updateUserData(user) {
const userRef: AngularFirestoreDocument<any> = this.db.doc(
`users/${user.uid}`,
);
const userPermissions = this.addPermissions(userRef);
console.log(userPermissions); // This works
const data: User = {
uid: user.uid,
email: user.email,
photoURL: user.photoURL,
displayName: user.displayName,
roles: {
member: true,
}, // I need to make sure this keeps current user roles
permissions: userPermissions,
};
console.log(data); // This works
userRef
.set(data)
.then(() => {
console.log('Success: Data for userDoc overwritten');
})
.catch((err) => {
console.error('Error writing to userDoc: ' + err);
});
}
addPermissions(userRef) {
const tempPermissions = [];
userRef.valueChanges().subscribe((userdata) => {
if (userdata.roles.reader === true) {
tempPermissions.push(this.permissionsService.memberPermissions);
}
if (userdata.roles.author === true) {
tempPermissions.push(this.permissionsService.authorPermissions);
}
if (userdata.roles.admin === true) {
tempPermissions.push(this.permissionsService.adminPermissions);
}
});
return tempPermissions;
}
checkPermissions(permission: string) {
if (!this.user) {
return false;
} else {
this.user.subscribe((data) => {
for (const p of data.permissions) {
if (p === permission) {
return true;
}
}
return false;
});
}
}
logout() {
this.afAuth.auth.signOut();
this.user = null;
}
}
I checked the documentation on the .doc() function and it should work fine with 0 arguments. It should be returning an empty doc reference. However, it keeps throwing the error saying it expects 1 argument. Any idea why this isn't working?

SQLite in Ionic 3 getting data null after navigate to another page

Please help me. I've made Database Provider to connect with SQLite in ionic 3. When i want to get the data rows it's always getting null but when I check data length it has 1
This is my DatabaseProvider
import { Injectable } from '#angular/core';
import { Platform } from 'ionic-angular';
import { SQLite, SQLiteObject } from '#ionic-native/sqlite';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Storage } from '#ionic/storage';
#Injectable()
export class DatabaseProvider {
private db: SQLiteObject;
private databaseReady: BehaviorSubject<boolean>;
constructor(private storage: Storage, private sqlite: SQLite, private platform: Platform) {
this.databaseReady = new BehaviorSubject(false);
this.platform.ready().then(() => {
this.sqlite.create({
name: 'takia.db',
location: 'default'
}).then((database: SQLiteObject) => {
this.db = database;
this.storage.get('database_filled').then(val => {
if (val) {
this.databaseReady.next(true);
} else {
this.initDB();
}
});
}).catch(e => { console.log(e); });
});
}
initDB(){
this.db.executeSql('CREATE TABLE IF NOT EXISTS users(user_id INTEGER PRIMARY KEY, username TEXT, email TEXT, password TEXT)', {})
.then(res => {
this.db.executeSql('INSERT INTO users VALUES(NULL,?,?,?,?)',['admin','admin.#email.com','password'])
.then(res => {
this.databaseReady.next(true);
this.storage.set('database_filled', true);
}).catch(e => {
console.log(e);
});
}).catch(e => {
console.log(e)
});
this.db.executeSql('CREATE TABLE IF NOT EXISTS questions(user_id INTEGER PRIMARY KEY, username TEXT, email TEXT, password TEXT)', {})
.then(res => {
this.db.executeSql('INSERT INTO users VALUES(NULL,?,?,?,?)',['admin','admin.tazkiaiibs.sch.id','bismillah'])
.then(res => {
this.databaseReady.next(true);
this.storage.set('database_filled', true);
}).catch(e => {
console.log(e);
});
}).catch(e => {
console.log(e)
});
}
getUser(){
return this.db.executeSql('SELECT user_id, username, email, password, COUNT(*) total FROM users', [])
.then(res => {
let users = [];
if (res.rows.length > 0) {
for (var i = 0; i < res.rows.length; i++) {
let row = res.rows.item(i);
users.push(row);
}
}
return users;
}, err => {
console.log('Error: ', err);
return [];
});
}
getDatabaseState() {
return this.databaseReady.asObservable();
}
}
and this is my code in component
constructor(public navCtrl: NavController,
public navParams: NavParams,
private dbProvider: DatabaseProvider,
private toastCtrl: ToastController) {
this.loadData();
}
loadData(){
this.dbProvider.getUser().then(data=>{
if(data.length>0){
this.id = data[0].id;
this.old_password = data[0].password;
this.username = data[0].username;
this.email = data[0].email;
let toast = this.toastCtrl.create({
message: 'ID '+data[0].username,
duration: 3000
});
toast.present();
}else{
let toast = this.toastCtrl.create({
message: 'No data'+data.length,
duration: 3000
});
toast.present();
}
});
}
When i run loadData function the length is not 0 but the data is null.
The comment under the question seemed to help, so I form it as an answer to hopefully collect some reputation points.
The problem seems to be a misformed SQL-query. The count(*) part makes the query return at least one row. Therefore the other fields might be NULL, when queried before data is added to the database.
The function "getUser" is called before the promise callback is executed.

NGRX 4: How to combine data from multiple sources

Question on how to combine reducers / data in app state:
Example of Data (Firebase):
{
"artists" : {
"168dgYui7ExaU612eooDF1" : {
"name" : "Brand New",
"id" : "168dgYui7ExaU612eooDF1",
}
},
"genres" : {
"popPunk" : {
"name" : "pop punk"
},
"screamo" : {
"name" : "screamo"
}
},
"genresPerArtist" : {
"168dgYui7ExaU612eooDF1" : {
"popPunk" : true,
"screamo" : true
}
}
}
App State:
export type State = { app: AppState };
export type AppState = { genres: IGenre[], artistGenres: IGenre[], artists: any[], currentArtist: any };
export const initialState: State = {
app: {
artists: [],
currentArtist: null,
genres: [],
artistGenres: []
}
}
Reducer:
export function appReducer(state: AppState, action: GenreActions.Actions): AppState {
// TODO: add reducers for Fetch Artist and Fetch Artists
switch (action.type) {
case GenreActions.FETCH_GENRES: {
return { ...state };
}
case GenreActions.FETCH_GENRES_SUCCESS: {
return { ...state, genres: action.payload };
}
case GenreActions.FETCH_ARTIST_GENRES: {
return { ...state };
}
case GenreActions.FETCH_ARTIST_GENRES_SUCCESS: {
return { ...state, artistGenres: action.payload };
}
default: {
return state;
}
}
}
Effects:
#Effect()
FetchGenres$: Observable<Action> = this.actions$
.ofType(appActions.FETCH_GENRES)
.switchMap(() => {
return this.firebaseProvider.genres
.map(payload => new appActions.FetchGenresSuccess(payload))
.catch(() => of(new appActions.FetchGenresSuccess([])));
});
// TODO: See if I should be using store's genres here, or if that should be combined elsewhere
#Effect()
FetchArtistGenres$: Observable<Action> = this.actions$
.ofType(appActions.FETCH_ARTIST_GENRES)
.map((action: appActions.FetchArtistGenres) => { return action.payload })
.switchMap((artistId: string) => {
return this.firebaseProvider.genresByArtist(artistId)
.withLatestFrom(this.store.select(state => state.app.genres))
.map((results) => {
let artistGenres = results[0];
let genres = results[1];
return genres.filter(genre => {
let result = artistGenres.findIndex(g => g.$key === genre.$key);
return (result ? true : false);
});
})
.map(payload => new appActions.FetchArtistGenresSuccess(payload));
});
Firebase Provider:
#Injectable()
export class FirebaseProvider {
genres: FirebaseListObservable<IGenre[]>;
constructor(
private db: AngularFireDatabase
) {
this.genres = this.db.list('/genres');
}
genresByArtist(artistId: string): Observable<IDictionary[]> {
return this.db.list(`/genresPerArtist/${artistId}`);
}
}
Artist Page:
My question is how are you supposed to combine different slices of the data in NGRX4?
Example: I have an Artist Detail page. In that page, I want to display the Artist info, along with the Artist's genres. Elsewhere, I might have a Artist List page where I display multiple artists and their genres. Where do I fetch and merge all of that relevant information in my app? In the reducer? In the effect (how FetchArtistGenres$ is currently doing it)?
Other concerns: I don't think I want to have the entire "genresPerArtist" or "artists" node in my app state, because it will eventually become very large and I'm not sure that would be performant (if I'm wrong here, and that's the way to go, let me know). Instead, I'd fetch the specific Artists genre node when I need it (/genresPerArtist/${artistId})

Passing data correctly with angularfire2 / ionic2

I have a simple structure in my Database:
The app logic here: I create a list with some data with the function to delete each list item separately.
I´m using the angularefire2 plugin for database communication. The code to get data looks like this in component:
// Set variables
currentUserID: any;
visits: any[] = [];
selectedVisit: any;
constructor(public navCtrl: NavController, public navParams: NavParams, private dbAction: DbActionsProvider, private afDatabase: AngularFireDatabase) {
// Build Current User ID
this.currentUserID = this.dbAction.currentUserID().subscribe(data => {
this.currentUserID = data.uid;
});
}
ngOnInit() {
// Get data
this.afDatabase.object('data/users/' + this.currentUserID + '/visits')
.snapshotChanges().map(action => {
const data = action.payload.toJSON();
return data;
})
.subscribe(result => {
Object.keys(result).map(key => {
this.visits.push({ 'key': key, 'data':result[key]
});
}); console.log(this.visits)
})
}
The code in my view:
<ion-item-sliding *ngFor="let visit of visits | orderBy:'date' : false" (ionDrag)="onSelectedVisit(visit)">
<ion-item>
<ion-icon ios="ios-man" md="md-man" item-start></ion-icon>
<strong>{{ !visit.data.name == '' ? visit.data.name : 'Unbekannt' }}</strong>
<p>Musterstraße 8, 66130 Saarbrücken</p>
</ion-item>
<ion-item-options side="right">
<button ion-button>Bearbeiten</button>
<button ion-button color="danger" (click)="deleteVisit()">Löschen</button>
</ion-item-options>
<ion-input [(ngModel)]="visit.id"></ion-input>
</ion-item-sliding>
Ok..now I want that the user can delete items. For this I need access to the key reference ($key in firebase, but not works.....)...so I had to build my own object with this key field in the top. Not a pretty solution...do you have another idea?
The problem:
If the user swipe an item to see the Delete-Option, I pass data with (ionDrag)="onSelectedVisit(visit). My code in component for this function:
onSelectedVisit(visit) {
this.selectedVisit = visit.key;
console.log(this.selectedVisit);
}
deleteVisit() {
this.afDatabase.list('data/users/' + this.currentUserID + '/visits').remove(this.selectedVisit);
this.navCtrl.setRoot(VisitsPage);
}
If I not navigate back to VisitsPage (same page) I´ll see duplicates in my list because of the own builded object before.....so I need a more elegant solution..
Found a pretty solution:
export class AppComponent {
itemsRef: AngularFireList<any>;
items: Observable<any[]>;
constructor(db: AngularFireDatabase) {
this.itemsRef = db.list('messages');
// Use snapshotChanges().map() to store the key
this.items = this.itemsRef.snapshotChanges().map(changes => {
return changes.map(c => ({ key: c.payload.key, ...c.payload.val() }));
});
}
addItem(newName: string) {
this.itemsRef.push({ text: newName });
}
updateItem(key: string, newText: string) {
this.itemsRef.update(key, { text: newText });
}
deleteItem(key: string) {
this.itemsRef.remove(key);
}
deleteEverything() {
this.itemsRef.remove();
}
}
Reference: Github - Angularfire2 Docs

Resources