I'm trying to parse fetched data from firebase at Angular (Typescript).
My JSON data looks like this in firebase:
"customer" : {
"customerId1" : {
"documents" : {
"documentId1" : {
"documentName" : "file1.pdf",
"documentUrl" : "someUrl1"
},
"documentId2" : {
"documentName" : "file2.pdf",
"documentUrl" : "someUrl2"
}
},
"email" : "customet1#email.com",
"name" : "Customer 1",
},
"customerId2" : {
"documents" : {
"documentId3" : {
"documentName" : "file3.pdf",
"documentUrl" : "someUrl3"
},
"documentId4" : {
"documentName" : "file4.pdf",
"documentUrl" : "someUrl4"
}
},
"email" : "customer2#email.com",
"name" : "Customer 2",
}
}
As you can see, each customer has 3 properties, documents, email and name at the first level. At the second level, the document property has nested property documentName and documentUrl.
And for parsing customer data I used the following code.
customer.component.ts:
...
export class CustomerComponent implements OnInit {
customerObservable: Observable<any[]>;
customerList: Customer[];
constructor(db: AngularFireDatabase) {
// Listen to customer data
this.customerObservable.subscribe(data => {
this.customerList = data;
});
}
}
...
With this code I can fetch and iterate data with the ngFor directive at html with customerList variable. The question is, how can I achieve something like this?
// Variable that contains array of an array of customer's document array
documentList: Document[][];
// Or this
// Variable that holds customer's documents
customerDocuments: Document[];
// Where Document model looks like this
export class Document {
public documentName: string;
public documentUrl: string;
}
Thank you for the support.
Here is the service code:
import {Injectable} from '#angular/core';
import {AngularFireDatabase} from 'angularfire2/database';
//rxJS
import {Observable} from 'rxjs/Observable';
import 'rxjs/operator/switchMap';
#Injectable()
export class FirebaseService {
constructor(private angularFireDatabase: AngularFireDatabase) {}
onGetUserDocuments(customer: string) {
return this.angularFireDatabase.list(`customer/${customer}/documents`).valueChanges()
}
onGetUsersDocuments(){
return this.angularFireDatabase.list('customer').snapshotChanges()
.map((changes)=>{
return changes.map((data) => {
return data.payload.key
});
})
.switchMap((usersId: string[]) => {
return Observable.combineLatest( usersId.map((u)=>{
return this.onGetUserDocuments(u);
}))
})
}
}
Then in your component you can call:
import {Component, OnInit} from '#angular/core';
import {FirebaseService} from '../services/firebase.service';
#Component({
selector: 'new',
templateUrl: './new.component.html',
styleUrls: ['./new.component.css']
})
export class NewComponent implements OnInit {
constructor(private firebaseService: FirebaseService) {}
ngOnInit(){
this.firebaseService.onGetUsersDocuments().subscribe(data => console.log(data));
}
}
That will give you:
[Array(2), Array(2)]
0: Array(2)
0: {documentName: "file1.pdf", documentUrl: "someUrl1"}
1: {documentName: "file2.pdf", documentUrl: "someUrl2"}
1: Array(2)
0: {documentName: "file3.pdf", documentUrl: "someUrl3"}
1: {documentName: "file4.pdf", documentUrl: "someUrl4"}
So you can use it like so:
<div *ngFor="let docs of data">
<div *ngFor="let doc of docs">
<p>file name: {{doc.documentName}}</p>
<p>file url: {{doc.documentUrl}}</p>
</div>
</div>
Let me know if you will need any comments thought the code or will have any questions.
P.S. What you really have to now that nesting like this is not recommended by firebase, you should flaten you data. You can read about this here
new docs and here old docs
Related
I would like to parse a json file to use, and extract data.
I don't know why the data extracted from my code work only for my html, but is empty for my typescript code...
json file to parse :
[
{
"appleWatch": "generation_3",
"bracelets": ["model_1","model_2","model_3"]
},
{
"appleWatch": "generation_4",
"bracelets": ["model_1","model_4","model_5"]
}
]
Typescript of my component:
export class AppleKitComponent implements OnInit {
constructor(private httpService: HttpClient) {}
arrAppleWatch: AppleWatchModel[] = [];
selectedWatch: AppleWatchModel = null;
url = '../../assets/json/appleKit.json';
ngOnInit() {
this.arrAppleWatch = this.parseAppleWatchData();
console.log(this.arrAppleWatch.toString() + 'test');
}
parseAppleWatchData() {
this.httpService.get('../../assets/json/appleKit.json').subscribe(
data => {
this.arrAppleWatch = data as AppleWatchModel[]; // FILL THE ARRAY WITH DATA.
},
(err: HttpErrorResponse) => {
console.log(err.message);
}
);
return this.arrAppleWatch;
}
}
My appleWatch model :
export class AppleWatchModel {
constructor(
public watch: string,
public bracelets?: string[],
public bracelet?: string
) {
}
}
HTML:
{{arrAppleWatch |json }}
My log should output :
[ { "appleWatch": "generation_3", "bracelets": [ "model_1", "model_2", "model_3" ] }, { "appleWatch": "generation_4", "bracelets": [ "model_1", "model_4", "model_5" ] } ]
but it just prints an empty string.
My html work and show the array :
[ { "appleWatch": "generation_3", "bracelets": [ "model_1", "model_2", "model_3" ] }, { "appleWatch": "generation_4", "bracelets": [ "model_1", "model_4", "model_5" ] } ]
There are a few issues with your implementation.
The httpService.get call call would be an async call. So it won't give you the data instantly. But you're trying to access it instantly. Hence you're not getting it in the Component Class.
Give this a try:
import { Component } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
export interface AppleWatchModel {
watch: string;
bracelets?: string[];
bracelet?: string;
};
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private httpService: HttpClient) {
}
arrAppleWatch: AppleWatchModel[] = [];
selectedWatch: AppleWatchModel = null;
ngOnInit() {
this.parseAppleWatchData()
.subscribe(res => {
this.arrAppleWatch = res;
console.log('test: ', this.arrAppleWatch);
});
}
parseAppleWatchData() {
return this.httpService.get<AppleWatchModel[]>('/assets/appleKit.json');
}
}
Here, we're returning an Observable<AppleWatchModel[]> from parseAppleWatchData. So we can subscribe to it in the ngOnInit to get the actual data.
Here's a Working Sample StackBlitz for your ref.
Your output is empty because you don't take the asynchronous nature of http requests into account. parseAppleWatchData is returned with the original arrAppleWatch value (which is []) before the http response is received. If you add some logs you will see B comes before A. You can also remove the return value.
export class AppleKitComponent implements OnInit {
constructor(private httpService: HttpClient) {
}
arrAppleWatch: AppleWatchModel [] = [];
selectedWatch: AppleWatchModel = null;
url = '../../assets/json/appleKit.json';
ngOnInit() {
this.parseAppleWatchData();
log('B', this.arrAppleWatch);
}
parseAppleWatchData() {
this.httpService.get('../../assets/json/appleKit.json').subscribe(
data => {
this.arrAppleWatch = data as AppleWatchModel []; // FILL THE ARRAY WITH DATA.
console.log('A', data);
},
(err: HttpErrorResponse) => {
console.log(err.message);
}
);
}
I am using ag-grid license edition and want to bring record set to the grid using a service. I am using Ag-Grid gridoption property and initializing rowdata
in the constructor of the component.ts class. But the data is not rendering. No error either. Please have a look of my component.ts class.
Please note: the web api is returning the json data correctly and the service in Angular is tested sucessfully without Ag-Grid
import {Component, OnInit} from "#angular/core";
import {GridOptions} from "ag-grid";
import {RedComponentComponent} from "../red-component/red-component.component";
import { person } from "../Shared/models/person.model";
import {personservice} from '../service/person.service';
#Component({
selector: 'app-my-grid-application',
templateUrl: './my-grid-application.component.html'
})
export class MyGridApplicationComponent implements OnInit{
private gridOptions: GridOptions;
private persons: person[];
constructor(private personservice: personservice) {
this.gridOptions = <GridOptions>{};
this.gridOptions.columnDefs = [
{
headerName: "ID",
field: "PersonId",
width: 100
},
{
headerName: "Name",
field: "PersonNm",
cellRendererFramework: RedComponentComponent,
width: 100
},
{
headerName: "Email",
field: "PersonEmail",
cellRendererFramework: RedComponentComponent,
width: 100
}
];
this.gridOptions.rowData = this.persons
}
ngOnInit() {
this.personservice.findAll().subscribe(
persons => {
this.persons = persons
},
error => {
console.log(error);
}
)
}
}
Instead of providing columnDefs and rowData with gridOptions, try providing them directly like this.
<ag-grid-angular
[gridOptions]="gridOptions"
[rowData]="rowData"
[columnDefs]="columnDefs"
>
</ag-grid-angular>
Also, declare columnDefs and rowData as component variables, and then assign them after getting the response.
rows: any[] = [];
columnDefs: ColDef[];
constructor(private personservice: personservice) {
this.gridOptions = <GridOptions>{};
this.columnDefs = [
// your column definition goes here
];
}
ngOnInit() {
this.personservice.findAll().subscribe(
persons => {
this.rows = persons
},
error => console.log(error)
)
}
Let me know if you face any issue after this.
I'm trying to build an app with ionic that reads data from a local `.json' file and uses this data to fill a page. But I'm already struggling with importing the file into the page. What I currently have is:
import { Component } from "#angular/core";
interface Entry {
name: string,
telephone: string
}
interface EntryList {
entryList: Array<Entry>;
}
#Component({
selector: 'page-list',
templateUrl: 'list.html'
})
export class ListPage {
entryList: EntryList;
constructor() {
this.load_entries();
};
load_entries () {
this.entryList = JSON.parse(
// ?
)
};
}
The .json file contains entries like:
[
{"name": "Person A","telephone": "1234"},
{"name": "Person B","telephone": "12345"}
]
I don't know how to proceed from here on. What's the right way to get my data into the app?
Please try this:
constructor(public http: HttpClient) {
this.load_entries();
};
load_entries(filePath: string) { //filePath: 'assets/test.json'
this.http
.get(filePath)
.subscribe((data) => {
console.log(data);
});
}
Of course, you have to import HttpClient first.
import { HttpClient } from '#angular/common/http';
I have following JSON retrieved from Web service and I am able to get specific element in nested JSON by hard-coding the index in html. Below is the sample data. This is just a portion of entire JSON, I caught rest of them and they are all in same order.
Now, I would like to get "id" element in condition to Name. All names will be given, but index for Table where Names located are unknown. I need to be able to get "id" for all given names. How should proceed this?
{
"School": {
"Table": [
{
"Name": [ "Leo" ],
"id": [ "123" ],
"class": [ "A" ]
},
{
"Name": [ "Kelvin" ],
"id": [ "456" ],
"class": [ "B" ]
}
]
}
}
ngOnInit(): void {
this.appService.getData().subscribe(res =>
this.results = res,
error => console.log(error)
);
}
This might be what you're looking for:
School.Table
.filter(o => o.Name === 'condition')
.map(o => o.id)
For example:
<p *ngFor="let id of ids$ |async"> {{ id }} </p>
this.ids$ = this.service.getSchool()
.map((o: School) => o.Table
.filter(o => o.Name === 'condition')
.map(o => o.id))
it's already been said but use a data service. Once that has returned the JSON data it's much easier to transform it to exactly how you want it. I've done a bit to this recently. Here's an example of a data service that does exactly this. Once this.gearDataJSON = gearData; is populated you can easily process the data into something the easy to work with on the HTML side.
Component
import { Component, OnInit } from '#angular/core';
import { Subscription } from 'rxjs/Subscription';
import { Observable } from 'rxjs/Rx';
import { Injectable } from '#angular/core';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import 'rxjs/Rx';
import { GearDataService } from '../../services/geardata.service'
#Component({
selector: 'app-gears',
templateUrl: './gears.component.html',
styleUrls: ['./gears.component.css']
})
#Injectable()
export class GearsComponent implements OnInit {
constructor(public gearDataService : GearDataService) {}
gearDataJSON; // Holds a complete copy of gears.json data
manufacturerNames : string[]; // Bit of JSON that we want to chop off or process
ngOnInit()
{
this.gearDataService.getGearData().subscribe((gearData) => // Load in gear data and populate array for control and selection
{
this.gearDataJSON = gearData;
this.manufacturerNames = gearData.manufacturers;
});
}
DataService
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class GearDataService {
constructor(public http : Http) {}
getGearData()
{
return this.http.get("./assets/gears.json")
.map(res => res.json());
}
}
Suppose that your json was something like-
component.ts
school:any = {
Table:[
{
name: "Steve",
id:"123"
},
{
id:"456"
}
]
};
If you want the output html to look like you suggested you don't really need to match the name but can just use iteration.
component.html
<table>
<tr>
<ng-container *ngFor="let user of school.Table">
<td>{{user.name}}</td>
</ng-container>
</tr>
<tr>
<ng-container *ngFor="let user of school.Table">
<td>{{user.id}}</td>
</ng-container>
</tr>
</table>
Would that suffice?
I'm currently learning Angular 2 and have confused myself with how to output data returned from a service to my template.
My API Response :
{
"site": {
"success": true,
"title": "foo",
"description": "bar"
}
}
My Service :
import { Injectable } from '#angular/core';
import {HTTP_PROVIDERS, Http, Response, Headers, RequestOptions } from "#angular/http";
import { Observable } from 'rxjs/Rx';
#Injectable()
export class ContentService {
constructor(private _http:Http) {}
getContent() {
return this._http.get('http://localhost:8080/api/foobar-endpoint/')
.map((res:Response) => res.json())
}
}
My Component :
import { Component, OnInit } from '#angular/core';
import { ContentService } from "../../services/content/content.service";
const template = require('./home.jade');
const styles = require('./home.sass');
#Component({
selector: 'home',
templateUrl: template,
styleUrls: [styles]
})
export class HomeComponent implements OnInit {
public foo = {}
constructor(private _contentService: ContentService) {}
ngOnInit() {
this.getContent();
}
getContent() {
this._contentService.getContent()
.subscribe(
data => {this.foo = data},
err => { console.log(err) },
() => console.log()
);
}
}
My Template :
pre
p {{ foo.site.title }}
If I place {{ foo | json }} in my template I can see the returned values in a JSON format, but when I try and output a single value, such as title I get undefined errors.
How can I access the values being returned?
I think the only thing you are missing here is the ?. Basically the problem is that when the components instantiates your foo property has no site param so angular throws the error.
So what you can do is either this:
{{foo.site?.title}}
Or this:
<p *ngIf="foo.site">{{foo.site.title}}</p>
This way angular won't try to bind the title before there is a site.