I am getting below error even by adding. can someone guide me or make some code changes so that the program executes as expected
Error: src/app/product/product.component.html:21:45 - error TS2367: This condition will always return 'true' since the types 'Observable<Product[]>' and 'number' have no overlap.
21 <table class = "table table-hover" *ngIf = "products != 0">
~~~~~~~~~~~~~
src/app/product/product.component.ts:9:16
9 templateUrl: './product.component.html',
~~~~~~~~~~~~~~~~~~~~~~~~~~
Error occurs in the template of component ProductComponent.
import { Component, OnInit } from '#angular/core';
import { Product } from './product.model';
import { Store } from '#ngrx/store';
import { Observable } from 'rxjs';
import { AppState } from './../app.state';
#Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
products : Observable<Product[]>;
constructor(private store: Store<AppState>) {
this.products = this.store.select(state => state.product)
}
addProduct(name: any, price: any){
this.store.dispatch({
type : 'ADD_PRODUCT',
payload : <Product>{
name : name,
price : price
}
});
}
ngOnInit(): void {
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<table class = "table table-hover" *ngIf = "products != 0">
<thead>
<tr>
<td>Product Name</td>
<td>Product Price</td>
</tr>
</thead>
<tbody>
<tr *ngFor = "let product of products | async">
<td></td>
<td></td>
</tr>
</tbody>
</table>
You are missing something here
<table class = "table table-hover" *ngIf = "products != 0">
The error tells you that the condition its going to be always true because products is of type Observable<Product[]> and 0 is a number, and obviously they are diferent.
Maybe you want to go with this
<table class = "table table-hover" *ngIf = "(products | async).length != 0">
Here the condition is asking if the length of the array returned by the Observable products is not 0. You can use the someObservable | async when you want to retrieve the value emitted by an Observable in the template.
I am developing a Angular website with help of Firebase Firestore. It is my first project on Angular. I have learned Angular 2months ago. Please See the below codes: -
Component.html
<section class="rank">
<p class="records" *ngIf="members.length === 0">No Records Found.</p>
<div class="text-img" *ngIf="members.length > 0">
<p class="sb">Best Sulphuric</p>
<p class="role">Member</p>
<p class="name">
{{ members[0].payload.doc.data().name }}
</p>
</div>
<table *ngIf="members.length > 0">
<tr>
<th>ID</th>
<th>Name</th>
<th>Posts</th>
<th>Score</th>
</tr>
<tr *ngFor="let member of members; let indexOfelement = index">
<td>{{ indexOfelement + 1 }}</td>
<td>{{ member.payload.doc.data().name }}</td>
<td>{{ member.payload.doc.data().posts }}</td>
<td>{{ member.payload.doc.data().score }}</td>
</tr>
</table>
</section>
Component.ts
import { Component, OnInit } from '#angular/core';
import { AngularFirestore } from '#angular/fire/firestore';
#Component({
selector: 'app-rank',
templateUrl: './rank.component.html',
styleUrls: ['./rank.component.scss'],
})
export class RankComponent implements OnInit {
members: any;
constructor(public db: AngularFirestore) {
db.collection('members')
.snapshotChanges()
.subscribe((res) => (this.members = res));
}
ngOnInit(): void {}
}
When I open this on browser this shows all the data in members in Firestore. But when i change component.ts to this -->
Component.ts
import { Component, OnInit } from '#angular/core';
import { AngularFirestore } from '#angular/fire/firestore';
#Component({
selector: 'app-rank',
templateUrl: './rank.component.html',
styleUrls: ['./rank.component.scss'],
})
export class RankComponent implements OnInit {
members: any;
constructor(public db: AngularFirestore) {
this.members = db.collection('members').ref.orderBy('score');
}
ngOnInit(): void {}
}
It shows no data on window. Can you help me please?
Thanks in Advance for Helping.
In the second version,
You are missing the
.snapshotChanges()
.subscribe((res) => (this.members = res));
}
inside the constructor. Without the subscribe, Angular will not make any HTTP Requests and your component will not receive any data.
In my Angular application. I have an array which is getting objects pushed to it from an rest api. The array is called playlist=[] and it is being shared across the components with a service called playlist service. Also within this service are two functions. One to save the playlist to localStorage and one to get it from localStorage. So what is happening is when I save the playlist, it saves to local storage fine. Even if I refresh they still are in the localstorage. So when I use my app in a single session (not refreshing the browser) the savePlaylist() method adds objects to the playlist array, it does not overwrite them (So that is fine). However if I refresh the page add items and then save - the items are overwritten, when they should be added to the localstorage ones that are already there saved. Is this possible? Is this to do with sessionStorage? Any Ideas? My code so far is:
playlist.service.ts
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class PlaylistService {
public playlist = [];
getPlaylist() {
if (localStorage.getItem('playlist') == null) {
this.playlist = [];
} else {
this.playlist = JSON.parse(localStorage.getItem('playlist'));
}
}
savePlaylist() {
// first save the data
localStorage.setItem('playlist', JSON.stringify(this.playlist));
// get what is saved afterwords
this.playlist = JSON.parse(localStorage.getItem('playlist'));
console.log('Saved', this.playlist);
}
constructor() {
}
}
playlist.ts (Where it should show the save playlist)
import { Component, OnInit } from '#angular/core';
import { PlaylistService } from '../../../services/playlist.service';
import { faSave } from '#fortawesome/free-solid-svg-icons';
#Component({
selector: 'app-playlist-view',
templateUrl: './playlist-view.component.html',
styleUrls: ['./playlist-view.component.scss']
})
export class PlaylistViewComponent implements OnInit {
faSave = faSave;
constructor(private list: PlaylistService) { }
ngOnInit() {
this.list.getPlaylist();
}
}
playlist.html
<app-header></app-header>
<div class="container">
<table class="table mt-3 mb-3">
<thead class="thead-light">
<tr>
<th>Artwork</th>
<th>Artist</th>
<th>Title</th>
<th>Genre</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of list.playlist">
<td><img src="{{user.artworkUrl60}}"></td>
<td>{{user.artistName}}</td>
<td>{{user.collectionName}}</td>
<td>{{user.primaryGenreName}}</td>
<td>{{user.collectionPrice}}</td>
</tr>
</tbody>
</table>
</div>
<app-footer></app-footer>
I'm beginner to Angular 2.
I was trying to create the custom pipe for search operation in angular 2.
While trying to filter the objects of data by using the filter function I was getting as like my data is not supporting the filter function.
My Pipe code :
import {Pipe,PipeTransform} from '#angular/core'
#Pipe({
name : 'GenderSetter'
})
export class SettingGenderPipe implements PipeTransform
{
transform(Employees:any,EmpFind:any):any{
if(EmpFind === undefined) return Employees;
else {
return Employees.filter(function(x){
console.log(x.toLowerCase().includes(EmpFind.toLowerCase()));
return x.toLowerCase().includes(EmpFind.toLowerCase())
})
}
}
}
My template html file :
<div style="text-align:center">
<input type="text" id='txtsearch' [(ngModel)]="EmpFind"/>
<table>
<thead>
<td>Name</td>
<td>Gender</td>
<td>Salary</td>
</thead>
<tbody>
<tr *ngFor='let x of Employees'>
<td>{{x.Empname | GenderSetter : EmpFind}}</td>
<td>{{x.gender}}</td>
<td>{{x.salary}}</td>
</tr>
</tbody>
</table>
</div>
My Component Code :
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
Employees=[
{Empname : 'Roshan',gender:1,salary:'70k' },
{Empname : 'Ishita',gender:0,salary:'60k' },
{Empname : 'Ritika',gender:0,salary:'50k' },
{Empname : 'Girish',gender:1,salary:'40k' },
]
}
In the console I'm getting the error as :
ERROR TypeError: Employees.filter is not a function.
The problem is that you are applying the pipe to the x.Empname, however the pipe itself should accept an array. Move your pipe to the ngFor:
<tr *ngFor='let x of Employees | GenderSetter : EmpFind'>
<td>{{x.Empname}}</td>
<td>{{x.gender}}</td>
<td>{{x.salary}}</td>
</tr>
I am struggling to find a way to do this. In a parent component, the template describes a table and its thead element, but delegates rendering the tbody to another component, like this:
<table>
<thead>
<tr>
<th>Name</th>
<th>Time</th>
</tr>
</thead>
<tbody *ngFor="let entry of getEntries()">
<my-result [entry]="entry"></my-result>
</tbody>
</table>
Each myResult component renders its own tr tag, basically like so:
<tr>
<td>{{ entry.name }}</td>
<td>{{ entry.time }}</td>
</tr>
The reason I'm not putting this directly in the parent component (avoiding the need for a myResult component) is that the myResult component is actually more complicated than shown here, so I want to put its behaviour in a separate component and file.
The resulting DOM looks bad. I believe this is because it is invalid, as tbody can only contain tr elements (see MDN), but my generated (simplified) DOM is :
<table>
<thead>
<tr>
<th>Name</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<my-result>
<tr>
<td>Bob</td>
<td>128</td>
</tr>
</my-result>
</tbody>
<tbody>
<my-result>
<tr>
<td>Lisa</td>
<td>333</td>
</tr>
</my-result>
</tbody>
</table>
Is there any way we can get the same thing rendered, but without the wrapping <my-result> tag, and while still using a component to be sole responsible for rendering a table row ?
I have looked at ng-content, DynamicComponentLoader, the ViewContainerRef, but they don't seem to provide a solution to this as far as I can see.
You can use attribute selectors
#Component({
selector: '[myTd]'
...
})
and then use it like
<td myTd></td>
You need "ViewContainerRef" and inside my-result component do something like this:
.html:
<ng-template #template>
<tr>
<td>Lisa</td>
<td>333</td>
</tr>
</ng-template>
.ts:
#ViewChild('template', { static: true }) template;
constructor(
private viewContainerRef: ViewContainerRef
) { }
ngOnInit() {
this.viewContainerRef.createEmbeddedView(this.template);
}
you can try use the new css display: contents
here's my toolbar scss:
:host {
display: contents;
}
:host-context(.is-mobile) .toolbar {
position: fixed;
/* Make sure the toolbar will stay on top of the content as it scrolls past. */
z-index: 2;
}
h1.app-name {
margin-left: 8px;
}
and the html:
<mat-toolbar color="primary" class="toolbar">
<button mat-icon-button (click)="toggle.emit()">
<mat-icon>menu</mat-icon>
</button>
<img src="/assets/icons/favicon.png">
<h1 class="app-name">#robertking Dashboard</h1>
</mat-toolbar>
and in use:
<navigation-toolbar (toggle)="snav.toggle()"></navigation-toolbar>
Attribute selectors are the best way to solve this issue.
So in your case:
<table>
<thead>
<tr>
<th>Name</th>
<th>Time</th>
</tr>
</thead>
<tbody my-results>
</tbody>
</table>
my-results ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'my-results, [my-results]',
templateUrl: './my-results.component.html',
styleUrls: ['./my-results.component.css']
})
export class MyResultsComponent implements OnInit {
entries: Array<any> = [
{ name: 'Entry One', time: '10:00'},
{ name: 'Entry Two', time: '10:05 '},
{ name: 'Entry Three', time: '10:10'},
];
constructor() { }
ngOnInit() {
}
}
my-results html
<tr my-result [entry]="entry" *ngFor="let entry of entries"><tr>
my-result ts
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: '[my-result]',
templateUrl: './my-result.component.html',
styleUrls: ['./my-result.component.css']
})
export class MyResultComponent implements OnInit {
#Input() entry: any;
constructor() { }
ngOnInit() {
}
}
my-result html
<td>{{ entry.name }}</td>
<td>{{ entry.time }}</td>
See working stackblitz: https://stackblitz.com/edit/angular-xbbegx
Use this directive on your element
#Directive({
selector: '[remove-wrapper]'
})
export class RemoveWrapperDirective {
constructor(private el: ElementRef) {
const parentElement = el.nativeElement.parentElement;
const element = el.nativeElement;
parentElement.removeChild(element);
parentElement.parentNode.insertBefore(element, parentElement.nextSibling);
parentElement.parentNode.removeChild(parentElement);
}
}
Example usage:
<div class="card" remove-wrapper>
This is my card component
</div>
and in the parent html you call card element as usual, for example:
<div class="cards-container">
<card></card>
</div>
The output will be:
<div class="cards-container">
<div class="card" remove-wrapper>
This is my card component
</div>
</div>
Another option nowadays is the ContribNgHostModule made available from the #angular-contrib/common package.
After importing the module you can add host: { ngNoHost: '' } to your #Component decorator and no wrapping element will be rendered.
Improvement on #Shlomi Aharoni answer. It is generally good practice to use Renderer2 to manipulate the DOM to keep Angular in the loop and because for other reasons including security (e.g. XSS Attacks) and server-side rendering.
Directive example
import { AfterViewInit, Directive, ElementRef, Renderer2 } from '#angular/core';
#Directive({
selector: '[remove-wrapper]'
})
export class RemoveWrapperDirective implements AfterViewInit {
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
ngAfterViewInit(): void {
// access the DOM. get the element to unwrap
const el = this.elRef.nativeElement;
const parent = this.renderer.parentNode(this.elRef.nativeElement);
// move all children out of the element
while (el.firstChild) { // this line doesn't work with server-rendering
this.renderer.appendChild(parent, el.firstChild);
}
// remove the empty element from parent
this.renderer.removeChild(parent, el);
}
}
Component example
#Component({
selector: 'app-page',
templateUrl: './page.component.html',
styleUrls: ['./page.component.scss'],
})
export class PageComponent implements AfterViewInit {
constructor(
private renderer: Renderer2,
private elRef: ElementRef) {
}
ngAfterViewInit(): void {
// access the DOM. get the element to unwrap
const el = this.elRef.nativeElement; // app-page
const parent = this.renderer.parentNode(this.elRef.nativeElement); // parent
// move children to parent (everything is moved including comments which angular depends on)
while (el.firstChild){ // this line doesn't work with server-rendering
this.renderer.appendChild(parent, el.firstChild);
}
// remove empty element from parent - true to signal that this removed element is a host element
this.renderer.removeChild(parent, el, true);
}
}
This works for me and it can avoid ExpressionChangedAfterItHasBeenCheckedError error.
child-component:
#Component({
selector: 'child-component'
templateUrl: './child.template.html'
})
export class ChildComponent implements OnInit {
#ViewChild('childTemplate', {static: true}) childTemplate: TemplateRef<any>;
constructor(
private view: ViewContainerRef
) {}
ngOnInit(): void {
this.view.createEmbeddedView(this.currentUserTemplate);
}
}
parent-component:
<child-component></child-component>