How to retrieve and display an embedded Firestore object with Angular? - json

I would like to display a Firestore model containing a profile name and a list of describing hashtags with Angular 6.
Being new to Firestore I learned, that I should model it like this:
enter image description here
members {
id: xyz
{
name: Jones;
hashtag: {
global: true,
digital: true
}
...
}
Now, I try to understand how to display the keys of every hashtag property in my component list. My current code always displays [object Object] as result. My component code is:
import { Component } from '#angular/core';
import { AngularFirestore } from 'angularfire2/firestore';
import { Observable } from 'rxjs';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
items$: Observable<any[]>;
constructor(db: AngularFirestore) {
this.items$ = db.collection('Members').valueChanges();
console.log(this.items$);
}
}
My template code is:
<p>names and their interests</p>
<div *ngFor="let item of items$ | async">
<h4>{{item.name}}</h4>
<ul>
<li>{{item.hashtag}}</li>
</ul>
</div>
How can I fix this?

Think you're looking for (Angular 6.1):
<li *ngFor="let hashtag of item.hashtag| keyvalue">
{{hashtag.key}}:{{hashtag.value}}
</li>
What this will do is get the key and the value of the item.hastag and let them be used in hastag.

Related

angular no access to object.id

hey i just started with angular and my problem is, why i have no access to my .id,.name and .color, the first code is my oberteile-names.ts where i have defined my const array
import { Oberteil } from './oberteile/oberteil';
export const OBERTEILE: Oberteil[] = [
{id:11, name: 'flannelshirt', color: 'vintagebrown'},
{id:12, name: 'flannelshirt', color: 'vintagegreen'},
{id:13, name: 'flannelshirt', color: 'vintagewhite'},
{id:14, name: 'cordhemd', color: 'black'},
{id:15, name: 'cordhemd', color: 'vintagebrown'},
{id:16, name: 'cordhemd', color: 'vintagegreen'},
{id:17, name: 'basicshirt', color: 'black'},
{id:18, name: 'basicshirt', color: 'white'}
];
here is my simple oberteil.ts
export interface Oberteil {
id: number;
name: string;
color : string;
}
so now i go into my oberteile.component.ts and initialize oberteil with the const OBERTEILE
import { Component, OnInit } from '#angular/core';
import { OBERTEILE } from '../oberteil-names';
#Component({
selector: 'app-oberteile',
templateUrl: './oberteile.component.html',
styleUrls: ['./oberteile.component.css']
})
export class OberteileComponent implements OnInit {
oberteil = OBERTEILE;
constructor() { }
ngOnInit(): void {
}
}
now i want to display them on my oberteile.component.html but i have no access to id
<h2>{{oberteil.id}}</h2>
i think it is very easy to be solved but i can not find any answers for it, even when i just want to display {{oberteile}}, it just display [object,Object]
oberteil is an array of multiple elements. You need to select a specific element of the array, or iterate over the array with *ngFor:
<h2>{{oberteil[0].id}}</h2><!-- display the ID of the first element -->
<h2 *ngFor="let element of oberteil">{{element.id}}</h2><!-- display IDs of all elements -->
oberteil is an array. You can't access it like that. You need to specify the index of the array.
Or if you are trying to show the id from each element of oberteil array, you can use *ngFor directive.
<h1 *ngFor="let o of oberteil">{{o.id}}</h1>
See *ngFor
oberteil is array, no Object, so you cannot bind the value with <h2>{{oberteil.id}}</h2>
It should be like that in *ngFor iteration to get all values
<div *ngFor="let item of oberteil">
<p>{{item.id}}</p>
<p>{{item.name}}</p>
<p>{{item.color}}</p>
</div>
It is just example to use your oberteil variable in html to bind the data.

How can I change a body tag class in Angular 10 (best practice)?

I want to switch between two classes (light and dark) at TAG Body.
What I did? I created a service:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class ThemeService {
body = document.body;
constructor() { }
changeLight() {
this.body.classList.replace('light', 'dark');
}
changeDark() {
this.body.classList.replace('dark', 'light');
}
}
It is working as expected but I know that this code does not use best practices.
What is the correct way to change between these two classes?
Edit: Added a service to the stackblitz, but again, there are many ways to do this. This is just a starting point.
While the "right way" is subjective, you have some options to make it "Angular-y"
Component:
import { Component, Inject } from '#angular/core';
import { DOCUMENT } from '#angular/common';
// Create a type that accepts either the string 'light' or 'dark' only
type Theme = 'light' | 'dark';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
// Default to 'light' theme
currentTheme: Theme = 'light';
// Inject document which is safe when used with server-side rendering
constructor(#Inject(DOCUMENT) private document: Document) {
// Add the current (light) theme as a default
this.document.body.classList.add(this.currentTheme);
}
// Swap them out, and keep track of the new theme
switchTheme(newTheme: Theme): void {
this.document.body.classList.replace(this.currentTheme, newTheme)
this.currentTheme = newTheme;
}
}
HTML:
<p>
Current theme: {{ currentTheme }}
<button (click)="switchTheme('light')">Light mode</button>
<button (click)="switchTheme('dark')">Dark mode</button>
</p>
Many ways to do this, but one benefit of defining the types is if you provide a bad value, such as:
<p>
Current theme: {{ currentTheme }}
<button (click)="switchTheme('light')">Light mode</button>
<button (click)="switchTheme('dark')">Dark mode</button>
<button (click)="switchTheme('noop')">Invalid</button>
</p>
You'll get an error:
Argument of type '"noop"' is not assignable to parameter of type 'Theme'.
StackBlitz

How to close the menu - cuppa-ng2-slidemenu

I am using this angular ng2 slide menu library. Can you please suggest me how to close the menu after selecting any item.
The configuration says there is an option. Not sure how to configure the value.
Thanks,
Raja K
as per angular ng2 slide library documentation
in your template you add config attribute, like
<cuppa-slidemenu
[menulist]="menuItemsArray"
[config]="config"
(open)="onMenuOpen()"
(close)="onMenuClose()"
(onItemSelect)="onItemSelect($event)">
</cuppa-slidemenu>
and in your ts file, you can define config, as shown in documentation.
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
config = {
closeOnCLick: true
};
}
here is a live working demo

Customer filter pipe - Angular2

In the below component view,
<h2>Topic listing</h2>
<form id="filter">
<label>Filter topics by name:</label>
<input type="text" [(ngModel)]="term">
</form>
<ul id="topic-listing">
<li *ngFor="let topic of topics | filter: term">
<div class="single-topic">
<!-- Topic name -->
<span [ngStyle]="{background: 'yellow'}">name - {{topic.name}}</span>
<!-- Topic type -->
<h3>{{topic.type}}</h3>
</div>
</li>
</ul>
Custom filter syntax is: let topic of topics | filter: term
where custom filter is:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filter'
})
export class FilterPipe implements PipeTransform {
transform(topics: any, term: any): any {
// check if search term is undefined
if(term === undefined) return topics;
return topics.filter(function(topic){ // javascript filter(function)
// if below is false, then topic will be removed from topics array
return topic.name.toLowerCase().includes(term.toLowerCase());
})
}
}
the component class maintains data for topics:
export class DirectoryComponent implements OnInit {
topics = [
{type:"Fiction", name:"Avatar"},
{type:"NonFiction", name:"Titanic"},
{type:"Tragedy", name:"MotherIndia"},
];
constructor() { }
ngOnInit() {
}
}
Edit: Without form tag, code works fine.
<label>Filter topics by name:</label>
<input type="text" [(ngModel)]="term">
Why custom filter FilterPipe does not filter term provided in input element?
Add brackets to your filter condition
<ul id="topic-listing">
<li *ngFor="let topic of (topics | filter: term)">
<div class="single-topic">
<!-- Topic name -->
<span [ngStyle]="{background: 'yellow'}">name - {{topic.name}}</span>
<!-- Topic type -->
<h3>{{topic.type}}</h3>
</div>
</li>
</ul>
Check the TS file
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular 5';
term : any = "avatar";
topics = [
{type:"Fiction", name:"Avatar"},
{type:"NonFiction", name:"Titanic"},
{type:"Tragedy", name:"MotherIndia"},
];
}
Remove the word function and change it to below code. refer the working version here
return topics.filter((topic)=>{
return topic.name.toLowerCase().includes(term.toLowerCase());
})
update - root cause of the issue
if you want to use NgModel inside the form tags, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions

Angular 2 *ngFor not displaying data

I can't seem to get the data in this.heroes to display on the list. I put an alert(this.heroes[0].name); and it did display the data so this._service.getHeroes(); is returning the data
app.html
<div style="width:1000px;margin:auto">
<ul>
<li *ngFor="#hero of heroes">
<span>{{hero.name}}</span>
</li>
</ul>
</div>
app.component.ts
import {Component} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {HeroService} from './heroes/hero.service';
import {Hero} from './heroes/hero';
#Component({
selector: 'my-app',
templateUrl: 'templates/app.html',
providers: [HeroService],
directives: [ROUTER_DIRECTIVES]
})
export class AppComponent {
heroes: Hero[];
constructor(private _service: HeroService) { }
ngOnInit() {
this.heroes = this._service.getHeroes();
}
}
Try this first and see if it works:
<li ng-repeat="hero in heroes">
{{hero.name}}
</li>
If this works you might have an older angular version that does not support the *ngFor syntax yet (I think this is supported in the new version angular 2).
I've done a little more research and no data is displaying. For example:
<h2>My favorite hero is {{ myHero }}</h2>
gets a blank line (where myHero is a populated string) whereas
<h2>My favorite hero is </h2>
displays
My favourite hero is
I'm going to change the Beta version of Angular 2 to see if it helps