Is there a way to print a firestore object? While working with AngularFire i'm attempting to print to html the name of the document in the firestore. My firestore has a parent "nforms" which has two children as documents "pdf","img", inside pdf i have one pdf called "norcal-letterhead.pdf", inside img i have one img called "git-img.jpg".
This is how I am pulling the data form the firestore.
fptr: Observable<any[]>;
fileData: DocumentReference[];
fileCollection: DocumentReference;
filePtr: Observable<any>;
constructor(firestore: AngularFirestore, firestorage: AngularFireStorage) {
this.filePtr = firestore.collection('nforms').valueChanges();
this.filePtr.subscribe(
data => {
this.fileData = data
console.log(this.fileData)
this.fileData.map(r => this.fileCollection = r)
console.log(this.fileCollection)
}
);
}
This is how I am printing the data from the pull.
<tr *ngFor="let data of filePtr|async">
<td *ngIf="fileCollection; let a">{{a}} or {{data}}</td></tr>
I'm getting back this: [object Object] or [object Object]
This is the response that i'm getting from the console. As you can see i'm able to pull what looks like an Array of 2 with objects of type DocumentReference.
I'd like to share some more insigh on this post for future reference to whomever it could help. While this isn't the answer to printing the firestore objects that were created from using the console 'upload' button, this is an answer to printing the same objects except this time i created them through the sdk '.upload'.
Define the object FireStore Item:
export interface fstoreitem{
form_id: string;
form_name: string;
form_path: string;
}
Define the firestore reference to a collection nforms:
constructor(private firestore: AngularFirestore){
this.fitemCollection = firestore.collection<fstoreitem>('nforms/');
this.fitem = this.fitemCollection.valueChanges();
}
Define the routine to upload a firestore item:
addStoreItem(form_name: string, form_path: string) {
const form_id = this.firestore.createId();
const form_item: fstoreitem = { form_id, form_name, form_path };
this.fitemCollection.doc(form_id).set(form_item);
}
Display the objects in the view:
<tr *ngFor="let data of fitem | async">
<td>{{data.form_name}}</td>
<td>download</td>
</tr>
Related
I'm trying to pass data into a modal. The modal is a separate component alltogether.
I'm able to console.log the bsModalRef property in the constructor, but for some reason, I can't console.log the bsModalRef.content property.
Here is my code in the page I'm launching the modal from...
<button class="btn btn-primary"
type="button"
(click)="openDeleteModal(result.id)">
Edit
</button>
Here is the code for that openDeleteModal() method...
public openDeleteModal(id: number): void {
this.selectedCase = this.listOfSearchResults.filter(result => result.id == id);
const initialState: ModalOptions = {
initialState: {
list: [
this.selectedCase
],
title: 'Edit Case',
whatYouWantToDelete: 'this error'
}
};
this.bsModalRef = this.modalService.show(DeleteModalComponent, initialState);
}
Now, for the code in the modal itself, I'm importing BsModalRef like this...
import { BsModalRef } from 'ngx-bootstrap/modal';
Here are the properties I set...
title?: string;
whatYouWantToDelete?: string;
list?: any[];
initialState: any;
and this is what I have for the constructor...
constructor(
public bsModalRef: BsModalRef,
private http: HttpClient) {
this.list = this.list;
console.log("this.list: ", this.list);
console.log("this.bsModalRef in constructor: ", this.bsModalRef);
console.log("this.bsModalRef.content in constructor: ", this.bsModalRef.content);
}
this is the screenshot of what I'm seeing in the console.log...
And this is the content part of the BsModalRef object...
My question is, how do I access that data in the list property from the constructor? That object has properties that I need to populate a form I have in the modal.
Stated differently...
this line of code...
this.bsModalRef = this.modalService.show(DeleteModalComponent, initialState);
opens the modal and passes in the data as initialState. How do I access the data that I'm passing in through the initialState object from within the modal itself?
Here is a screenshot that shows I can see the data in the NgOnInit, but the issue is I can't get that data to show up in the modal html file.
Here is the html in the delete component file where I'm trying to display the list.
<p>Are you sure you want to delete {{ this.whatYouWantToDelete }}?</p>
<p>id selected: {{ this.products }}</p>
<div *ngFor="let user of this.list; index as i">
Case Id: {{user.caseId}}
</div>
There are 2 key notes here:
Objects logged in the Javascript console are "live", so expanding them shows the current contents of the object. Content.list was empty when you first called console.log(). Then data was updated and hence on console too. See issue
In order to see real data I prefer this:
console.log(JSON.parse(JSON.stringify(this.bsModalRef)));
Seems there is issue with bsModalRef. You can access content via:
setTimeout(() => { console.log(this.bsModalRef.content), 0});
But it is a workaound. You should report this to ngx-bootstrap repository.
My question is, how do I access that data in the list property from the constructor? That object has properties that I need to populate a form I have in the modal.
The proper way to access data is within ngOnInit lifecycle hook method .
title, whatYouWantToDelete and list data would be present within ngOnInit; you don't need to access it via bsModalRef instance.
Try this:
ngOnInit() {
console.log(this.list);
console.log(this.whatYouWantToDelete);
console.log(this.title);
}
Edit:
Since this.list is an array of array as per the screenshot, it means that user is an array within ngFor. Try user[0]?.caseId
So, basically i have two api calls in two different functions that are called from a single component page. The await function is applied to each of them because each of the response provided from the first api is used as params to the second api. for the first api call, inside the function, the response is pushed to the array named all_countries.
const apiCall = getCountry(req)
.then((response) => {
all_countries.push(...response);
dispatch(actionSuccess(NICE_ONE, response));
})
.catch((error) => {
dispatch(actionFailure(NICE_ONE, error));
});
each of the array objects inside all_countries is to be mapped and value(i.e country code present inside each object from all_countries) is used to call another api to get the response but my problem is upon console logging the all_countries from the second function, it shows the symbol of empty array. upon opening the array, it lists all the countries. when i console log the length of the array, it shows 0 and upon console logging the elements like all_countries[0], it shows undefined. Why is this happening? Could someone help me out please?
This gets shown on console
Upon expanding, this is shown:
enter image description here
So, i found that on adding all_countries.push, intellij shows me a warning, response is not an array type but rather AllCountryRes type.
AllRelaysRes type is defined as:
export type AllRelaysRes = {
logo: string;
ipv4: string;
ipv6: string;
port: string;
location: {
id: number;
ip: string
};
};
and the getCountry function is like this:
export const getCountry = (url: string): Promise<AllRelaysRes> => {
ExternalApi.setBaseUrl(url);
return ExternalApi.apiCall<AllRelaysRes>({
method: "GET",
url: "something",
});
};
The response from the api is an array of objects. I am just a beginner with typescript and trying out stuffs. My question is how do i make the response type as an array of object AllRelayRes? Doesnot return ExternalApi.apiCall angular brackets AllRelayRes mean that the response expected is an array of AllRelayRes since, AllRelayRes is kept inside the angular brackets? How do i fix this? Is this the real problem why i get the array length 0?
I believe you're dealing with a collection of objects of type AllRelayRes, not an array. It shows as [] in the console, but that represents a collection.
If you want to use it as an array, specify the type in your function <AllRelaysRes[]>
export const getCountry = (url: string): Promise<AllRelaysRes[]> => {
ExternalApi.setBaseUrl(url);
return ExternalApi.apiCall<AllRelaysRes[]>({
method: "GET",
url: "something",
});
};
Then you should be able to manipulate the data in an array format
If you want to keep it as a collection, you can probably iterate through them with
let x, key, obj
for(x in AllRelayRes) {
key = x; // 0, 1, 2 .. the index
obj = AllRelayRex[x] // the object
// country_name = AllRelayRex[x].country_name, etc
}
bellow the class i initialize an object of type Photo, which photo is an interface with some attributes. And then i'm trying to use array filter to filter photos.
The filter return a copy of the photos array but i specify which photo do i want so i will have an array with 1 index inside. And then, i try to set a property inside the object to false, and the "Cannot set property 'ismain' of undefined in angular" occurs. Any ideas why?
Here is my code.
setMainPhoto(photo: Photo) {
this.userService
.setMainPhoto(this.authService.decodedToken.nameid, photo.id)
.subscribe(
() => {
// We use the array filter method to filter the photos apart from the main photo
// Filter returns a copy of the photos array. Filters out anything that it doesn`t match in the p
this.currentMain = this.photos.filter(p => p.ismain === true)[0];
this.currentMain.ismain = false;
photo.ismain = true;
this.alertify.success('Successfully set to profile picture');
},
error => {
this.alertify.error('Photo could not be set as profile picture');
}
);
}
In the above method the error occurs.
And below is my Photo interface.
export interface Photo {
id: number;
url: string;
dateadded: Date;
ismain: boolean;
description: string;
}
ERROR
UPDATE
PROBLEM SOLVED
The response from the server needs to match with the object attributes. So i had to change the property interface to match the JSON object coming back from the server.
Try this
setMainPhoto(photo: Photo) {
this.userService
.setMainPhoto(this.authService.decodedToken.nameid, photo.id)
.subscribe(
() => {
// We use the array filter method to filter the photos apart from the main photo
// Filter returns a copy of the photos array. Filters out anything that it doesn`t match in the p
this.currentMain = this.photos.filter(p => p.ismain === true)[0];
if (this.currentMain) {
this.currentMain.ismain = false;
photo.ismain = true;
this.alertify.success('Successfully set to profile picture'); }
},
error => {
this.alertify.error('Photo could not be set as profile picture');
}
);
}
If this works, then the filter does not return anything.
Interesting, I've had this come up before too. Typescript is quite strict with capitalization and if you don't match the json object exactly, it won't work, create a new property, etc.
I've found it helpful to take a look at the json return before building my typescript model, to make sure I'm matching the object being sent.
I've been sitting with a problem for the past days & I can't seem to get a solution anywhere.
Background:
I have a typescript class defined as follows:
export class Client extends Person {
claimNumber: string;
policyNumber: string;
address: string;
insuranceCompany: Organisation = new Organisation();
toString(): string {
return this.policyNumber
.concat(this.claimNumber);
}
}
The above is used as a model that drives an angular 5 template. In my component, I fetch (using angular 5 HttpClient) a list of clients from a remote api & generate an html table rows. The LOC to generate the table rows is:
<tr *ngFor="let client of clients | filter:searchString"> ... </tr>
searchString above is property bound to a search input tag & filter is a custom filter Pipe defined as follows:
export class FilterPipe implements PipeTransform {
transform(items: Client[], term: string) {
if (term == undefined || term === '') return items;
return items.filter(item =>item.toString().toLocaleLowerCase().includes(term.toLocaleLowerCase()));
}
}
Problem:
When I inspect item.toString() in the filter pipe above, it returns [object Object] as opposed to a string made up of policyNumber, & claimNumber.
Investigation:
I investigated this issue as follows: I instantiated the Client class as follows:
let c = new Client();
c.policyNumber = 'ababababa';
c.claimNumber = 'aaaaaaa';
console.log('client toString() is => ' + c.toString());
Interesting enough, the console.log above outputs : 'ababababaaaaaaaa'.
Question:
What am I doing wrong that results in the item.toString() in the filter pipe return [object Object] whereas toString() on a class I instantiated returns the correct string?
If you get the clients from a WebService (or something similar), you are just getting plain json objects. If you say that the received objects are of type Client, typescript will show them as objects of such type, but only the properties will be the same, the methods will not be from the Client class, but from the Object class.
You might want to instantiate them as real client objects after you retrieve them from the server:
public myServiceMethod() {
return this.http.get(...).map(plainClients => {
const realClients: Array<Client> = (plainClients || []).map(plainClient => {
let realClient = new Client();
realClient.claimNumber = plainClient.claimNumber;
realClient.policyNumber = plainClient.policyNumber;
realClient.address = plainClient.address;
return realClient;
});
return realClients;
})
}
You might prefer to use anemic objects with their types being interfaces, and use an utilitarian function to retrieve the client as a string, to avoid cases like the one you're having:
export interface Person {
...
}
export interface Client extends Person {
claimNumber: string;
policyNumber: string;
address: string;
insuranceCompany: Organisation;
}
// In some utilitarian class
public static getClientInfo(client: Client) {
return client.policyNumber.concat(client.claimNumber);
}
// In your pipe
return items.filter(item => getClientInfo(item).toLocaleLowerCase().includes(term.toLocaleLowerCase()));
I'm not saying to just use anemic classes in your app, but if some type of object is passed around and will probably be serialized, then using anemic objects can avoid problems like the one you are having.
A way to figure out what the problem might be would be to rename your method to something that isn't a built in method name, like maybe toSearchString. It would also be worth adding console logs to your filter function to make sure you're actually getting clients there. You may actually be getting a different object.
Im following this tutorial. On the way to get list of users from api.github Im getting error:
Cannot find a differ supporting object '[object Object]'
I think its related to
<ul>
<li *ngFor = "#user of users">
{{user | json}}
</li>
</ul>
In my code because before it there was no any error, and im unsure if data come from get request, just clicking didnt give any error, here is my code so far
#Component({
selector: 'router',
pipes : [],
template: `
<div>
<form [ngFormModel] = "searchform">
<input type = 'text' [ngFormControl]= 'input1'/>
</form>
<button (click) = "getusers()">Submit</button>
</div>
<div>
<ul>
<li *ngFor = "#user of users">
{{user | json}}
</li>
</ul>
</div>
<router-outlet></router-outlet>
`,
directives: [FORM_DIRECTIVES]
})
export class router {
searchform: ControlGroup;
users: Array<Object>[];
input1: AbstractControl;
constructor(public http: Http, fb: FormBuilder) {
this.searchform = fb.group({
'input1': ['']
})
this.input1 = this.searchform.controls['input1']
}
getusers() {
this.http.get(`https://api.github.com/
search/users?q=${this.input1.value}`)
.map(response => response.json())
.subscribe(
data => this.users = data,
error => console.log(error)
)
}
}
bootstrap(router, [HTTP_PROVIDERS])
I think that the object you received in your response payload isn't an array. Perhaps the array you want to iterate is contained into an attribute. You should check the structure of the received data...
You could try something like that:
getusers() {
this.http.get(`https://api.github.com/search/users?q=${this.input1.value}`)
.map(response => response.json().items) // <------
.subscribe(
data => this.users = data,
error => console.log(error)
);
}
Edit
Following the Github doc (developer.github.com/v3/search/#search-users), the format of the response is:
{
"total_count": 12,
"incomplete_results": false,
"items": [
{
"login": "mojombo",
"id": 1,
(...)
"type": "User",
"score": 105.47857
}
]
}
So the list of users is contained into the items field and you should use this:
getusers() {
this.http.get(`https://api.github.com/search/users?q=${this.input1.value}`)
.map(response => response.json().items) // <------
.subscribe(
data => this.users = data,
error => console.log(error)
);
}
I received this error in my code because I'd not run JSON.parse(result).
So my result was a string instead of an array of objects.
i.e. I got:
"[{},{}]"
instead of:
[{},{}]
import { Storage } from '#ionic/storage';
...
private static readonly SERVER = 'server';
...
getStorage(): Promise {
return this.storage.get(LoginService.SERVER);
}
...
this.getStorage()
.then((value) => {
let servers: Server[] = JSON.parse(value) as Server[];
}
);
Missing square brackets around input property may cause this error.
For example:
Component Foo {
#Input()
bars: BarType[];
}
Correct:
<app-foo [bars]="smth"></app-foo>
Incorrect (triggering error):
<app-foo bars="smth"></app-foo>
Something that has caught me out more than once is having another variable on the page with the same name.
E.g. in the example below the data for the NgFor is in the variable requests.
But there is also a variable called #requests used for the if-else
<ng-template #requests>
<div class="pending-requests">
<div class="request-list" *ngFor="let request of requests">
<span>{{ request.clientName }}</span>
</div>
</div>
</ng-template>
This ridiculous error message merely means there's a binding to an array that doesn't exist.
<option
*ngFor="let option of setting.options"
[value]="option"
>{{ option }}
</option>
In the example above the value of setting.options is undefined. To fix, press F12 and open developer window. When the the get request returns the data look for the values to contain data.
If data exists, then make sure the binding name is correct
//was the property name correct?
setting.properNamedOptions
If the data exists, is it an Array?
If the data doesn't exist then fix it on the backend.
If this is an Observable being return in the HTML simply add the async pipe
observable | async
Explanation:
You can *ngFor on the arrays. You have your users declared as the array. But, the response from the Get returns you an object. You cannot ngFor on the object. You should have an array for that. You can explicitly cast the object to array and that will solve the issue.
data to [data]
Solution
getusers() {
this.http.get(`https://api.github.com/
search/users?q=${this.input1.value}`)
.map(response => response.json())
.subscribe(
data => this.users = [data], //Cast your object to array. that will do it.
error => console.log(error)
)
If you don't have an array but you are trying to use your observable like an array even though it's a stream of objects, this won't work natively. I show how to fix this below.
If you are trying to use an observable whose source is of type BehaviorSubject, change it to ReplaySubject then in your component subscribe to it like this:
Component
this.messages$ = this.chatService.messages$.pipe(scan((acc, val) => [...acc, val], []));
Html
<div class="message-list" *ngFor="let item of messages$ | async">