React: validation on controllable input is not consistent - html

I've 2 controllable text input with validation:
required={true}
minLength={5}
maxLength={10}
Both are share the same state, so their text are always in sync.
value={text}
onChange={(e) => setText(e.target.value)}
The initial value of them are 'ab':
const [text, setText] = useState("ab");
Then I added a css for visualizing the validation state:
input[type="text"]:valid {
background-color: lightgreen !important;
}
input[type="text"]:invalid {
background-color: pink !important;
}
The problem:
At the first load, both color are green.
The value are 'ab' it doesn't satisfy the validation.
Why not colored red?
When i added one character of the first textbox => abc
The color is red, that's to be expected.
But the second one still colored green. That's not expected.
Anyone can fix the problem? I want the text & validation are in sync.
Sandbox: enter link description here
code:
import "./styles.css";
import { useState } from "react";
import "./App.css";
export default function App() {
const [text, setText] = useState("ab");
return (
<div className="App">
<input
type="text"
required={true}
minLength={5}
maxLength={10}
value={text}
onChange={(e) => setText(e.target.value)}
/>
<br />
<input
type="text"
required={true}
minLength={5}
maxLength={10}
value={text}
onChange={(e) => setText(e.target.value)}
/>
</div>
);
}
css:
input[type="text"]:valid {
background-color: lightgreen !important;
}
input[type="text"]:invalid {
background-color: pink !important;
}

To be completely honest, I don't know why React is behaving this way, but an alternative would be to add a 'valid' or 'invalid' class to the input depending on the 'text' value:
<input
className={text.length >= 5 && text.length <= 10 ? 'valid' : 'invalid'}
type="text"
required={true}
value={text}
onChange={(e) => setText(e.target.value)}
/>
.valid {
background-color: lightgreen !important;
}
.invalid {
background-color: pink !important;
}

Related

Button changing color after closing modal

I am working on a site, and i have a modal which basically looks like this:
import React,{useState} from "react";
import './AddItem.css';
import { Form, Button,Modal } from "react-bootstrap";
function AddItem({open, setOpen})
{
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return(
<>
<Button className="add" onClick={handleShow}>
ADD
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Email address</Form.Label>
<Form.Control
type="email"
placeholder="name#example.com"
autoFocus
/>
</Form.Group>
<Form.Group
className="mb-3"
controlId="exampleForm.ControlTextarea1"
>
<Form.Label>Example textarea</Form.Label>
<Form.Control as="textarea" rows={3} />
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</>
);
}
export default AddItem;
When i click on save changes, or close the modal in any way, the add button in the picture,(which i clicked to open the modal) turns un undesirable color-
The color only reverts back to normal when i click anywhere else on the screen.
How do i prevent this from occuring? My Additem.css file:
.add{
position: absolute;
top: 21.35%;
left: 61%;
height: 6%;
width: 10%;
color: white;
background-color: #283d4a;
border-left: 1.5px solid #15aef1;
border-right: 1.5px solid #15aef1;
border-top: 2.7px solid #15aef1;
border-bottom: 2.7px solid #15aef1;
border-top-left-radius: 5px !important;
border-bottom-left-radius: 5px !important;
}
.add:hover{
background-color:#15aef1 ;
}
The color problem lies with bootstrap button. It has a property of rolling back to a color. Using a regular button like:
#React Button
<button className="add" onClick={handleShow}>
ADD
</button>
instead of
#Bootstrap button
<Button className="add" onClick={handleShow}>
ADD
</Button>
is effective in solving this.

Show a spinner until the results load from the HTTP request for autocomplete field

I want to show a spinner until the results load from the HTTP request on autocomplete field. Below is the snippet of the code written.
HTML
<label class="col-2 col-form-label text-right font-weight-bold">City *</label>
<div class="col-2">
<div>
<input id="typeahead-prevent-manual-entry" type="text" class="form-control"
[(ngModel)]="myActivity.city"
[ngbTypeahead]="search"
[inputFormatter]="formatter"
[resultFormatter]="formatter"
name="citySelected"
#citySelected="ngModel"/>
</div>
<div>
Typescript
formatter = (city: City) => city.name;
search = (text$: Observable<string>) => text$.pipe(
debounceTime(10),
distinctUntilChanged(),
filter(criterion => criterion.length >= 2),
map(criterion => criterion.length < 3 ? [] : this.searchLocalities(criterion).filter(city => new
RegExp(criterion, 'mi').test(city.name)).slice(0, 20))
);
searchLocalities(criterion: string): City[] {
this.isLoadingResult = true;
this.activityService.getLocalities(this.prvCodId, criterion).subscribe(
data => {
data.map(item => {
if (this.localities.find((city) => city.name === item.name) === undefined) {
this.localities.push(item);
}});
this.isLoadingResult = false;
});
return this.localities;
}
are you using material angular in your project? if yes you can easily use mat-proggress-spinner.
first of all import the module into your appModule:
import {MatProgressSpinnerModule} from '#angular/material/progress-spinner';
add it to your imports:
//
imports: [MatProgressSpinnerModule]
//
then in your template, you can use it as bellow:
<div class="col-2">
<mat-spinner *ngIf="isLoadingResult" diameter="30"></mat-spinner>
<div *ngIf="!isLoadingResult">
<input id="typeahead-prevent-manual-entry" type="text" class="form-control"
[(ngModel)]="myActivity.city"
[ngbTypeahead]="search"
[inputFormatter]="formatter"
[resultFormatter]="formatter"
name="citySelected"
#citySelected="ngModel"/>
</div>
<div>
but if you are not using material angular and you don't want to, you can just use a tag gif instead of mat-spinner.
put the gif in your assets and:
<img *ngIf="isLoadingResult" src="assets/images/loading.gif" />
in your html:
<div class="input-container">
<img class="loading-img" src="your loading img location">
<input type="text">
</div>
in your css:
.input-container {
display: flex;
position: relative;
width: 200px
}
.loading-img {
position: absolute;
right: 0;
top: 3px;
height: 20px;
width: 20px;
}
input {
width: 100%;
padding-right: 25px;
}
this will put the IMG in the right corner of the input.
You can use the mat-progress-bar library, see the example below.
//declaration
isApiCalling = false;
//use case
this.isApiCalling = true;
this._apiCall.get(this.url)
.subscribe(
data => {
this.isApiCalling = false;
},
err => {
this.isApiCalling = false;
}
);
<!-- actual term is isApiCalling -->
<mat-progress-bar mode="indeterminate" *ngIf="isApiCalling"></mat-progress-bar>

Checkbox overlapping checkbox label

I am trying to add checkboxes to a login page using code that I found at react-bootstrap.github.io
The problem I'm having is the checkbox is overlapping the label.
My code (register.js) is below
import React, { useState } from 'react'
import { Link } from 'react-router-dom';
import { request } from './services/Request';
import { Button, FormGroup, FormControl, FormLabel, FormCheck } from 'react-bootstrap';
//import { FormRow } from 'react-bootstrap/Form';
import "./Styles/register.css"
export const Register = () => {
return (
<div style={{display: "flex", justifyContent: "center"}}>
<h1> Register </h1>
<div className="Register">
<form onSubmit={handleSubmit}>
</FormGroup>
<FormGroup controlId="email" bsSize="large">
<FormLabel>Email</FormLabel>
<FormControl
required
autoFocus
type="email"
//value={email}
onChange={ onChangeHandlerFn }
/>
</FormGroup>
<FormGroup controlId="password" bsSize="large">
<FormLabel>Password</FormLabel>
<FormControl
required
autoFocus
type="password"
//value={password}
onChange={ onChangeHandlerFn }
/>
</FormGroup>
<FormGroup controlId="password" bsSize="large">
{['checkbox'].map((type) => (
<div key={`default-${type}`} className="mb-3">
<FormCheck
type={type}
id={`default-${type}`}
label={`I am an individual`}
/>
</div>
))}
<Button variant="primary" type="submit" block>Register</Button>
<span>
Have an account?
Sign in
</span>
<ul>
<li><Link to="/privacy">Privacy & Terms</Link></li>
</ul>
</form>
</div>
</div>
);
};
The problem is the checkboxes are overlapping their labels:
Here is my css:
#media all and (min-width: 480px) {
.Register {
padding: 60px 0;
}
.Register form {
margin: 0 auto;
max-width: 320px;
}
}
ul {
list-style-type: none !important;
margin: 0;
padding: 0;
overflow: hidden;
}
Everything works, for the sake of length, I've done post all of the code.
Any help would be greatly appreciated.
What about adding to the left padding of the label so it clears the checkbox?
label {
padding-left:15px;
}

Error message changes width of all block. How to fix it?

When the error message appears it changes the login form width.
The short message makes it shorter and long - longer. How to fix it without adding a fixed with?
I use FormHelperText element to display an error message.
Moving FormHelperText inside FormControl doesn`t fix the problem.
// some code ....
render () {
const { email, password, error } = this.state;
const isInvalid = password === '' || email === '';
return (
<Paper className={styles.paper}>
<Avatar className={styles.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component='h1' variant='h5'>
Login
</Typography>
<form onSubmit={this.onSubmit} className={styles.form}>
<FormControl margin='normal' required fullWidth>
<InputLabel htmlFor='email'>Email Address</InputLabel>
<Input
id='email'
name='email'
autoComplete='off'
autoFocus value={email}
onChange={this.onChange}
/>
</FormControl>
<FormControl margin='normal' required fullWidth>
<InputLabel htmlFor='password'>Password</InputLabel>
<Input
name='password'
type='password'
id='password'
autoComplete='off'
value={password}
onChange={this.onChange}
/>
</FormControl>
{error &&
<FormHelperText className={styles.error} error>{error.message}</FormHelperText>
}
<Button
type='submit'
fullWidth
variant='contained'
color='primary'
disabled={isInvalid}
className={styles.formSubmitBtn}
>
Login
</Button>
</form>
</Paper>
);
}
.paper {
display: flex;
flex-direction: column;
align-items: center;
}
.avatar {
margin-top: 20px;
}
.error {
}
.form {
margin: 30px 30px;
&__submit-btn {
margin-top: 20px !important;
}
}
short error message
default
There are the max-width and min-width properties in CSS that you can specify. They will ensure that the width stays between the specified sizes. If the content exceeds max-width, it affects the height of the box.

HTML/CSS Making a textbox with text that is grayed out, and disappears when I click to enter info, how?

How do I make a textbox that has a grayed out content, and when I click on it to enter text, the grayed out portion, it disappears and allows me to enter the desired text?
Example:
A "First Name" text box. The words "First Name" are inside the text box grayed out, when I click, those words disappear and I write my name in it.
Chrome, Firefox, IE10 and Safari support the html5 placeholder attribute
<input type="text" placeholder="First Name:" />
In order to get a more cross browser solution you'll need to use some javascript, there are plenty of pre-made solutions out there, though I don't know any off the top of my head.
http://www.w3schools.com/tags/att_input_placeholder.asp
This answer illustrates a pre-HTML5 approach. Please take a look at Psytronic's answer for a modern solution using the placeholder attribute.
HTML:
<input type="text" name="firstname" title="First Name" style="color:#888;"
value="First Name" onfocus="inputFocus(this)" onblur="inputBlur(this)" />
JavaScript:
function inputFocus(i) {
if (i.value == i.defaultValue) { i.value = ""; i.style.color = "#000"; }
}
function inputBlur(i) {
if (i.value == "") { i.value = i.defaultValue; i.style.color = "#888"; }
}
With HTML5, you can do this natively with: <input name="first_name" placeholder="First Name">
This is not supported with all browsers though (IE)
This may work:
<input type="first_name" value="First Name" onfocus="this.value==this.defaultValue?this.value='':null">
Otherwise, if you are using jQuery, you can use .focus and .css to change the color.
If you're targeting HTML5 only you can use:
<input type="text" id="firstname" placeholder="First Name:" />
For non HTML5 browsers, I would build upon Floern's answer by using jQuery and make the javascript non-obtrusive. I would also use a class to define the blurred properties.
$(document).ready(function () {
//Set the initial blur (unless its highlighted by default)
inputBlur($('#Comments'));
$('#Comments').blur(function () {
inputBlur(this);
});
$('#Comments').focus(function () {
inputFocus(this);
});
})
Functions:
function inputFocus(i) {
if (i.value == i.defaultValue) {
i.value = "";
$(i).removeClass("blurredDefaultText");
}
}
function inputBlur(i) {
if (i.value == "" || i.value == i.defaultValue) {
i.value = i.defaultValue;
$(i).addClass("blurredDefaultText");
}
}
CSS:
.blurredDefaultText {
color:#888 !important;
}
The shortest way is to directly add the below code as additional attributes in the input type that you want to change.
onfocus="if(this.value=='Search')this.value=''"
onblur="if(this.value=='')this.value='Search'"
Please note: Change the text "Search" to "go" or any other text to suit your requirements.
This works:
<input type="text" id="firstname" placeholder="First Name" />
Note: You can change the placeholder, id and type value to "email" or whatever suits your need.
More details by W3Schools at:http://www.w3schools.com/tags/att_input_placeholder.asp
But by far the best solutions are by Floern and Vivek Mhatre ( edited by j0k )
I have a code snippet below, that is a typical web page.
.Submit {
background-color: #008CBA;
border: 3px;
color: white;
padding: 8px 26px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
div {
background-color: yellow;
}
<div>
<center>
<p>Some functions may not work, such as the css ect.
<p>First Name:<input type="text" id="firstname" placeholder="John" />
<p>Surname:<input type="text" id="surname" placeholder="Doe" />
<p>Email:<input type="email" id="email" placeholder="john.doe#example.com" />
<p>Password:<input type="email" id="email" placeholder="john.doe#example.com" />
<br /><button class="submit">Submit</button> </center>
</div>
This is an elaborate version, to help you understand
function setVolatileBehavior(elem, onColor, offColor, promptText){ //changed spelling of function name to be the same as name used at invocation below
elem.addEventListener("change", function(){
if (document.activeElement == elem && elem.value==promptText){
elem.value='';
elem.style.color = onColor;
}
else if (elem.value==''){
elem.value=promptText;
elem.style.color = offColor;
}
});
elem.addEventListener("blur", function(){
if (document.activeElement == elem && elem.value==promptText){
elem.value='';
elem.style.color = onColor;
}
else if (elem.value==''){
elem.value=promptText;
elem.style.color = offColor;
}
});
elem.addEventListener("focus", function(){
if (document.activeElement == elem && elem.value==promptText){
elem.value='';
elem.style.color = onColor;
}
else if (elem.value==''){
elem.value=promptText;
elem.style.color = offColor;
}
});
elem.value=promptText;
elem.style.color=offColor;
}
Use like this:
setVolatileBehavior(document.getElementById('yourElementID'),'black','gray','Name');
You can use Floern's solution. You may also want to disable the input while you set the color to gray. http://www.w3schools.com/tags/att_input_disabled.asp
Here's a one-liner slim way for layering text on top of an input in jQuery using ES6 syntax.
$('.input-group > input').focus(e => $(e.currentTarget).parent().find('.placeholder').hide()).blur(e => { if (!$(e.currentTarget).val()) $(e.currentTarget).parent().find('.placeholder').show(); });
* {
font-family: sans-serif;
}
.input-group {
position: relative;
}
.input-group > input {
width: 150px;
padding: 10px 0px 10px 25px;
}
.input-group > .placeholder {
position: absolute;
top: 50%;
left: 25px;
transform: translateY(-50%);
color: #929292;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="input-group">
<span class="placeholder">Username</span>
<input>
</div>