Custom validation in react-bootstrap forms not working - css

I'm trying to add custom validation logic to my react-bootstrap forms, but isInvalid doesn't seem to be respected. As soon as validated={true} is set on the form, it always validates non-empty strings, regardless of isValid or isInvalid. The invalid message is displayed, but the field shows as green. See here:
Here is the offending code:
<Form noValidate validated={formValidated} id="bidForm">
<Form.Group className="mb-3" controlId="formBasicBidding">
<Form.Label>Bid Amount</Form.Label>
<InputGroup hasValidation className="mb-3">
<InputGroup.Text id="inputGroupPrepend">$</InputGroup.Text>
<Form.Control
required
value={bidAmount}
isInvalid={!(parseInt(bidAmount) > 0) && formValidated}
onChange={e => setBidAmount(e.target.value)}
/>
<InputGroup.Text id="inputGroupAppend">.00</InputGroup.Text>
<Form.Control.Feedback type="invalid">
Please select a dollar amount {`>`} 0
</Form.Control.Feedback>
</InputGroup>
</Form.Group>
<...>
</Form>
I was able to get it to work using a pattern= regex on the input, but I'm planning to do more complex validation where a regex won't be sufficient. How can I get react-bootstrap to follow isValid and isInvalid properly? Thanks!

The docs don't do a great job of making this clear, but the form-level validated attribute hooks directly into HTML5's native validation library and can't be used simultaneously with other validation techniques. So if you're using the native option and an input doesn't violate any of the native validation options (required, pattern, etc), then the input will be considered valid. Since you don't have any of the native validation attributes besides required on your bid input, "foo" is valid as far as React-Bootstrap is concerned.
(If you're thinking 'well that's bad code design to let you use both simultaneously', I'd tend to agree with you.)
You'll note that with your current code, if you enter "foo" and submit the form the element actually does have an is-invalid class applied to it, but it's also got the :valid psuedo-selector applied to it because the entire form was validated, which seems to take precedence from a CSS perspective:
The best solution here is to use either the native HTML5 validation option (detailed here) or roll your own (with or without a helper library like Formik), but not both simultaneously.
If you're trying to avoid validating until the input is actually in a "dirty" state (e.g. the user filled it out or submitted the form), you can do something like this (CodePen here):
<Form.Control
required
value={bidAmount}
isInvalid={!(parseInt(bidAmount) > 0) && (bidAmount.length || formSubmitted)}
onChange={(e) => setBidAmount(e.target.value)}
/>

Related

Disable submit until all fields are valid in reactstrap

I have a form in reactstrap that has several input fields that uses FormFeedback like this:
<Input invalid={typeof data.name === "undefined" || data.name.length<1} bssize="sm" type="text" name="name" id="name" placeholder="Name" value={data.name} onChange={this.props.handleInputChange} />
<FormFeedback >A name is required</FormFeedback>
<Button color="primary" onClick={this.save} disabled={!this.state.okToSubmit}>Submit</Button>
Is it possible to have the submit button of the form disabled until all fields are validating ok?
I canĀ“t find any way to access the "invalid" prop of a field. The closest I have come so far is to look at the classList of target in the handleInputChange-function. But that feels very hacky and not the best way.
Quite new to React so all help is really appreciated.
There are a number of ways you can accomplish this: Since you are using onChange event handler, you can set another state var.
So, for example, say you have five form elements that you want to have be required. Every time an element is validated, increase that state var by one. Then add a conditional for the button to be disabled or not by disabled={this.state.okToSubmit != 5} (this can also be done using hooks if you're using a functional component.
Another option would be to keep the button live, and do all the validation within the onSubmit handler, but I think most modern day UX is to validate on a per element basis.

Redux Form Fields Component and Validation

I am using the redux to hide and show components based on a value.
The Redux form documentation mentions the following:
Connecting to multiple fields should be used sparingly, as it will require the entire component to re-render every time any of the fields it is connected to change. This can be a performance bottleneck. Unless you absolutely need to, you should connect to your fields individually with .
I am unclear if my solution to hiding and showing fields based on radio buttons is good enough to use Fields giving the warning to use sparingly.
Can you please clarify if my component merits enough reason to use Fields. If not, what is an alternative way to implement?
Also, how does fields implement validations?
<div>
<form>
<Fields
component={RadioButtonGroupField}
names={['radioButtonGroup', 'nameTextField', 'nickNameTextField']}
/>
</ form>
</div>
function RadioButtonGroupField(fields) {
return(
<div>
<RadioButtonGroupComponent
{...fields.radioButtonGroup.input}
{...fields.radioButtonGroup.meta}
/>
{
(fields.radioButtonGroup.input.value === 'name' ||
fields.radioButtonGroup.input.value === 'both') &&
<NameTextFieldComponent
{...fields.radioButtonGroup.input}
{...fields.radioButtonGroup.meta}
/>
}
{
(fields.radioButtonGroup.input.value === 'nickname' ||
fields.radioButtonGroup.input.value === 'both') &&
<NicknameTextFieldComponent
{...fields.radioButtonGroup.input}
{...fields.radioButtonGroup.meta}
/>
}
</div>
);
}
There is another way you could do that, selecting the specific value using redux-form selectors (http://redux-form.com/6.0.5/docs/api/Selectors.md/) from the redux store in your mapStateToProps and then conditionally rendering certain components.
However, I think that Fields is exactly what you should use in this circumstance. I think that warning is largely to warn people not to go and put their entire form into Fields, having those 3 fields rerender is no big deal.
The thought process that led to the creation of Fields in the first place is probably the best way to get a handle on this: https://github.com/erikras/redux-form/issues/841

Enable autocomplete feature in aurelia single page application

We have enabled autocomplete property true for all input fields. We didn't use form tags in the templates. The input fields don't fetch the previously entered data. So how can we implement autocomplete property.
Firstly, this is not specific to Aurelia. Once the element is in the DOM, it is a feature of the browser to offer the user a previously entered value for that field given that an assumption can be made about what the field is supposed to be!).
Depending on the browser, the autofill feature relies on having 'known' input attributes (name and type) and possibly even the surrounding text, including label text.
If you are not getting the expected results, try making sure your inputs have very obvious name attributes, first. Eg.
<input type="text" name="email">
If you could share a snippet of code, I might be able to offer more help.

Refactoring potentially complex template

Let supplier be documents (with code, names, and many other fields).
I have a component
export class SuppliersDetails extends MeteorComponent {
supplier: any;
invalidKeys: Object; // array <key> => <error message>
and a form
<div>
<input [(ngModel)]="supplier.code" [class.invalid]="invalidKeys['code']" id="code" type="text" class="validate">
<label for="code" [class.active]="supplier.code || invalidKeys['name']" [attr.data-error]="invalidKeys['code']" >Code</label>
</div>
that allow me to edit it.
How could I refactore my component/template, to lighten my template ?
Here it's only 1 field, and only the display of invalidKeys message is handled. But I have 8 fields and some specific logic to add. This will get unreadable.
I am looking for something like
<div>
<input plsDoItAllAndUseThatId='code'></input>
<label plsDoItAllAndUseThatId='code'>Code</input>
</div>
But I have no idea of the design, any idea ?
I would suggest looking into dynamic forms as described in the cookbook section of angular2 docs. The key here is to separate the business logic out of the form itself, such as by creating:
A questions object that will hold all the input properties
A service that will create all the generate all the questions needed by a specific form
A generic component that will loop through a list of questions and bind all the question properties to the input
This would be a good time you could use an attribute directive.
https://angular.io/docs/ts/latest/guide/attribute-directives.html#!#write-directive
You could write it as an attribute like you did in what you want to do. With that you can manipulate the element in the directive to add other attributes if you want or do whatever.
That would make it pretty slick. I'm a fan of this kind of stuff.
Lots of cool stuff you can do with that if you get creative.

Referencing Ractive component's text

I want to enable a button when two text fields have length > 0: How do I refer to these text fields lengths to express this? Seems simple, but its not obvious to me how to refer to the component's and their text (length). I basically want to use FRP to enable/disable button for form submission. These would be "sibling" components I suppose.
If two-way binding is an option, you could do something along these lines:
<input value='{{foo}}'>
<input value='{{bar}}'>
<button disabled='{{ !foo || !bar }}'>submit</button>
This works because the empty string ('') is falsy in JavaScript, so !foo || !bar is only false when both foo and bar are non-empty.
#Rich Harris: It seems that the problem with the <button disabled="{{ !(''+foo) || !(''+bar) }}">submit</button> trick is that the button is enabled at the start, because both foo and bar are initially undefined and ''+undefined gives the string 'undefined'. The technique also becomes unwieldy if you have several fields with various validation requirements.
As you may know, Angular has a $invalid that you can apply to a form, e.g., <button ng-disabled="myForm.$invalid">submit</button> where myForm is the form's name attribute. In Ractive, I guess one could write an isvalid function and then use <button disabled="{{ !isvalid() }}">submit</button> but that only works for one form (or is there some way to "attach" it to a particular element?).

Resources