Data fetch from firebase - firebase

import { Component } from '#angular/core';
import { AngularFireDatabase, AngularFireList } from 'angularfire2/database';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
courses;
constructor(db: AngularFireDatabase) {
db.list('/courses').valueChanges()
.subscribe(courses => {
this.courses = courses;
console.log(this.courses);
});
}
}
Expected Behavior:
[Object, Object, Object, Object]
Actual Behavior:
["course 1", "course 2", {...}, {...}]
The above code returns an array but I expected an array of objects. Also the type returned but the valueChanges() is Observable<{}[]>. I want to know is it the normal behavior of valueChanges() i.e., returning an Observable as object along with an array. Please help me out and tell me where I am wrong in my code. I want an array of objects as an end result with this code.

to answer your question: yes, this is the normal behavior.
Since Firebase is a realtime database, it wants to keep track of any path you've queried and see if it changed ( no matter from where the change came from, you or your user )
I'm not sure what you're trying to do with the objects returned afterwards, but since the code is very minor, i would expect you just need to display it in some list with its values ...
So I would write something like this, where you can still access each item in the Array like it's an object:
# course.model.ts
export class courseModel {
title: string = "";
price: string = "";
author: string = "";
}
# app.component.ts
import { Component } from '#angular/core';
import { AngularFireDatabase, AngularFireList } from 'angularfire2/database';
import { Observable } from 'rxjs';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
courses : Observable<courseModel[]> = new courseModel();
constructor(db: AngularFireDatabase) {
this.courses = db.list('/courses').valueChanges();
}
}
# app.html
<p *ngFor="let course of courses | async">
{{ course.title }}
{{ course.author }}
{{ course.price }}
</p>

From https://github.com/angular/angularfire2/blob/master/docs/rtdb/lists.md
valueChanges()
What is it? - Returns an Observable of data as a synchronized array of JSON objects. All Snapshot metadata is stripped and just the method provides only the data.
So instead of getting [{ key: value }] you are getting [value].
If you want the full object, including key, use snapshotChanges().
You'll need to import map from rxjs/operators for this as well.
import { map } from "rxjs/operators";
db.list('/courses').snapshotChanges()
.pipe(map(snapshots => {
return snapshots.map(snapshot => {
let course = {};
course[snapshot.key] = snapshot.payload.val();
return course;
});
})).subscribe(courses => {
this.courses = courses;
console.log(this.courses);
});
This will return an array of users with the structure [{ key: value }]. If you want it formatted differently, like [{ "key": key, "value": value }], let me know and I'll update my answer.

It's a simple change and your code is work as it is
And it's worked for me
First you need to use data type any[] of courses
You need to use subscribe method instead of directly store valueChanges() value into variable
Because of angular older version support directly valueChanges() method to get values but in latest angular version you need to use subscribe method and assign value like this.
courses : any[];
constructor(db:AngularFireDatabase){
db.list('/courses').valueChanges().subscribe(courses=>
{
this.courses=courses;
console.log(this.courses);
});

Related

ERROR undefined, HttpErrorResponse {headers: HttpHeaders, status: 404, statusText: "Not Found"

i have a little worry, if some can just help me, i use a laravel 5.8 api, which i create, when i retrieve the parameter data mtr from posteman its walk, but when i do the same thing with this url << url = 'http://127.0.0.1:8000/api/studentbyid/45' >> it works its problem because the 45 is the mtr, whereas according to the logic that I try to put in place and that it is the user who will introduce the mtr, then display...
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Candidate } from '../models/candidate';
#Component({
selector: 'app-candidate',
templateUrl: './candidate.page.html',
styleUrls: ['./candidate.page.scss'],
})
export class CandidatePage implements OnInit {
mtr: '45';
// tslint:disable-next-line: new-parens
data: any;
url = 'http://127.0.0.1:8000/api/studentbyid/';
constructor( private httpClient: HttpClient) { }
ionViewWillEnter(mtr: string) {
return this.httpClient.get(this.url + '/' + this.mtr)
.subscribe(
data => {
this.data = JSON.stringify(data);
console.log(this.data);
},
error => {
console.log(error);
});
}
ngOnInit() {}
getbyID( mtr: string) {}
}
here is the result in the console when I place it directly
enter image description here
and that's when I try to retrieve the word from a userenter image description here
if there are some who can just help me, and I notice that there is no answer in the form of object
Your variable mtr is indeed undefined.
mtr: '45';
console.log(mtr);
// undefined
You need to set the variable to the '45' string instead you typed the mtrvariable as type '45'.
mtr: string = '45';
console.log(mtr);
// '45'

Property 'map' does not exist on type '{}' rxjs6

Please view the following code:
import { Component } from '#angular/core';
import { AngularFirestore, AngularFirestoreCollection } from 'angularfire2/firestore';
import { Observable} from 'rxjs';
import { map } from 'rxjs/operators';
import { StudentsInfo } from '../studentInfo';
#Component({
selector: 'app-delete-student',
templateUrl: './delete-student.component.html',
styleUrls: ['./delete-student.component.css']
})
export class DeleteStudentComponent {
public itemsCollection: AngularFirestoreCollection<StudentsInfo>;
public items: Observable<StudentsInfo[]>;
constructor(private db: AngularFirestore) {
this.itemsCollection = this.db.collection<StudentsInfo>('/Stud_Info');
//this.items = this.itemsCollection.valueChanges();
// .snapshotChanges() returns a DocumentChangeAction[], which contains
// a lot of information about "what happened" with each change. If you want to
// get the data and the id use the map operator.
this.items = this.itemsCollection.snapshotChanges().pipe(
map(changes => changes.map(a => {
const data = a.payload.doc.data() as StudentsInfo;
const id = a.payload.doc.id;
return { id, ...data}
})
));
}
// Deleting a Student from Firestore
public deleteStudent(docId:string) {
this.itemsCollection.doc(docId).delete();
}
}
Problem: Property 'map' does not exist on type '{}'.
I have followed this documentation of angularfire2.
I have done everything according to the documentation needed and the according to the latest rxjs6 release.
The final angular application Runs fine but while doing ng build --prod --aot this error occurs.

retrieve data from firebase in ionic 3 and angular 5

Pls I need help in retrieving user data from firebase with AngularFireObject on logging in.
I have been able to save data to firebase but having issues retrieving it. Pls someone help out.
Many thanks in advance.
Ok, first you've to configure your AngularFireModule (I think you already do that). AngularFireModule.initializeApp(FIREBASE_CONFIG).
So, is a good way to create a model/service to handle your entities requests with firebase, something like this:
Model:
export interface Cutom {
key?: string,
name: string,
quantity: number,
price: number
}
Service:
import { Injectable } from '#angular/core';
import { AngularFireDatabase } from 'angularfire2/database';
import { Custom } from './../../models/custom.model';
#Injectable()
export class CustomService {
private customListRef;
constructor(private db: AngularFireDatabase) {
this.customListRef = this.db.list<Custom>('your-endpoint-at-firebase');
}
getCustomList() {
return this.customListRef;
}
}
In Component you will use your recently created service:
...
export class HomePage implements OnInit {
// remember to import the correct model
customList$: Observable<Custom[]>
constructor(
public navCtrl: NavController,
private customService: CustomListService
) {}
ngOnInit() {
this.customList$ = this.customService
.getCustomList()
.valueChanges();
}
or if you need the metadata too (like the ID):
ngOnInit() {
this.customList$ = this.customService
.getCustomList()
.snapshotChanges()
.pipe(
map(items => { // this needs to be imported with: import { map } from 'rxjs/operators';
return items.map(a => {
const data = a.payload.val();
const key = a.payload.key;
return {key, ...data};
});
}));
}
...
And the finally at your template:
<ion-item *ngFor="let item of customList$ | async">
{{item.name}}
</ion-item>
I hope it helps.

Return firebase values from a service to a component angular 6

I'm creating an application with angular 6 and firebase using angularfire2, I chose to use the firestore where I have a collection called pages like in the image:
basically I created a service - "PagesService" where I have a function that returns the data of the page that I sent. I'm trying to use getPage to return the values to my component, and assign them to the form, nothing else I tried worked, only returns an "observable" that I can not work, does anyone have an idea of what I can do?
Full code, service:
import { Injectable } from '#angular/core';
import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
#Injectable()
export class PagesService {
private pagesCollection: AngularFirestoreCollection<any>;
private page: AngularFirestoreDocument<any>;
constructor(private afs: AngularFirestore) {
this.pagesCollection = afs.collection('pages');
}
getPage(pageName: string) {
return this.afs.doc<any>('pages/${pageName}').valueChanges();
}
addPages(pageName: string, pageForm: any) {
this.pagesCollection.doc(pageName).set(pageForm.value);
}
}
My component:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup } from '#angular/forms';
import { Observable } from 'rxjs';
import { PagesService } from '../../services/pages.service';
#Component({
selector: 'app-quem-somos',
templateUrl: './quem-somos.component.html',
styleUrls: ['./quem-somos.component.scss']
})
export class QuemSomosComponent implements OnInit {
pageForm: FormGroup;
pageName: string = "wo-we-are";
page: any;
constructor(private pagesService: PagesService, private fb: FormBuilder) { }
ngOnInit() {
this.page = this.pagesService.getPage(this.pageName);
console.log(this.page);
this.pageForm = this.fb.group({
title: '',
content: ''
});
}
save() {
this.pagesService.addPages(this.pageName, this.pageForm);
}
}
obs: Sorry my english
If I have understand you right, When you say "Observable that I cannot work" is mean that you cannot access his data when you are trying to assign its values in the form?
In this case (I assume that your service is working as expected), just subscribe to it and populate the form after your values are ready to use. for example:
ngOnInit() {
this.pagesService.getPage(this.pageName).subscribe(v => {
// Here your data is ready, so you can send it to a function and populate the form as you need.
this.populateForm(v);
});
// Here I just construct the FormGroup, so your application can rendered.
this.pageForm = this.fb.group({
title: '',
content: ''
});
}
And add this function to do the task:
populateForm = (data) => {
console.log(data); // Just log it in console, and see if its the data that you seek for
}
Instead of console.log() you can populate your form or do what ever you need to.
Good Luck !
--EDIT--
I just noticed now, In your service:
getPage(pageName: string) {
return this.afs.doc<any>('pages/${pageName}').valueChanges();
}
You call the doc with ' ' instead of ``, so In fact, you are not using Template Strings. So your call is wrong and not fetch with the right path.
Change it to:
return this.afs.doc<any>(`pages/${pageName}`).valueChanges();

Updating a template with a component input

Preface: I'm new to Meteor, Angular, and Typescript, so there is a very real possibility of an XY problem somewhere in here.
I'm working on a simple project management app using Meteor and Angular 2 (using the angular2-meteor package) where the structure (for now) consists of projects which have events. One view is a list of projects. Clicking on a project shows a modal of the project's details, including a list of the project's events. So, three components: ProjectList, ProjectDetails, and ProjectEventsList. ProjectDetails uses a Session variable to know which project to show, and that works. However, the list of events in the modal doesn't update after it is created for the first project clicked on.
ProjectEventsList.ts
import {Component, View} from 'angular2/core';
import {MeteorComponent} from 'angular2-meteor';
import {ProjectEvents} from 'collections/ProjectEvents';
#Component({
selector: 'projectEventsList',
inputs: ['projectId']
})
#View({
templateUrl: '/client/projectEventsList/projectEventsList.html'
})
export class ProjectEventsList extends MeteorComponent {
projectEvents: Mongo.Cursor<ProjectEvent>;
projectId: string;
constructor() {
super();
this.subscribe('projectEvents', this.projectId, () => {
this.autorun(() => {
this.projectEvents = ProjectEvents.find({projectId: this.projectId});
}, true);
});
}
}
As I understand it (though I may be way off here), I'm having difficulty getting autorun to, well, automatically run. I've tried putting a getter and setter on projectId and it does get updated when I click on a project, but the code inside autorun doesn't run after the first click. Things I've tried:
Switching the nesting of subscribe() and autorun().
Adding/removing the autobind argument to both subscribe() and autorun(). I don't really understand what that's supposed to be doing.
Moving the subscribe code to a setter on projectId:
private _projectId: string = '';
get projectId() {
return this._projectId;
}
set projectId(id: string) {
this._projectId = id;
this.subscribe('projectEvents', this._projectId, () => {
this.projectEvents = ProjectEvents.find({projectId: this._projectId});
}, true);
}
When I do this the list stops displaying any items.
If this all seems like it should work, I'll create a small test case to post, but I am hoping that something in here will be obviously wrong to those who know. Thanks!
this.subscribe() and this.autorun() doesn't seem to be part of the Angular component class. If this is an external library you might need to explicitly run it in an Angular zone for change detection to work:
constructor(private zone: NgZone) {
this.subscribe('projectEvents', this.projectId, () => {
this.autorun(() => {
zone.run(() => {
this.projectEvents = ProjectEvents.find({projectId: this.projectId});
});
}, true);
});
}
If you want to subscribe to events fired from the component itself use host-binding
#Component(
{selector: 'some-selector',
host: {'projectEvents': 'projectsEventHandler($event)'}
export class SomeComponent {
projectsEventHandler(event) {
// do something
}
}
I eventually got the setter method working, as shown below. It feels clunky, so I'm hoping there's a cleaner way to do this, but the below is working for me now (i.e., the list of events is updated when the parent component (ProjectList) sends a new projectId to the input.
ProjectEventsList.ts
import {Component, View} from 'angular2/core';
import {MeteorComponent} from 'angular2-meteor';
import {ProjectEvents} from 'collections/ProjectEvents';
#Component({
selector: 'projectEventsList',
inputs: ['projectId']
})
#View({
templateUrl: '/client/projectEventsList/projectEventsList.html'
})
export class ProjectEventsList extends MeteorComponent {
projectEvents: Mongo.Cursor<ProjectEvent>;
set projectId(id: string) {
this._projectId = id;
this.projectEventsSub = this.subscribe('projectEvents', this._projectId, () => {
this.projectEvents = ProjectEvents.find({projectId: this._projectId}, {sort: { startDate: 1 }});
}, true);
}
get projectId() {
return this._projectId;
}
constructor() {
super();
this.subscribe('projectEvents', this.projectId, () => {
this.projectEvents = ProjectEvents.find({projectId: this.projectId});
}, true);
}
}

Resources