How to add debounce time for list of input tags in reactive form angular 9 and detect which input tag getting type? - angular-reactive-forms

I have a list of inputs using ngFor + input I am displaying it as below
<div*ngFor="let esControl of extraServicesControls.controls; let i = index; last as isLast; first as isFirst" [formGroup]="esControl">
<input
type="number"
name=""
id=""
class="form-control form-control-sm"
aria-label="Enter insured amount"
[value]="
esControl.value.specialService?.inputParameters &&
(esControl.value.specialService?.inputParameters)[0]?.value
"
(keyup)="valuechangeInput($event.target.value, esControl.value.specialService)"
/>
</div>
How to call API on each input in list with debounce time of 1000 ms when type letter any input

For this issue I am suggesting to use angular reactive forms.
In order to user Angular reactive forms
Import reactive forms module to your module
import { ReactiveFormsModule } from '#angular/forms';
#NgModule({
imports: [
// other imports ...
ReactiveFormsModule
],
})
export class AppModule { }
In the ts file create a new formGroup
import { debounceTime} from 'rxjs/operators';
import { FormBuilder } from '#angular/forms';
constructor(private fb:FormBuilder){
this.form= this.fb.group({
insertAmount: ''
});
//Then subscribe for valuechange
this.form.valueChanges.pipe(debounceTime(500)).subscribe((val)=>{
there is a new value for the input. Check logic here
})
}
In this code the 500 means the amount of ms the stream has to wait before emitting a new value. In the mean time all the new value will be discarded.
More detail about reactive forms:https://angular.io/guide/reactive-forms
More detail about RXJS debounceTime operator https://www.learnrxjs.io/learn-rxjs/operators/filtering/debouncetime
The reactive form is a pretty good kind of approach to achieve such debounceTime functionality.
The simpler approach is similar to what you did
html
<input
type="number"
name=""
id=""
class="form-control form-control-sm"
aria-label="Enter insured amount"
[value]="
esControl.value.specialService?.inputParameters &&
(esControl.value.specialService?.inputParameters)[0]?.value
"
(keyup)="valuechangeInput($event.target.value,i)"
/>
</div>
.ts
import {Replaysubject} from "rxjs"
private inputValue: Replaysubject<string[]>= new Replaysubject()
constructor(private fb:FormBuilder){
this.inputValue.pipe(debounceTime(500)).subscribe((val)=>{
there is a new value for the input. Check logic here
})
}
valuechangeInput(value:string,index:number){
this.inputValue.next({value,index});
}

Related

Reading form inputs?

How would you read the input value ?
On the reacjs site I see very complicated way !!
https://reactjs.org/docs/forms.html
I just want to read the value and submit it via ajax fetch() request. I.e. I don't need to manage bindings, events and such ...
The easiest way by far to read values from html controls is by using an event handler.
export default class myComponent extends Component {
person = {};
onChange = field => e => {
this.person[field] = e.target.value;
};
render() {
return (
<Input
id="firstName"
name="firstName"
autoComplete="firstName"
autoFocus
onChange={this.onChange('FirstName')}
/>
);
}
}
In the above code snippet we are basically telling react to fire the onChange member on an update of firstName control update. Our method will receive an event e, that has a handle to our control and we can basically probe it's value member to get what's typed in (much like jquery's $('#element').value()).
Why is it the easiest method? because it's generic enough to allow you to handle multiple inputs in a react component. Notice that, I'm also instructing React to pass me the control name in addition to the event, and using this method I can basically know exactly which of my inputs (in case of multiple) caused the event to fire.
Reading user input value is feasible and recommended via event handlers.
Below example would explain how to read input value and send it to the backend via fetch when Form is submitted
class Test extends Component{
constructor(props){
super(props);
this.state = {
name: “”
}
}
handleChange = event => {
this.setState({name: event.target.value});
}
handleSubmit = () => {
//send the value via fetch backend I.e., this.state.name
}
render(){
const { name } = this.state;
render(
<form onSubmit={this.handleSubmit}
<label>
Name:
<input type="text" value={name} onChange={this.handleChange} name="name" />
</label>
<input type="submit" value="Submit" />
</form>
)
}
}

What is the good way to have a multi input field in Redux Form?

In my project we are building a form with React and Redux-Form. We have a single information that is composed by the value of two inputs. But the values of each input is combined and validated together.
The first implementation try was by connecting each component with Field. It let us update the state properly but we couldn't validate all the values together with the validate prop.
The second Try was using Fieldscomponent but it does not have a validate prop. We thought in create a Pull Request for it but the API for it isn't clear yet, since what we want to validate the combination of the two values and the behavior of the Fields props (such as parse and format) is different, executing the function for each input inside Fields component separately.
I know it is possible to create a component and use Field to connect with the application state, but I didn't want to manage things as the touched prop, or the callbacks to update the state, or other things that I even have noticed, since Redux-Form has all of it done.
The fact is that I end up with an implementation but it didn't looked very elegant. I'd like you to take a look at the implementation and give your opinion, sugest other solutions and even if this solution is not implemented in Redux-Form yet we could maybe open a pull request for that.
Here is an example implementation
Simple form container
import SimpleForm from 'app/simpleForm/components/simpleForm'
import { reduxForm } from 'redux-form'
export default reduxForm({
form: 'simpleForm'
})(SimpleForm)
Simple form component
import React from 'react'
import { Field } from 'redux-form'
import MultiInputText from 'app/simpleForm/components/multiInputText'
const onSubmit = values => alert(JSON.stringify(values))
const validateAddress = (value) => {
if (!value) return 'Address is empty'
if (!value.street) return 'Street is empty'
if (!value.number) return 'Number is empty'
return null
}
const SimpleForm = ({ handleSubmit }) => {
return (
<form onSubmit={ handleSubmit(onSubmit) }>
<Field label="Home Address" name="home" component={MultiInputText} type="text" validate={validateAddress}/>
<Field label="Work Address" name="work" component={MultiInputText} type="text" validate={validateAddress}/>
<button type="submit">Submit</button>
</form>
)
}
export default SimpleForm
MultiInputText component
import React from 'react'
import { Fields, FormSection } from 'redux-form'
const renderAddreessInputs = ({ street, number, error }) => (<div>
<input {...street.input} type="text" />
<input {...number.input} type="text" />
{ street.meta.touched && number.meta.touched && error && <span className="error">{error}</span> }
</div>)
const MultiInputText = (props) => {
const { input: { name }, label, meta: { error }} = props
const names = [
'street',
'number'
]
return (<div>
<label htmlFor={name}>{label}</label>
<FormSection name={name}>
<Fields names={names} component={renderAddreessInputs} error={error}/>
</FormSection>
</div>)
}
export default MultiInputText
I see two options:
1) Use record-level validation.
reduxForm({
form: 'addressForm',
validate: values => {
const errors = {}
if(!home) {
errors.home = 'Address is empty'
}
// etc, etc. Could reuse same code for home and work
return errors
}
})
2) Create a single input that handles a complex value.
<Field name="home" component="AddressInput"/>
...
const AddressInput = ({ input, meta }) =>
<div>
<input
name={`${input.name}.street`}
value={(input.value && input.value.street) || ''}
onChange={event => input.onChange({
...input.value,
street: event.target.value
})}/>
...other inputs here...
</div>
That's total pseudocode, but I hope it gets the point across: a single input can edit a whole object structure.
Personally, I'd choose Option 1, but I prefer record-level validation over field-level validation in general. The nice thing about Option 2 is that a single AddressInput could be reused across the application. The downside is that you don't get specific field-level focus/blur/dirty/pristine state.
Hope that helps...?

asyncValidation on key strokes and throttled (not on blur)

The redux-form asyncValidation is great but it only works on blur. Is it possible to make it happen during key presses, and throttled? So it runs only every 300ms and on final value?
Is it possible? The short answer is yes.
Is it tricky? Well, as you mentioned, theres the fact that the asyncValidation for redux-form option only works for the onBlur and you'd instead want it to work onChange.
So you could fork and add this feature into redux-form, so you can do this:
#reduxForm({
form: 'login',
asyncValidate: validator,
//asyncBlurFields: ['username','password'],
asyncChangeFields: ['username','password'], /* add this new option */
})
For the deboucing part, you'd want to somehow debounce the onChange handler rather than the validate function itself, which brings up another option...
redux-form exports its internal action creators which may be enough to hack it together. Particularly, there's the stopAsyncValidation action creator that lets you pass field-level async errors directly into a form. Pair that with the onChange prop for Field, and you actually have the right pieces to get it done like this:
import React from 'react'
import { Field, reduxForm, stopAsyncValidation } from 'redux-form'
import _debounce from 'lodash.debounce'
import renderField from './renderField'
#reduxForm({form: 'debouncedOnChangeValidation'})
class DebouncedOnChangeValidationForm extends React.Component {
customValidate = () => {
const { form, dispatch } = this.props
dispatch(stopAsyncValidation(form, { username: "thats wrong..." })) /* pass in async error directly! */
}
debounceValidate = _debounce(this.customValidate, 1000) /* decorate with debounce! */
render() {
const { handleSubmit, pristine, reset, submitting} = this.props
return (
<form onSubmit={handleSubmit}>
<Field name="username" type="text"
component={renderField} label="Username"
onChange={this.debounceValidate} /* bind validation to onChange! */
/>
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}
}
Additionally, to access the form values for performing validation, you'd need to use the getFormValues selector.
Of course this won't be as robust as a more built-in solution, but it may work well enough for some use cases.

Redux-Form: Not able to change value of input elements

I've been trying to implement a form in MapsAddrForm.jsx using Redux-Form and I can't seem to change the value of my input element. When the page loads, the input element does not respond to keyboard input, and when the form field submits, it returns an empty object to the parent component DistrictFinder. Beyond these two files, I've also added form:formReducer as an argument to combineReducers much like the simple example in the Redux-Form tutorials. Is there any way to restore the ability for the DistrictFinder to receive data objects from the address form? For reference, I'm using React 15.1.0, React-redux 4.4.5, ES6, and Redux-Form 5.3.1, all compiled using Webpack.
MapsAddrForm.jsx
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {reduxForm} from 'redux-form';
class MapsAddrForm extends Component {
constructor(props) {
super(props);
}
render() {
const {fields: {address,address2}, handleSubmit} = this.props;
return (
<form onSubmit={handleSubmit}>
<div>
<input type="text" placeholder="Your address" {...address}/>
</div>
<button type="submit">Submit</button>
</form>
);
}
}
export default reduxForm({
form: 'addressForm',
fields: ['address']
})(MapsAddrForm);
DistrictFinder.jsx
import React, { Component, PropTypes } from 'react'
import MapsAddrForm from './MapsAddrForm.jsx'
import { connect } from 'react-redux'
import { changeAddress } from '../actions/index.jsx'
class DistrictFinder extends Component {
constructor(props) {
super(props);
this.handleAddrSubmit = this.handleAddrSubmit.bind(this);
}
handleAddrSubmit(data) {
console.log("Address received: " + JSON.stringify(data));
}
render() {
const {address, district} = this.props
return (
<div class="GMaps">
<h1>Find your district!</h1>
<MapsAddrForm onSubmit={this.handleAddrSubmit} />
<p>My district number is: {district}</p>
</div>
);
}
}
DistrictFinder.propTypes = {
district: PropTypes.string.isRequired,
dispatch: PropTypes.func.isRequired
};
function mapStateToProps(state) {
const { district } = state.infoChange;
return {
district
};
};
export default connect(mapStateToProps)(DistrictFinder);
I ran into this as well on redux-form#6.2.0
After looking at the source for one of the examples, I noticed the call to combineReducers has an explicit key "form" to the object argument. When I added this explicit key, the fields became editable.
// Correct
const reducer = combineReducers({
form: reduxFormReducer // mounted under "form"
})
If you have an existing app, like I do, you likely have this style es6 syntax from the various redux starters.
// Incorrect: results in the witnessed bad behavior
const reducer = combineReducers({
reduxFormReducer
})
See the combineReducers call at https://github.com/erikras/redux-form/blob/master/examples/simple/src/index.js#L10
It'd be interesting to see if this could be a constant that could be passed in and leveraged by the lib. "form" feels like a easily corruptible "namespace".

How to add form validation pattern in Angular 2?

I have a simple form that needs to validate if the beginning and the end of the input is not space.
In HTML5, I will do this:
<input type="text" pattern="^(?!\s|.*\s$).*$">
What is the right property for validation pattern in the new Angular 2 ngControl directive? The official Beta API is still lacking documentation on this issue.
Now, you don't need to use FormBuilder and all this complicated valiation angular stuff. I put more details from this (Angular 2.0.8 - 3march2016):
https://github.com/angular/angular/commit/38cb526
Example from repo :
<input [ngControl]="fullName" pattern="[a-zA-Z ]*">
I test it and it works :) - here is my code:
<form (ngSubmit)="onSubmit(room)" #roomForm='ngForm' >
...
<input
id='room-capacity'
type="text"
class="form-control"
[(ngModel)]='room.capacity'
ngControl="capacity"
required
pattern="[0-9]+"
#capacity='ngForm'>
Alternative approach (UPDATE June 2017)
Validation is ONLY on server side. If something is wrong then server return error code e.g HTTP 400 and following json object in response body (as example):
this.err = {
"capacity" : "too_small"
"filed_name" : "error_name",
"field2_name" : "other_error_name",
...
}
In html template I use separate tag (div/span/small etc.)
<input [(ngModel)]='room.capacity' ...>
<small *ngIf="err.capacity" ...>{{ translate(err.capacity) }}</small>
If in 'capacity' is error then tag with msg translation will be visible. This approach have following advantages:
it is very simple
avoid backend validation code duplication on frontend (for regexp validation this can either prevent or complicate ReDoS attacks)
control on way the error is shown (e.g. <small> tag)
backend return error_name which is easy to translate to proper language in frontend
Of course sometimes I make exception if validation is needed on frontend side (e.g. retypePassword field on registration is never send to server).
Since version 2.0.0-beta.8 (2016-03-02), Angular now includes a Validators.pattern regex validator.
See the CHANGELOG
You could build your form using FormBuilder as it let you more flexible way to configure form.
export class MyComp {
form: ControlGroup;
constructor(#Inject()fb: FormBuilder) {
this.form = fb.group({
foo: ['', MyValidators.regex(/^(?!\s|.*\s$).*$/)]
});
}
Then in your template :
<input type="text" ngControl="foo" />
<div *ngIf="!form.foo.valid">Please correct foo entry !</div>
You can also customize ng-invalid CSS class.
As there is actually no validators for regex, you have to write your own. It is a simple function that takes a control in input, and return null if valid or a StringMap if invalid.
export class MyValidators {
static regex(pattern: string): Function {
return (control: Control): {[key: string]: any} => {
return control.value.match(pattern) ? null : {pattern: true};
};
}
}
Hope that it help you.
custom validation step by step
Html template
<form [ngFormModel]="demoForm">
<input
name="NotAllowSpecialCharacters"
type="text"
#demo="ngForm"
[ngFormControl] ="demoForm.controls['spec']"
>
<div class='error' *ngIf="demo.control.touched">
<div *ngIf="demo.control.hasError('required')"> field is required.</div>
<div *ngIf="demo.control.hasError('invalidChar')">Special Characters are not Allowed</div>
</div>
</form>
Component App.ts
import {Control, ControlGroup, FormBuilder, Validators, NgForm, NgClass} from 'angular2/common';
import {CustomValidator} from '../../yourServices/validatorService';
under class
define
demoForm: ControlGroup;
constructor( #Inject(FormBuilder) private Fb: FormBuilder ) {
this.demoForm = Fb.group({
spec: new Control('', Validators.compose([Validators.required, CustomValidator.specialCharValidator])),
})
}
under {../../yourServices/validatorService.ts}
export class CustomValidator {
static specialCharValidator(control: Control): { [key: string]: any } {
if (control.value) {
if (!control.value.match(/[-!$%^&*()_+|~=`{}\[\]:";##'<>?,.\/]/)) {
return null;
}
else {
return { 'invalidChar': true };
}
}
}
}
My solution with Angular 4.0.1: Just showing the UI for required CVC input - where the CVC must be exactly 3 digits:
<form #paymentCardForm="ngForm">
...
<md-input-container align="start">
<input #cvc2="ngModel" mdInput type="text" id="cvc2" name="cvc2" minlength="3" maxlength="3" placeholder="CVC" [(ngModel)]="paymentCard.cvc2" [disabled]="isBusy" pattern="\d{3}" required />
<md-hint *ngIf="cvc2.errors && (cvc2.touched || submitted)" class="validation-result">
<span [hidden]="!cvc2.errors.required && cvc2.dirty">
CVC is required.
</span>
<span [hidden]="!cvc2.errors.minlength && !cvc2.errors.maxlength && !cvc2.errors.pattern">
CVC must be 3 numbers.
</span>
</md-hint>
</md-input-container>
...
<button type="submit" md-raised-button color="primary" (click)="confirm($event, paymentCardForm.value)" [disabled]="isBusy || !paymentCardForm.valid">Confirm</button>
</form>
Without make validation patterns, You can easily trim begin and end spaces using these modules.Try this.
https://www.npmjs.com/package/ngx-trim-directive
https://www.npmjs.com/package/ng2-trim-directive
Thank you.

Resources