ionic async ngFor data - asynchronous

UPDATE ON BOTTOM
I am trying to show data in an *ngFor that i'm getting from an object that is getting retrieved asynchronously from ionic storage. At the moment i am getting a blank screen.
I have tried multiple things like using async pipes in different manners.
Does anybody know the right way?
Here is my storage service method that is getting called:
public getFlow(flowId:number){
return this.storage.get(FLOWS_KEY).then((flows:Map<number,Flow>)=>{
return flows.get(flowId);
});
}
this returns a Promise<Flow>
this is my component code:
import { Component, OnInit } from '#angular/core';
import { ModalController } from 'ionic-angular';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { Flow } from '../../model/Flow';
import { FlowService } from '../../model/services/flowService';
import {CreateTaskPage} from '../create-task/create-task'
import { Task } from '../../model/Task';
#IonicPage()
#Component({
selector: 'page-flow',
templateUrl: 'flow.html',
})
export class FlowPage {
flow;
constructor(public navCtrl: NavController, public navParams: NavParams,private flowService:FlowService,public modalCtrl: ModalController) {
this.flow = this.flowService.getFlow(Number(this.navParams.get("flowId")))
}
ngOnInit(): void {
}
ionViewDidLoad() {
console.log('ionViewDidLoad FlowPage');
}
createTask(){
const modal = this.modalCtrl.create(CreateTaskPage,{flowId:this.flow.flowId});
modal.present();
}
swipe(e,task:Task){
if(e.direction == 2){
console.log("panUp");
task.column--;
}
if(e.direction == 4){
console.log("panDown");
task.column++;
}
}
}
My html:
<ion-content padding>
<div *ngIf="(flow | async)">
<div *ngFor="let col of flow.columns;index as i">
<h2>{{col}}</h2>
<div *ngFor="let task of flow.getTasksFromCol(i)">
<ion-card (swipe)="swipe($event,task)">
<ion-item>
<h2>{{task}}</h2>
<button ion-button item-end clear icon-end>
<ion-icon name='more'></ion-icon>
</button>
<p>{{task}}</p>
</ion-item>
</ion-card>
</div>
</div>
</div>
<ion-fab right bottom>
<button ion-fab color="light"><ion-icon name="arrow-dropleft"></ion-icon></button>
<ion-fab-list side="left">
<button (click)="createTask()" ion-fab><ion-icon name="add-circle"></ion-icon></button>
<button ion-fab><ion-icon name="create"></ion-icon></button>
</ion-fab-list>
</ion-fab>
</ion-content>
Thanks for helping.
UPDATE:
I found one big mistake in my component it now looks like this:
flow:Flow;
constructor(public navCtrl: NavController, public navParams: NavParams,private flowService:FlowService,public modalCtrl: ModalController) {
this.flowService.getFlow(Number(this.navParams.get("flowId"))).then(flow =>{
this.flow = flow;
})
}
i also updated my html but it still isn't working: i now get error:
ERROR TypeError: _co.flow.getTasksFromCol is not a function
at Object.eval [as updateDirectives]
This is weird because this method exists in my Flow Model:
import { Task } from "./Task";
export class Flow {
//PK for 1-n relation with task
flowId:number;
projectName:string;
columns:string[];
tasks: Map<number,Task>;
constructor(flowId:number,projectName:string,columns:string[],tasks:Map<number,Task>){
this.flowId = flowId;
this.projectName = projectName;
this.columns = columns;
this.tasks = tasks;
}
public getTasks(){
return Array.from(this.tasks.values())
}
public getTasksFromCol(colNumber:number){
var tasks = new Array<Task>();
for(let task of Array.from(this.tasks.values())){
if(task.column == colNumber){
tasks.push(task)
}
}
return tasks;
}
}
UPDATE2
i now added this to my service
public getTasksFromCol(flowId:number,colNumber:number){
return this.storage.get(FLOWS_KEY).then((flows:Map<number,Flow>)=>{
var flow:Flow = flows.get(flowId);
var tasks = new Array<Task>();
for(let task of Array.from(flow.tasks.values())){
if(task.column == colNumber){
tasks.push(task)
}
}
return tasks;
});
}
do i just call this in my html page? i'm kinda stuck

I found your mistake.
You have created one variable.
flow:Flow;
You have assigned value to that variable.
this.flow = flow;
Now you need to understand that variable has contains some value related to what you have assigned. So you can't access flow.getTasksFromCol()
Thats the reason you have faced this error.
ERROR TypeError: _co.flow.getTasksFromCol is not a function at Object.eval [as updateDirectives]
Solution:-
Just move this getTasksFromCol() method to service and apply html like following,
I hope it's working. Let try this once and let me know if any error.

Related

How can I share data between components through a service?

I am having trouble sharing information between components. What I am trying to do is, show in the home.component.html data that is generated in the about.component.ts.
The operation would be:
By means of a button in the home.component.html, a function in the home.component.ts is called, this function calls a function of the about-service.ts, which collects the data from the about.component.html that obtains the about.component.ts data.
This is my home.component.html:
<button mat-menu-item (click)="goToAbout()">
<mat-icon svgIcon="logoBN"></mat-icon>
Acerca
</button>
This is my home.component.ts:
import { AboutComponent } from '../about/about.component';
export class HomeComponent {
public aboutInfo: AboutService;
goToAbout() {
.subscribe(emitData =>
}
}
From the goToAbout() function of the home.component.ts I need to get the data from the aboutBuild() function of the about.component.ts:
This is my about.component.ts:
import { AboutService } from '';
export class AboutComponent {
ngOnInit() {
}
aboutBuild() {
......code.........
}
}
This is my about.component.html:
<header class="title-color" fxFlex>Build Info</header>
I have created a function in the service to try to communicate both components.
about-service.ts:
observer = new Subject();
public subscriber$ = this.observer.asObservable();
emitData(aboutBuild) {
this.observer.next(aboutBuild);
}
But I can't access the aboutBuild() function of the about.component.ts, what do I need to include in the service to communicate the two components?
The AboutService is fine you have one public subscriber and a function to trigger event.
Your goToAbout() in home.component.ts :
export class HomeComponent {
constructor(public aboutService: AboutService) {}
goToAbout() {
let data = {key: 'value'};
this.aboutService.emitData(data);
}
}
Then your about.component.ts:
export class AboutComponent {
constructor(public aboutService: AboutService) {}
ngOnInit() {
this.aboutService.subscriber$.subscribe(data => {
this.aboutBuild(data);
});
}
aboutBuild(data) {
console.log(data)
}
}

Having issues trying to get downloadURL for multiple images from firebase storage using angular fire2

I have created a collection that holds the news title, news img, news desc, etc.. I also created the upload method that works fine , uploads the file to storage and saves the path to the collection.
Issues when trying to retrieve the image using getDownloadUrl();
Here are the files
news.component.html
<div class="container-fluid">
<div *ngFor="let item of thenews | async">
<p>
{{ item.newsTitle }} {{ item.newsDesc }}
</p>
<div class="col-md-4">
<img [src]="getImgUrl(item.newsImg)" class="img-responsive">
</div>
</div>
</div>
<div class="container-fluid">
<button mat-raised-button (click)="onLogout()">Logout</button>
</div>
News.component.ts
import { tap } from 'rxjs/operators';
import { AngularFireStorage } from 'angularfire2/storage';
import { AngularFirestoreCollection, AngularFirestoreDocument, AngularFirestore } from 'angularfire2/firestore';
import { Component, OnInit, Injectable } from '#angular/core';
import { AuthService } from './../auth.service';
import { DataService } from '../services/data.service';
import { News } from './../models/newsModel';
import { Observable } from 'rxjs';
#Component({
selector: 'app-news',
templateUrl: './news.component.html',
styleUrls: ['./news.component.css']
})
#Injectable()
export class NewsComponent implements OnInit {
news = {} as News;
newsCol: AngularFirestoreCollection<News>;
thenews: Observable<News[]>;
imgUrl: Observable<any>;
theData: News[] = [];
constructor(public authservice: AuthService, public dataservice: DataService, public afs: AngularFirestore,
private storage: AngularFireStorage) { }
ngOnInit() {
this.newsCol = this.afs.collection<News>('news');
this.thenews = this.newsCol.valueChanges();
}
getImgUrl(img) {
return this.storage.ref(img).getDownloadURL();
}
addNews(news) {
this.dataservice.addNews(news);
}
onLogout() {
this.authservice.onLogout();
}
}
When this is served it runs into an infinite loop. and the site goes hung.
Any help?
getDownloadURL() is a observable method (async function), so you have to wait for the observable to return the value i.e. the url or null if the image is not available
for example
imageUrl: Observable<string | null>;
const ref = this.storage.ref('users/davideast.jpg');
this.imageUrl= ref.getDownloadURL();
in template
<img [src]="profileUrl | async" />
Please refer this link from github
Hope this helps

invalidpipeargument: '[object object']: error showing

So I am relatively new to Ionic and Firebase, and I am trying to display user data that already exists in Firebase.
Here is the error that I am getting:
invalidpipeargument: '[object object']: error showing.
I do know what the issue is after looking at other questions similar to mine. It seems I cannot assign the async function as database.object does not return an observable. So I have tried to add .valueChanges(); to make it an observable, but I believe this is clashing with the angularfireobject as that is already assigned to the variable profile.data so this would not work.
I have also tried this:
// get (): AngularFireObject<any[]>{
//return this.afDatabase.list`/profile/${data.uid}`;
//}
If you point me in the right direction that would be great.
Here is my code:
.ts file
import { Component } from '#angular/core';
import { NavController, ToastController } from 'ionic-angular';
import { AngularFireAuth } from 'angularfire2/auth';
import { AngularFireDatabase, AngularFireObject} from 'angularfire2/database';
import { Profile } from '../../model/profile';
import { DEFAULT_INTERPOLATION_CONFIG } from '#angular/compiler';
#Component({
selector: 'page-about',
templateUrl: 'about.html'
})
export class AboutPage {
profileData: AngularFireObject<Profile>
// get (): AngularFireObject<any[]>{
//return this.afDatabase.list`/profile/${data.uid}`;
//}
constructor(private afAuth:AngularFireAuth, private afDatabase: AngularFireDatabase,
public navCtrl: NavController, private toast: ToastController) {
}
ionViewDidLoad() {
this.afAuth.authState.take(1).subscribe(data =>{
if (data && data.email && data.uid){
this.toast.create({
message: `Welcome to MatchMyFighter ${data.email}`,
duration: 3000,
}).present();
// this.profileData = this.afDatabase.object(`profile/${data.uid}`)
this.profileData = this.afDatabase.object(`/profile/${data.uid}`).valueChanges();;
}
else{
this.toast.create({
message:`could not autenticate`,
duration:3000
}).present;
}
})
}
}
.html
<p>
Username: {{(profileData | async)?.username}}
</p>
The call to valueChanges() converts the AngularFireObject<Profile> to an observable.
You have to change the type to Observable<Profile> like so:
profileData: Observable<Profile>;
constructor(private db: AngularFireDatabase) { } // other stuff emitted ...
ionViewDidLoad() {
// ...
this.profileData = this.db.object<Profile>(`/profile/${data.uid}`).valueChanges();
// ...
}

How to display user data in modal

I am currently working on a project where there are two types of users: Drivers and Passengers. I am using Ionic Framework and Firebase Database ad Authentication. The Passengers are able to send requests to the drivers and the drivers are able to see these requests. I am currently working on the driver home page and creating a platform that lists the passenger's/customer's data and requests. I really want to user ionic modals to keep the information organized but I am unsure on how to go about this. I went ahead and attached the HTML and Javascript files for the Driver Home page and the modal page. Any help is extremely appreciated. This is my first time using Firebase and I am extremely lost.
My Database Structure
Driver Home Page
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams, ModalController, Modal } from 'ionic-angular';
import { DriverHomePage } from '../driver-home/driver-home';
import { CustomerModalPage } from '../customer-modal/customer-modal';
import { AngularFireAuth } from 'angularfire2/auth';
import { AngularFireDatabase, FirebaseListObservable } from 'angularfire2/database';
import * as firebase from 'firebase';
/**
* Generated class for the DriverHomePage page.
*
* See https://ionicframework.com/docs/components/#navigation for more info on
* Ionic pages and navigation.
*/
#IonicPage()
#Component({
selector: 'page-driver-home',
templateUrl: 'driver-home.html'
})
export class DriverHomePage {
const custRef = firebase.database().ref().child('User').orderByChild('type').equalTo('customer').once('value', function(snapshot) {
snapshot.forEach(function(child) {
var newCustomer = child.val();
var firstName=child.val().firstName;
var lastName=child.val().lastName;
var phone=child.val().phone;
var location = child.val().location;
var numOfPassengers = child.val().numOfPassengers;
var payment = child.val().payment;
});
});
constructor(private modalCtrl: ModalController, private afAuth: AngularFireAuth, private afDatabase: AngularFireDatabase, public navCtrl: NavController, public navParams: NavParams) {
}
openModal(){
//const custData
/* const custModal: Modal = this.modalCtrl.create('CustomerModalPage');
custModal.present();
custModal.onDidDismiss();*/
}
ionViewDidLoad() {
console.log('ionViewDidLoad DriverHomePage');
}
}
<!--
Generated template for the DriverHomePage page.
See http://ionicframework.com/docs/components/#navigation for more info on
Ionic pages and navigation.
-->
<ion-header>
<ion-navbar>
<button ion-button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>driver-home</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<button (click)"openModal()">{{ custRef.firstName }} {{ custRef.lastName }}</button>
</ion-content>
Modal
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular';
import { AngularFire, FirebaseObjectObservable } from 'angularfire2';
import { AngularFireDatabase } from 'angularfire2/database';
import * as firebase from 'firebase';
/**
* Generated class for the CustomerModalPage page.
*
* See https://ionicframework.com/docs/components/#navigation for more info on
* Ionic pages and navigation.
*/
#IonicPage()
#Component({
selector: 'page-customer-modal',
templateUrl: 'customer-modal.html',
})
export class CustomerModalPage {
const custRef = firebase.database().ref().child(`User`).orderByChild('type').equalTo('customer').on('value', function(snapshot) {
snapshot.forEach(function(child) {
var datas = child.val();
var firstName=child.val().firstName;
var lastName=child.val().lastName;
var phone=child.val().phone;
var location = child.val().location;
var numOfPassengers = child.val().numOfPassengers;
var payment = child.val().payment;
});
});
constructor(private fb: AngularFire, private viewCtrl: ViewController, public navCtrl: NavController, public navParams: NavParams) {
}
acceptRequest(){
}
closeModal() {
this.viewCtrl.dismiss();
}
/*ionViewDidLoad() {
console.log('ionViewDidLoad CustomerModalPage');
}*/
ionViewWillLoad(){
//const data = this.navParams.get('data');
console.log();
}
}
<!--
Generated template for the CustomerModalPage page.
See http://ionicframework.com/docs/components/#navigation for more info on
Ionic pages and navigation.
-->
<ion-header>
<ion-navbar>
<ion-title>CustomerModalPage</ion-title>
<ion-buttons end>
<button ion-button (click)="closeModal()">Close</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content padding>
<p>{{ custRef.firstName }}</p>
<p>{{ custRef.lastName }}</p>
<p>{{ custRef.phone }}</p>
<p>{{ custRef.location }}</p>
<p>{{ custRef.numOfPassengers }}</p>
<p>{{ custRef.payment }}</p>
<button (click)="acceptRequest()">Accept</button>
</ion-content>
I see you are using firebase library to access your database and I can also see you are importing angularfire2 too. I strongly recommend you use angularfire2 and avoid using firebase library as this is way easier. You can read more in the angularfire2 docs.
For your question, the best query would be something like this:
this.db.list('/user', ref => ref.orderByChild('type').equalTo('customer'))
.subscribe(users => {
users.forEach(user => {
//Do stuff with each user here.
})
});
Also, remember to inject the correct reference in your component. Your constructor should look like this:
constructor(db: AngularFireDatabase) { }

Ionic 2 Angularfire 2 retrieve collection from firebase

I'm developing a mobile app using Ionic 2, Angularfire 2 and Firebase, but I'm stuck, this is my Firebase data structure:
I need to create a list from carrito, but at the same time I need to get the precio field and link it to another collection - precios, and get with the key value (as shown in the image above), the fields related (opcion & precio).
The code : list.ts
import { Component } from '#angular/core';
import { NavController, NavParams } from 'ionic-angular';
import {AngularFire, FirebaseListObservable} from 'angularfire2';
#Component({
selector: 'page-lista',
templateUrl: 'lista.html',
})
export class Lista {
public restaName:any;
serviceData: string;
carrito: FirebaseListObservable<any>;
precios: FirebaseListObservable<any>;
constructor(public navCtrl: NavController, public navParams:
NavParams, af: AngularFire, private shareService: ShareService) {
this.serviceData = shareService.getRestaName();
this.restaName = this.serviceData
this.carrito = af.database.list('/carrito', {
query: {
orderByChild: 'restaname',
equalTo: this.restaName
}
});
this.precios = af.database.list('/precios', {
query: {
orderByKey: true,
equalTo: this.precio ???? ---> HERE BEGIN MY DOUBTS
}
});
}
Here is part of lista.html
<ion-content padding>
<ion-list>
<ion-item *ngFor="let item of carrito | async ">
<p> {{item.prodname}} - {{item.cantidad}}</p>
</ion-item>
<ion-item *ngFor="let precio of precios | async "> ??? HOW CAN I GET THE FIELDS precio and opcion
<p>${{precio.precio}}</p>
</ion-item>
</ion-list>
</ion-content>
Any tips or comments will be appreciated !
You could use an Array of FirebaseObjectObservable instead of
precios: FirebaseListObservable<any>;
public precios :Array<FirebaseObjectObservable<any>>
this.carrito.subscribe(carrito => carrito.forEach(singleCarrito=>
precios.push(af.database.object('/precios/'+singleCarrito.precio))

Resources