Angular6 filter - filter

I'm trying to build a filter pipe in Angular 6, but it's not working properly.
.html
<form>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="searchString" placeholder="Type to search..." [(ngModel)]="searchString">
</div>
</div>
</form>
<table class="table">
<tr *ngFor="let user of users | filter2 : searchString">
{{user.name}}
</tr>
</table>
pipe
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filter2'
})
export class FilterPipe implements PipeTransform {
transform(items: any[], searchText: string): any[] {
if (!items) return [];
if (!searchText) return items;
searchText = searchText.toLowerCase();
return items.filter(it => {
return it.toLowerCase().includes(searchText);
});
}
}
I get this error when I write inside input:
ERROR TypeError: it.toLowerCase is not a function
What am I doing wrong? Thank you for your time!
users

You can try it.name.toString().toLowerCase().includes(searchText);
Component
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
users:any[];
constructor(){
this.users = [
{name:"John"},
{name:"Tom"},
{name:"Carlton"},
{name:"Judy"},
{name:"Ada"}
];
}
}
Html
<form>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="searchString" placeholder="Type to search..." [(ngModel)]="searchString">
</div>
</div>
</form>
<table class="table">
<tr *ngFor="let user of users | filter2 : searchString">
{{user.name}}
</tr>
</table>
Pipe
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filter2'
})
export class FilterPipe implements PipeTransform {
transform(items: any[], searchText: string): any[] {
if (!items) return [];
if (!searchText) return items;
searchText = searchText.toLowerCase();
return items.filter(it => {
return it.name.toLowerCase().includes(searchText);
});
}
}

Related

ERROR Error: No value accessor for form control with name:

I have followed
the following guide
Angular-Reactive forms
I modified a bit the code to put it in a module, adding some fields to the originals model and it worked.
after upgrading to angular 8, the module is still working, but the console in Chrome for every control gives me this error:core.js:6150 ERROR Error: No value accessor for form control with name: 'title' at _throwError (forms.js:2603) at setUpControl (forms.js:2377) at FormGroupDirective.addControl (forms.js:5699) at FormControlName._setUpControl (forms.js:6274) at FormControlName.ngOnChanges (forms.js:6219) at FormControlName.rememberChangeHistoryAndInvokeOnChangesHook (core.js:1506) at callHook (core.js:2525) at callHooks (core.js:2492) at executeInitAndCheckHooks (core.js:2443) at selectIndexInternal (core.js:8389)
this is my code for question-form-component.html:
<div [formGroup]="form">
<label [attr.for]="question?.key">{{ question?.label }}</label>
<div [ngSwitch]="question?.controlType">
<input
ngDefaultControl
*ngSwitchCase="'textbox'"
[formControlName]="question?.key"
[id]="question?.key"
[type]="question?.type"
[value] = "question?.value"
/>
<div [ngSwitch]="question?.controlType">
<ion-textarea
ngDefaultControl
*ngSwitchCase="'textArea'"
[formControlName]="question?.key"
[id]="question?.key"
autoGrow="question?.autoGrow"
[value]= "question?.value"
></ion-textarea>
</div>
<div *ngSwitchCase="'barcodeScanner'">
<app-barcode-scanner
[formControlName]="question?.key"
(ngModelChange)="question?.onChange($event)"
[id]="question?.key"
[value]="question?.value"
></app-barcode-scanner>
</div>
<div *ngSwitchCase="'datebox'">
<ion-datetime [formControlName]="question?.key" [id]="question?.key">
</ion-datetime>
</div>
<div *ngSwitchCase="'switchBox'">
<p class="switchText">
{{ getValue ? question.labelTrue : question.labelFalse }}
</p>
<ion-icon
[name]="getValue ? question.iconTrue:question.iconFalse"
></ion-icon>
<ion-toggle
[formControlName]="question?.key"
[id]="question?.key"
></ion-toggle>
</div>
<div
[formControlName]="question?.key"
[id]="question?.key"
>
<div *ngSwitchCase="'itemSelector'"
[formControlName]="question?.key"
[id]="question?.key"
ngDefaultControl
>
<app-selector-items
[formControlName]="question?.key"
[id]="question?.key"
[text]="question?.text" [service]="question?.service" (selectedItem)="question.selectedItem($event)"
[sorterFunction]="question?.sorterFunction"
[filterFunction]="question?.ItemsFilterFunction"
[createPopup]= "question?.createPopup"
[item]="question?.value"
(selectedItem)="question.selectedItem($event)" >
</div>
<select
[id]="question?.key"
*ngSwitchCase="'dropdown'"
[value]="question?.value"
[formControlName]="question?.key"
>
<option *ngFor="let opt of question?.options" [value]="opt?.value">{{
opt.key
}}</option>
</select>
<div *ngSwitchCase="'geobox'">
<input-geolocation
[id]="question?.key"
[address]="question.value?.adress"
[formControlName]="question?.key"
(ngModelChange)="question?.onChange($event)"
></input-geolocation>
</div>
{{ question ? question.label : "" }} is required
this is my question-form-component.ts:
import {
Component,
Input,
OnInit,
Output,
EventEmitter,
ChangeDetectionStrategy,
ɵConsole,
OnChanges,
SimpleChanges
} from '#angular/core';
import { FormGroup, FormControl } from '#angular/forms';
import { QuestionBase } from '../../models/question-base';
#Component({
selector: 'app-question',
templateUrl: './question-form-component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class QuestionFormComponent implements OnInit, OnChanges {
public value: any;
#Input() question: QuestionBase;
#Input() form: FormGroup;
ngOnInit() {
this.value = this.question ? this.question.value : undefined;
this.form = this.form
? this.form
: new FormGroup({
// I need an instance of formgroup for run the tests
name: new FormControl(this.question.key),
value: new FormControl(this.question.value)
});
}
ngOnChanges(changes: SimpleChanges) {
}
get isValid() {
return this.question ? this.form.controls[this.question.key].valid : false;
}
get getValue() {
return this.question.key ? this.form.get(this.question.key).value : '';
}
getIcon() {
return this.form.get(this.question.key).value ? String(this.question.iconTrue) : String(this.question.iconFalse)
}
}
thanks in advance

Custom Control not updating parent form validation

I've been working on adding a custom control to a form, I would like to do this because I know we have multiple components that may make up a form.
So for example you may have something like in app.components.ts:
import { Component, OnInit, AfterViewInit } from '#angular/core';
import { FormGroup, FormBuilder, FormControl, Validators, AbstractControl } from '#angular/forms';
import { Model } from './cool-array/cool-array.component';
#Component({
selector: 'app-root',
styleUrls: ['./app.component.css'],
template:`
<!--The content below is only a placeholder and can be replaced.-->
<div class="col-md-6">
<form [formGroup]="form" >
<div class="form-group">
<label >Name</label>
<input formControlName="name" type="text" class="form-control" >
</div>
<app-cool-array formControlName="items"></app-cool-array>
</form>
</div>
<div class="col-md-6">
<div class="row">
IsValid Form: <strong> {{form.valid}}</strong>
Total Errors: <strong>{{form.errors? form.errors.length:0}}</strong>
</div>
<div class="row">
<table class="table table-stripped">
<thead>
<tr>
<th>Error</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let error of form.errors">
</tr>
</tbody>
</table>
</div>
</div>
`
})
export class AppComponent implements OnInit, AfterViewInit {
/**
*
*/
constructor(private formBuilder:FormBuilder) {
}
form:FormGroup;
model:MyModel;
ngOnInit(): void {
this.model = new MyModel();
this.form = this.formBuilder.group({ });
this.form.addControl('name', new FormControl('', Validators.required));
this.form.addControl('items', new FormControl([],[(control)=>MyValidator.MustHaveOne(control) ]))
}
ngAfterViewInit(): void {
console.log(this.form.errors)
}
}
export class MyValidator{
static MustHaveOne(control:AbstractControl){
if(control.value.length === 0) return {'length':'Items Must Have at least 1 item'};
return null;
}
}
export class MyModel{
name:string='';
items:Model[]=[];
}
You also may want to add the child component:
import { Component, OnInit } from '#angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '#angular/forms';
#Component({
selector: 'app-cool-array',
styleUrls: ['./cool-array.component.css'],
providers:[
{
provide:NG_VALUE_ACCESSOR,
multi:true,
useExisting:CoolArrayComponent
}
],
template:`
<button (click)="onAdd()" class="btn btn-primary" >Add</button>
<button (click)="onRemove()" class="btn">Remove</button>
<table class="table table-striped table-responsive">
<thead>
<tr>
<th>Something Required</th>
<th>Something Not Requred</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of items" (click)="onSelectedItem(item)" [ngClass]="{'selected-item':item === selectedItem}">
<td><input class="form-control" required [(ngModel)]="item.somethingRequired"/></td>
<td><input class="form-control" [(ngModel)]="item.somethingNotRequired"/></td>
</tr>
</tbody>
</table>
`
})
export class CoolArrayComponent implements OnInit, ControlValueAccessor {
onChange:any = ()=>{};
onTouched: any = () => { };
writeValue(obj: any): void {
this.items = obj;
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
}
disabled:boolean=false;
items:Model[]=[];
selectedItem:Model;
constructor() { }
ngOnInit() {
}
onAdd():void{
this.items.push(new Model());
}
onRemove():void{
let index = this.items.indexOf(this.selectedItem);
if(index>-1)
this.items.splice(index, 1);
}
onSelectedItem(event){
this.selectedItem = event;
}
}
export class Model{
somethingRequired:string;
somethingNotRequired:string;
get isValid():boolean{
return this.somethingRequired !==undefined && this.somethingRequired.length>0;
}
}
When the child component becomes invalid this should set the form to be invalid.
I have tried adding a CustomValidator, however, it is never fired when the values change in the underlying array.
Can someone please explain why this is?
Okay, so I had to do a few things here to get this to work I will not post the entire code just the relevant parts.
I had to do is add a new custom Validator
static ArrayMustBeValid(control:AbstractControl){
if(control.value){
if(control.value.length>0){
let items:Model[] = control.value;
let invalidIndex = items.findIndex(x=> !x.isValid);
if(invalidIndex === -1) return null;
return {'array': 'array items are not valid'};
}
}
return {'array':'array cannot be null'};
}
Then I had to add an update event on the input this should fire on keyup
onUpdate(){
this.onChange(this.items);
}
Had to add the validator to FormControl in app.component.ts
this.form.addControl('items', new FormControl([],[(control)=>MyValidator.MustHaveOne(control), (control)=>MyValidator.ArrayMustBeValid(control) ]))

How to validate a form on button submit

I have a form, which is getting validated once the control gets the class change from ng-untouched ng-pristine ng-invalid to ng-pristine ng-invalid ng-touched but if i have a form with more controls then i want the user to know which field he/she has missed out on the button submit. how can i do that using angularJS2. I have used ReactiveFormsModule to validate the controls
The following is my code: component
import { Component } from '#angular/core';
import { FormBuilder, Validators, FormGroup, FormControl } from '#angular/forms';
#Component({
selector: 'page',
templateUrl:'../app/template.html'
})
export class AppComponent {
registrationForm: FormGroup;
constructor(private fb: FormBuilder) {
this.registrationForm = fb.group({
username: ['', [Validators.required, Validators.minLength(4)]],
emailId: ['', [Validators.required, this.emailValidator]],
phonenumber: ['', [Validators.required, this.phoneValidation]]
})
}
emailValidator(control: FormControl): { [key: string]: any } {
var emailRegexp = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+#[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
if (control.value && !emailRegexp.test(control.value)) {
return { invalidEmail: true };
}
}
phoneValidation(control: FormControl) {
if (control['touched'] && control['_value'] != '') {
if (!/^[1-9][0-9]{10}$/.test(control['_value'])) {
return { 'invalidPhone': true }
}
}
}
}
The following is my code: module
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { ReactiveFormsModule } from '#angular/forms';
import { AppComponent } from '../app/component';
#NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
The following is my code: template
<form [formGroup]="registrationForm" (ngSubmit)="registrationForm.valid && submitRegistration(registrationForm.value)">
<input type="text" formControlName="username" placeholder="username" />
<div class='form-text error' *ngIf="registrationForm.controls.username.touched">
<div *ngIf="registrationForm.controls.username.hasError('required')">Username is required.</div>
</div>
<br />
<input type="text" formControlName="emailId" placeholder="emailId" />
<div class='form-text error' *ngIf="registrationForm.controls.emailId.touched">
<div *ngIf="registrationForm.controls.emailId.hasError('required')">email id is required.</div>
<div *ngIf="registrationForm.controls.emailId.hasError('invalidEmail')">
Email is not in correct format.
</div>
</div>
<br />
<input type="text" formControlName="phonenumber" placeholder="phonenumber" />
<div class='form-text error' *ngIf="registrationForm.controls.phonenumber.touched">
<div *ngIf="registrationForm.controls.phonenumber.hasError('required')">phone number is required.</div>
<div *ngIf="registrationForm.controls.phonenumber.hasError('invalidPhone')">Invalid phone number.</div>
</div>
<br />
<input type="submit" value="Submit" />
</form>
I thought of updating all the invalid controls class to ng-untouched ng-pristine ng-invalid but not sure if this is the right approach
Angular forms don't provide ways to specify when to do validation.
Just set a flag to true when the form is submitted and show validation warnings only when the flag is true using *ngIf="flag" or similar.
<form [formGroup]="registrationForm" (ngSubmit)="submitRegistration(registrationForm.value)">
<div *ngIf="showValidation> validation errors here </div>
showValidation:boolean = false;
submitRegistration() {
if(this.registrationForm.status == 'VALID') {
this.processForm();
} else {
this.showValidation = true;
}
}

Angular 2 - Required field validation if checkbox is selected

Hy guys I'm new to Angular2 and in JS frameworks in general. I'm flowing tutorials on official site and haven't been able to find the solution to this problem.
So I have checkbox which is optional but if the checkbox is "checked" a new input field is shown. this part is not a problem. The problem is that I'm using modal based validation and I can't figure out how to make this new input field required only if the checkbox is checked.
this is may implementation so far:
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<!--{{form}}-->
<div formGroupName="test">
<div class="field">
<div class="checkbox">
<input type="checkbox" name="entryRecurring" value="" id="entryRecurring" formControlName="entryRecurring" />
<label for="entryRecurring">
<div class="checkbox_icon"></div>
Recurring Entry
</label>
</div>
</div>
<div *ngIf="form.value.test.entryRecurring">
<div class="field">
<label for="entryRecurringAmount">Repeat Amount</label>
<input type="text" name="entryRecurringAmount" value="" id="entryRecurringAmount" formControlName="entryRecurringAmount" />
</div>
</div>
</div>
<div class="field last">
<button name="submit" id="submit" class="btn btn_sushi" [disabled]="!form.valid">Submit</button>
</div>
import {Component, Input, OnInit, OnChanges} from '#angular/core';
import { Validators } from '#angular/common';
import { REACTIVE_FORM_DIRECTIVES, FormGroup, FormControl, FormBuilder } from '#angular/forms';
import { FormMessages } from './../helpers/formMessages.component';
import {EntriesService} from './entries.service';
import {ValidationService} from '../helpers/validation.service';
import {Category, CategoryByType} from '../../mock/mock-categories';
#Component({
selector: 'entryForm',
templateUrl: 'app/components/entries/entriesEdit.template.html',
directives: [REACTIVE_FORM_DIRECTIVES, FormMessages],
providers: [EntriesService, ValidationService]
})
export class EntriesEditComponent implements OnInit, OnChanges {
#Input() control: FormControl;
public form:FormGroup;
public submitted:boolean = false;
// private selectedId: number;
categories: Category[];
categoriesSortedByType: CategoryByType[];
constructor(
private _fb:FormBuilder,
private _entriesService: EntriesService
// private _router: Router
) {
this.form = this._fb.group({
test: this._fb.group({
entryRecurring: [''],
entryRecurringAmount: [''],
})
});
}
onSubmit() {
this.submitted = true;
// console.log(this.form.value);
if (this.form.dirty && this.form.valid) {
this._entriesService.saveEntry(this.form.value);
}
}
You could do that by using a custom validation service.
import {NgFormModel} from "angular2/common";
import {Component, Host} from 'angular2/core';
#Component({
selector : 'validation-message',
template : `
<span *ngIf="errorMessage !== null">{{errorMessage}}</span>
`,
inputs: ['controlName : field'],
})
export class ControlMessages {
controlName : string;
constructor(#Host() private _formDir : NgFormModel){
}
get errorMessage() : string {
let input = this._formDir.form.find(this.controlName);
let checkBx = this._formDir.form.find('checkBoxName');
if(input.value.trim() === '' && checkBx.checked) {
return 'The input field is now required'
}
return null;
}
}
Then use the new component like bellow
<div *ngIf="form.value.test.entryRecurring">
<div class="field">
<label for="entryRecurringAmount">Repeat Amount</label>
<input type="text" name="entryRecurringAmount" value="" id="entryRecurringAmount" ngControl="entryRecurringAmount" />
<validation-message field="entryRecurringAmount"></validation-message>
</div>
</div>
Hope that helped!

redux-form data is not passed to handleSubmit

I'm having trouble again with redux-form. I'm calling the handleSubmit function from the parent, and the windows.alert() is calledcorrectly, but the data is not passed to the function. What am I doing wrong?
import React, {Component, PropTypes} from 'react';
import {reduxForm} from 'redux-form';
import memberValidation from './memberValidation';
class DashboardAdding extends Component {
static propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired,
resetForm: PropTypes.func.isRequired
}
render() {
const {
fields: { pseudo, email},
handleSubmit,
resetForm
} = this.props;
return (
<div>
<form className="form-horizontal" onSubmit={handleSubmit.bind(this)}>
<div className={'form-group' + (pseudo.error && pseudo.touched ? ' has-error' : '')}>
<label className="col-sm-2">Pseudo</label>
<div className={'col-sm-8 '}>
<input type="text" className="form-control" id="pseudo" {...pseudo}/>
{pseudo.error && pseudo.touched && <div className="text-danger">{pseudo.error}</div>}
</div>
</div>
<div className={'form-group' + (email.error && email.touched ? ' has-error' : '')}>
<label className="col-sm-2">Email</label>
<div className={'col-sm-8 '}>
<input type="text" className="form-control" id="email" {...email}/>
{email.error && email.touched && <div className="text-danger">{email.error}</div>}
</div>
</div>
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10">
<button className="btn btn-success" onClick={handleSubmit}>
<i className="fa fa-paper-plane"/> Submit
</button>
<button className="btn btn-warning" onClick={resetForm} style={{marginLeft: 15}}>
<i className="fa fa-undo"/> Reset
</button>
</div>
</div>
</form>
</div>
);
}
}
export default reduxForm({
form: 'dashboardForm',
fields: ['pseudo', 'email'],
validate: memberValidation,
asyncBlurFields: ['email']
})(DashboardAdding);
...and the parent calling the handleSubmit:
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import Helmet from 'react-helmet';
import {bindActionCreators} from 'redux';
import {initialize} from 'redux-form';
import {isLoaded, loadMembers} from 'redux/modules/members/members';
import * as addActions from 'redux/modules/members/addSingleMember';
import {addMember} from 'redux/modules/members/addSingleMember';
import { DashboardList } from 'components';
import { DashboardHeader } from 'components';
import { DashboardAdding } from 'components';
import { asyncConnect } from 'redux-async-connect';
#asyncConnect([{
deferred: true,
promise: ({store: {dispatch, getState}}) => {
if (!isLoaded(getState())) {
return dispatch(loadMembers());
}
}
}])
class Dashboard extends Component {
static propTypes = {
members: PropTypes.array,
error: PropTypes.string,
loading: PropTypes.bool,
addMember: PropTypes.func,
initialize: PropTypes.func.isRequired
}
handleSubmit = (data, dispatch) => {
window.alert(data);
dispatch(addMember(JSON.stringify(data)));
this.props.initialize('dashboardForm', {});
}
handleInitialize = () => {
this.props.initialize('dashboardForm', {
pseudo: 'Pibo',
email: 'pibirino#gmail.com'
});
}
render() {
const {members} = this.props;
return (
<div className="container">
<h1>Dashboard</h1>
<Helmet title="Dashboard"/>
<DashboardHeader />
<div>
<DashboardList members={members}/>
<h3>Ici commence le form</h3>
<div style={{textAlign: 'center', margin: 15}}>
<button className="btn btn-primary" onClick={this.handleInitialize}>
<i className="fa fa-pencil"/> Initialize Form
</button>
</div>
</div>
<DashboardAdding onSubmit={this.handleSubmit}/>
</div>
);
}
}
function mapStateToProps(state) {
return {
members: state.members.data,
error: state.members.error,
loading: state.members.loading
};
}
function matchDispatchToProps(dispatch) {
return bindActionCreators({
addActions,
addMember,
initialize: initialize
}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(Dashboard);
The redux-documentation says:
"You are upgrading from a previous version of redux-form that required that {valid: true} be returned".
I suspect that the problem is that, but I really don't understand what that could mean!
My version -> "redux-form": "^3.0.0"
Thanks!
Find the solution where I didn't expect... The form gives the data in json format. JSON.stringify() messed it up.
I hope it can help somebody. Bye

Resources