Customer filter pipe - Angular2 - html

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

Related

What is the purpose of <app-control-message> in angular 8 and how it is used?

I need to create a button for uploading a file in my form. I am using tag in my html but it is throwing an error : Can't bind to 'control' since it isn't a known property of 'app-contorl-message'.
Here is my html code -
<div class="col-lg-6">
<div class="form-group">
<div class="custom-file">
<input *ngIf="mode == 'CREATE'" type="file" (change)="onFileSelect($event)"
accept=".jpg,.jpeg,.png"/>
<app-contorl-message [control]="form.get('file')"></app-contorl-message>
</div>
</div>
</div>
here is def of onSelect($event) :
onFileSelect(event) {
this.form.get('file').setValue(event.srcElement.files);
this.fileData = event.srcElement.files;
console.log(event);
}
Thanks in advnace!
In your AppControlMessageComponent you need to create an #Input() named control. To learn more about inputs and output, visit: https://angular.io/guide/inputs-outputs
app-control-message.component.ts
import { Component, OnInit, Input } from "#angular/core";
#Component({
selector: "app-app-control-message",
templateUrl: "./app-control-message.component.html",
styleUrls: ["./app-control-message.component.css"]
})
export class AppControlMessageComponent implements OnInit {
#Input() control; // Input whose value parent component will provide
constructor() {}
ngOnInit() {}
}

ngFor giving error 'Identifier is not defined __type does not contain such a member'

I have implemented a sub-component in which the user can dynamically add and remove a set of controls to and from a collection. The solution was based on the answer from this SO question.
It compiles and works like a charm but there is an annoying message on the *ngFor directive that says:
Identifier 'sections' is not defined. '__type' does not contain such a
member Angular
I am using VS Code as my IDE.
I have seen similar errors on *ngIf directives and the message goes away when you add a double exclamation point (!!) at the beginning of the condition statement but in this case the directive is using a collection not a Boolean value.
How can I make this eyesore go away?
The HTML looks like this:
<div class="row" [formGroup]="saveForm">
<label for="sections" class="col-md-3 col-form-label">Sections:</label>
<div class="col-md-9">
<a class="add-link" (click)="addSection()">Add Section</a>
<div formArrayName="sections">
<!-- The "problem" seems to be "saveForm.controls.sections" -->
<div *ngFor="let section of saveForm.controls.sections.controls; let i=index" [formGroupName]="i">
<label for="from">From:</label>
<input class="form-control" type="text" formControlName="from">
<label for="to">To:</label>
<input class="form-control" type="text" formControlName="to">
</div>
</div>
</div>
</div>
And this is the component:
import { FormGroup, FormBuilder, FormArray, ControlContainer } from '#angular/forms';
import { Component, OnInit, Input } from '#angular/core';
import { ISection } from '../shared/practice.model';
#Component({
selector: '[formGroup] app-sections',
templateUrl: './sections.component.html',
styleUrls: ['./sections.component.scss']
})
export class SectionsComponent implements OnInit {
#Input() sections: ISection[];
saveForm: FormGroup;
get sectionsArr(): FormArray {
return this.saveForm.get('sections') as FormArray;
}
constructor(private formBuilder: FormBuilder, private controlContainer: ControlContainer) { }
ngOnInit() {
this.saveForm = this.controlContainer.control as FormGroup;
this.saveForm.addControl('sections', this.formBuilder.array([this.initSections()]));
this.sectionsArr.clear();
this.sections.forEach(s => {
this.sectionsArr.push(this.formBuilder.group({
from: s.from,
to: s.to
}));
});
}
initSections(): FormGroup {
return this.formBuilder.group({
from: [''],
to: ['']
});
}
addSection(): void {
this.sectionsArr.push(this.initSections());
}
}
Turns out Florian almost got it right, the correct syntax would be:
<div *ngFor="let section of saveForm.get('sections')['controls']; let i=index" [formGroupName]="i">
That way the error/warning goes away and the component still works as expected.

Target a checkbox in *ngfor checkbox list

I have a list of checkboxes that are binded to an object in my component typescript file, I want it to check/uncheck on the list when user clicks on the checkbox, but for some reason, it only checks the and uncheck the first checkbox on the list and not the one that user has clicked on.
Here is the code below:
<div>
<ul class="reports-container">
<li *ngFor="let item of data.reports" [class.active]="selectedReport==item" >
<input type="checkbox" id="{{'savedreport'+i}}" class="k-checkbox" [checked]="item.IsSubscribed" [value]="item.IsSubscribed"(change)="onChkBoxChange($event,item)" />
</li>
</ul>
</div>
here is the typescript function:
onChkBoxChange(event, item: SavedReport) {
item.IsSubscribed = event.target.checked;
}
when I put a breakpoint it always passes in the first item from the list, any thoughts?
As #Michael Beeson suggested I used two-way binding on my checkbox value, that solved the problem, so the code is now:
<div>
<ul class="reports-container">
<li *ngFor="let item of data.reports" [class.active]="selectedReport==item" >
<input type="checkbox" id="{{'savedreport'+i}}" class="k-checkbox" [checked]="item.IsSubscribed" [(value)]="item.IsSubscribed"(change)="onChkBoxChange($event,item)" />
</li>
</ul>
</div>
Advice: use angular forms for this that's why forms exist in angular is
gonna simplify the whole case like yours.
I made a stackblitz to show you how to occur this by using reactive forms and FormArray in angular you even can use template driven forms if you want to, the point is using forms feature in angular gonna save your time and effort when you encounter such a case.
html
<div>
<ul class="reports-container">
<form [formGroup]="checkboxForm">
<div formArrayName="checkboxList" *ngFor="let item of data; let i = index">
<label>
<input type="checkbox" id="{{'savedreport'+i}}" [name]="item" [formControlName]="i" class="k-checkbox" (change)="onChkBoxChange($event, i)" />
{{item}}
</label>
</div>
</form>
</ul>
</div>
TS
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, FormArray } from '#angular/forms';
#Component({
selector: '...',
templateUrl: '...',
styleUrls: [ '...' ]
})
export class AppComponent implements OnInit {
data: string[] = ['data1', 'data2', 'data3', 'data4'];
checkboxForm: FormGroup;
ngOnInit() {
this.checkboxForm = new FormGroup({
checkboxList: new FormArray([])
});
this.arrayOfCheckboxs();
}
private arrayOfCheckboxs() {
const formArr = this.checkboxForm.get('checkboxList') as FormArray;
this.data.forEach(item => {
formArr.push(new FormControl());
});
}
onChkBoxChange(e: MouseEvent, idx: number) {
console.log(`${(<HTMLInputElement>e.target).name}: ${this.checkboxForm.get('checkboxList').value[idx]}`);
}
}

TypeError: Cannot read property 'bookIsbn' of undefined when used ngModel with anular materials

I went through all the related issues, but can not find any proper answer.
I am getting an error when using the [(ngModel)] with the angular material in forms data binding.
add-book.component.html
<html>
<head>
<title>
Create Book
</title>
</head>
<header>
<h2 class="form_heading"> Create New Book </h2>
</header>
<body class="create_Book_Body">
<form name="createBookForm" #createBookForm="ngForm">
{{libraryItemModel | json}}
<mat-form-field class="form_Field">
<input type="text" pattern="[0-9]*" minlength="5" maxlength="5" min="10000" name="bookIsbn" #bookIsbn="ngModel" [(ngModel)]="libraryItemModel.bookIsbn" matInput
placeholder="DVD ISBN NO" required>
<div *ngIf="((bookIsbn.touched) && !(bookIsbn.valid))">
<div *ngIf="bookIsbn.errors.required">
<mat-error >
This field is required/Invalid
</mat-error>
</div>
<div *ngIf="bookIsbn.errors.minlength">
<mat-error >
ISBN should be of 5 characters
</mat-error>
</div>
<div *ngIf="bookIsbn.errors.pattern">
<mat-error >
Invalid Pattern
</mat-error>
</div>
</div>
</mat-form-field>
</form>
</body>
</html>
add-book.component.ts
import {Component, Input, OnInit} from '#angular/core';
import {FormControl, FormGroup, Validators} from '#angular/forms';
#Component({
selector: 'app-add-book',
templateUrl: './add-book.component.html',
styleUrls: ['./add-book.component.css']
})
export class AddBookComponent implements OnInit {
onSubmit($event) : void {
console.log(($event));
}
constructor() { }
ngOnInit() {
}
}
here I have created a class library item, in which the models are been created and the form data will be bound to.
library-item.ts
export class LibraryItem {
constructor(
public bookIsbn : string
){}
}
app.component.ts
import { Component } from '#angular/core';
import {LibraryItem} from './library-item';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Lib_Manager';
inputText : string = 'initial Value'
libraryItemModel = new LibraryItem('12345');
}
Error
Thanks in advance for considering my issue.
In your html you have used bookIsbn as a form validation input... but what you have done is adding a string as bookIsbn and try to read the properties...
Check angular validation for proper validation..
https://angular.io/guide/form-validation

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

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.