AutoFocus on Text Input in React Using Styled Components - html

How do you make the TextInput in React using Styled Components to be autoFocus on page load?
I did using useEffect but it seems it doesn't autoFocus on the TextInput. I wanted the email text input to be focus outlined on page load.
Codesandbox -> CODESANDBOX
function App() {
const emailInput = useRef(null);
useEffect(() => {
if (emailInput.current) {
emailInput.current.focus();
}
}, []);
return (
<div>
<Input type="text" placeholder="Name" />
<Input type="email" placeholder="Email" innerRef={emailInput} />
</div>
);
}

As per styled-components documentation, in innerRef section, the innerRef is deprecated as of v4:
NOTE
The "innerRef" prop was removed in styled-components v4 in favor of the React 16 forwardRef API. Just use the normal ref prop instead.
Before React 16, there was no nice way to pass references up to the parent (except using callbacks). In React 16.3 createRef and forwardRef have been introduced for doing that, along with useRef hook later. Styled Components v4, has started to use that mechanism.
Therefore, you may use ref directly:
function App() {
const emailInput = useRef(null);
useEffect(() => {
if (emailInput.current) {
emailInput.current.focus();
}
}, []);
return (
<div>
<Input type="text" placeholder="Name" />
<Input type="email" placeholder="Email" ref={emailInput} />
</div>
);
}
On the other side, if the intent is only for focusing, as stated by #Andy, the autoFocus property may be a better choice:
function App() {
return (
<div>
<Input type="text" placeholder="Name" />
<Input type="email" placeholder="Email" autoFocus />
</div>
);
}
The React's autoFocus attribute is doing exactly the same thing as the code above (focus programmatically element on the mount). Do not confuse it with HTML's autofocus attribute, which behavior is inconsistent between browsers.

Related

Is there a way to enable the autocomplete for angular reactive form?

I want to set on the autocomplete attribute for an angular form but it doesn't work as expected. It remembers only the values that I submitted only the first time and I would like to remember and suggest all the values no matter how many times I click on submit button.
Here is the stackblitz with the code that I tried.
<form
autocomplete="on"
(ngSubmit)="onSubmit()"
name="filtersForm"
[formGroup]="formGroup1"
>
<div>
<label>First Name</label>
<input
id="firstName"
name="firstName"
autocomplete="on"
formControlName="firstName"
/>
</div>
<div>
<label>Last Name</label>
<input
id="firstName"
name="lastName"
autocomplete="on"
formControlName="lastName"
/>
</div>
<button type="submit">Submit</button>
</form>
Here are the details about the autocomplete attribute that I used.
In Firefox, the autocomplete is working after several clicks on Submit button, the problem is in Chrome and Edge.
Is there a way to make the autocomplete to work for inputs inside the angular form?
I think, I have found a workaround, that only works with Template Driven Form.
TLDR;
What I have discovered while looking after this issue.
On first form submit autofill remember only first time submission values
form submit POST method can remember all values.
Yes, by looking at above, it clearly seems like 2nd way is suitable for us. But why would anybody do form POST for submitting form to BE. There should be better way to tackle this. Otherwise we would have to think of handling PostBack 😃😃 (FW like .Net does it by keeping hidden input's).
Don't worry we can do some workaround here to avoid form POST. I found an answer for handling POST call without page refresh.
Working JSBin with plain HTML and JS
AutoCompleteSaveForm = function(form){
var iframe = document.createElement('iframe');
iframe.name = 'uniqu_asdfaf';
iframe.style.cssText = 'position:absolute; height:1px; top:-100px; left:-100px';
document.body.appendChild(iframe);
var oldTarget = form.target;
var oldAction = form.action;
form.target = 'uniqu_asdfaf';
form.action = '/favicon.ico';
form.submit();
setTimeout(function(){
form.target = oldTarget;
form.action = oldAction;
document.body.removeChild(iframe);
});
}
Basically we change set few things on form attribute.
target="iframe_name" - Connects to iFrame to avoid page refresh.
method="POST" - POST call
url="/favicon" - API url to favicon (lightweight call)
In angular you can create an directive for the same.
import {
Directive, ElementRef, EventEmitter,
HostBinding, HostListener, Input, Output,
} from '#angular/core';
#Directive({
selector: '[postForm]',
})
export class PostFormDirective {
#HostBinding('method') method = 'POST';
#HostListener('submit', ['$event'])
submit($event) {
$event.preventDefault();
this.autoCompleteSaveForm(this.el.nativeElement);
}
constructor(private el: ElementRef) {}
autoCompleteSaveForm(form) {
let iframe = document.querySelector('iframe');
if (!iframe) {
iframe = document.createElement('iframe');
iframe.style.display = 'none';
}
iframe.name = 'uniqu_asdfaf';
document.body.appendChild(iframe);
var oldTarget = form.target;
var oldAction = form.action;
form.target = 'uniqu_asdfaf';
form.action = '/favicon.ico'; // dummy action
form.submit();
setTimeout(() => {
// set back the oldTarget and oldAction
form.target = oldTarget;
form.action = oldAction;
// after form submit
this.onSubmit.emit();
});
}
#Output() onSubmit = new EventEmitter();
ngOnDestroy() {
let iframe = document.querySelector('iframe');
if (iframe) {
document.body.removeChild(iframe);
}
}
}
Okay, so far everything went well. Then I started integrating this in formGroup(Model Driven Form), somehow it didn't worked. It does not store value next time these fields.
<form (ngSubmit)="onSubmit()" [formGroup]="formGroup1" autocomplete="on">
<div>
<label>First Name</label>
<input id="firstName" name="firstName" formControlName="firstName" />
</div>
<div>
<label>Last Name</label>
<input id="lastName" name="lastName" formControlName="lastName" />
</div>
<button>Submit</button>
</form>
Later I tried the same with Template Driven Form. It just worked like a charm! I did not went into the depth why it didn't work for Model Driven Form (perhaps that investigation could eat more time).
<form #form1="ngForm" ngForm postForm (onSubmit)="onSubmit(form1)">
<ng-container [ngModelGroup]="userForm">
<div>
<label>First Name</label>
<input name="firstName" [(ngModel)]="userForm.firstName" />
</div>
<div>
<label>Last Name</label>
<input name="lastName" [(ngModel)]="userForm.lastName" />
</div>
</ng-container>
<button>Submit</button>
</form>
Yeah, I just said in the begining it works only with Template Driven Form. So you would have to switch to Template. And one more important thing to note, you may think of creating dummy POST api call, that can be lightweight rather than hitting favicon.
Stackblitz
autocomplete attribute works only with submitted values. It has nothing to do with Angular.
https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
If you need some custom behavior then you are better off creating your own component to autocomplete user's input, this way you can use some default values, add more of them on blur etc.
You just need to remove autocomplete="on" in input tag. With chrome, we only add attribute autocomplete="on" in form element and it will be cached all value that user input into input text. Result will be like this:
<form
autocomplete="on"
(ngSubmit)="onSubmit()"
name="filtersForm"
[formGroup]="formGroup1"
>
<div>
<label>First Name</label>
<input
id="firstName"
name="firstName"
formControlName="firstName"
/>
</div>
<div>
<label>Last Name</label>
<input
id="firstName"
name="lastName"
formControlName="lastName"
/>
</div>
<button type="submit">Submit</button>
</form>
You have to create an array with your desired options which should be displayed as autocomplete. You can have a look here https://material.angular.io/components/autocomplete/examples, there are multiple examples which should help you. Even if you're not using Angular Material, the logic would be the same

oninvalid attribute not rendering in React js

I am trying to add oninvalid attribute in HTML element under React js code. (using react hooks not class based)
const openEndedAnswer = answer => {
return (<>
<input type="text" className="form-control"
required="required"
oninvalid="this.setCustomValidity('Enter User Name Here')"
oninput="this.setCustomValidity('')"
maxLength="255"
id={`answer_${question.id}`}
name={`answer_${question.id}`}
onChange={e => updatePostForm(e)}
pattern=".*[^ ].*"
title=" No white spaces"
/>
</>)
}
But it never renders in the browser. all other attributes can be seen in F12 source view.
The attribute names should onInvalid instead of oninvalid and onInput instead of oninput. Additionally, you need to call the setCustomValidity function on the input field as follow (because the input field is the target of the event):
onInvalid={e => e.target.setCustomValidity('Enter User Name Here')}
onInput={e => e.target.setCustomValidity('')}
If you are using React with javascript this should work:
onInvalid={e => e.target.setCustomValidity('Your custom message')}
onInput={e => e.target.setCustomValidity('')}
But if you are working with React with typescript you also need to add this:
onInvalid={e => (e.target as HTMLInputElement).setCustomValidity('Enter User Name Here')}
onInput={e => (e.target as HTMLInputElement).setCustomValidity('')}
Try onInvalid instead:
export default function App() {
return (
<form>
Name:{" "}
<input
type="text"
onInvalid={() => console.log("working!")}
name="fname"
required
/>
<input type="submit" value="Submit" />
</form>
);
}

Why is OnChange not working when used in Formik?

I am trying to use Formik in React for a dummy app. I am not being able to type anything in either of the input boxes if I give value as a prop. On the other hand, if I skip the value props, then I can type in the boxes but the same is not reflected as the value while submitting.
Here's the code:
export default class DashboardPage extends React.Component {
render() {
return (
<Formik
initialValues={{ fname: "", lname: "" }}
onSubmit={(values) => {
alert(values.fname);
}}
render={({ values, handleChange, handleSubmit }) => (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="First Name" name="fname" onChange={handleChange} value={values.fname} />
<input type="text" placeholder="Last Name" name="lname" onChange={handleChange} value={values.lname} />
<button type="submit>ADD<button/>
</form>
)}
/>
);
}
}
I may be very wrong here and might be over-looking a minor error, but any help/suggestion is appreciated!
export default class DashboardPage extends React.Component {
render() {
return (
<Formik
initialValues={{ fname: "", lname: "" }}
onSubmit={ (values) => alert(values.fname) }
>
{ props => (
<React.Fragment>
<form onSubmit={handleSubmit}>
<input type="text" placeholder="First Name" name="fname" onChangeText={props.handleChange('fname')} />
<input type="text" placeholder="Last Name" name="lname" onChangeText={props.handleChange('lname')} />
<button type="submit>ADD<button/>
</form>
</React.Fragment>
)}
</Formik>
)
}
}
Hi mate can you please try this?
Another possibility for this weird behavior is putting your component inside a function and sending the props as function parameters instead of using a functional component with props as parameters.
When a component is inside a function you lose react component lifecycle and the parameters will not refresh even when its values change.
Make sure your form elements are properly configured as react elements.
Mine was also not working, I gave id prop in my TextField and now it works
const formik = useFormik({
initialValues: {
username: "",
password: "",
},
onSubmit: (values) => {
console.log(values);
// onLogin(values.username, values.password);
},
});
<form onSubmit={formik.handleSubmit}>
<TextField
value={formik.values.username}
onChange={formik.handleChange}
id="username"
name="username"
variant="outlined"
style={TextField1style}
placeholder="Enter username"
fullWidth
required
//.
// .
// .
// other code
enter image description here

ReactJs - onClick doesn't seem to work on a button HTML tag

I have this simple piece of code in my ReactJs app:
import React from 'react'
import '../../../assets/styles/authentication/login.css'
import '../../../assets/bootstrap/css/bootstrap.min.css'
import { login } from './login_form_actions.js'
export default class LoginForm extends React.Component {
constructor(props) {
super(props)
this.state = {
email : { email: "" },
password : { password: "" }
}
this.handle_sign_in_click = this.handle_sign_in_click.bind(this)
}
handle_sign_in_click(e) {
console.log(e, this.state.email, this.state.password)
login(this.state.email, this.state.password)
}
render() {
return (
<div className="col-sm-6 col-sm-offset-3 form-box">
<div className="form-auth">
<form className="login-form">
<div className="form-group">
<label className="sr-only" htmlFor="form-username">Email</label>
<input type="text" name="form-username" placeholder="Email..."
className="form-username form-control" id="form-username"
onChange={(event) => this.setState({email:event.target.value})}/>
</div>
<div className="form-group">
<label className="sr-only" htmlFor="form-password">Password</label>
<input type="password" name="form-password" placeholder="Password..."
className="form-password form-control" id="form-password"
onChange={(event) => this.setState({password:event.target.value})}/>
</div>
<button className="btn" onClick={this.handle_sign_in_click}>Sign in!</button>
</form>
</div>
</div>
)
}
}
When I click the Sign In button, nothing happens for some reason.
I tried to bind the handle_sign_in_click as proposed in some other reactjs question (from StackOverflow) but still nothing.. What am I missing here? (next step is to use redux to store the result of the auth on success)
Thanks for any help
Edit1: Console and proposition
console:
react-dom.development.js:22287 Download the React DevTools for a better development experience: react-devtools
authentication:1 [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): ​
However, when I use an <a/> instead of <button/>, it does work but my design is broken (it's the same at all as when it's a button)
Here is how to properly handle basic forms in React (without libs like Formik etc...):
import React from 'react';
export default class Form extends React.Component {
state = { email: '', password: '' };
handleChange = event => {
this.setState({ [event.target.name]: event.target.value });
};
handleSubmit = event => {
event.preventDefault();
// login logic here ususally passed down as props from the parent (if not using redux...) so it will be something like this.props.login(...)
this.setState({ email: '', password: '' });
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
name="email"
type="text"
onChange={this.handleChange}
placeholder="email"
value={this.state.email}
/>
<input
name="password"
type="text"
onChange={this.handleChange}
placeholder="password"
value={this.state.password}
/>
<input type="submit" />
</form>
);
}
}
You are missing a e.preventDefault in your signin handler, check this working sample

ng-model vs ngModel - breaks form

New to angular, new to life:
I have a small email form.
This works:
<form method="post" name="form" role="form" ng-controller="contactForm" ng-submit="form.$valid && sendMessage(input)" novalidate class="form-horizontal">
<p ng-show="success"><b>We received your message</b></p>
<p ng-show="error">Something wrong happened!, please try again.</p>
<label for="name">Name:</label><br>
<input type="text" id="name" name="name" ng-model="input.name" required><br>
<label for="email">Email:</label><br>
<input type="email" id="email" name="email" ng-model="input.email" required><br>
<label for="messsage">Message:</label><br>
<textarea id="messsage" name="message" ng-model="input.message" ngMaxlength='2000' required></textarea><br>
<button type="submit" name="submit" ng-disabled="error" value="submit">Submit</button>
</form>
This does not work:
<form method="post" name="form" role="form" ng-controller="contactForm" ng-submit="form.$valid && sendMessage(input)" novalidate class="form-horizontal">
<p ng-show="success"><b>We received your message</b></p>
<p ng-show="error">Something wrong happened!, please try again.</p>
<label for="name">Name:</label><br>
<input type="text" id="name" name="name" ngModel="input.name" required><br>
<label for="email">Email:</label><br>
<input type="email" id="email" name="email" ngModel="input.email" required><br>
<label for="messsage">Message:</label><br>
<textarea id="messsage" name="message" ngModel="input.message" ngMaxlength='2000' required></textarea><br>
<button type="submit" name="submit" ng-disabled="error" value="submit">Submit</button>
</form>
for the 2 inputs and the textarea if I use 'ng-model' the email sends, but when the page loads, the form loads invalid.
If i use 'ngModel' the form loads clean, but the email wont submit.
controller here:
app.controller("contactForm", ['$scope', '$http', function($scope, $http) {
$scope.success = false;
$scope.error = false;
$scope.sendMessage = function( input ) {
$http({
method: 'POST',
url: 'processForm.php',
data: input,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
.success( function(data) {
if ( data.success ) {
$scope.success = true;
$scope.input.name="";
$scope.input.email="";
$scope.input.message="";
} else {
$scope.error = true;
}
} );
}
You can see it live here:
http://smartestdiner.com/Bethel/indexx.html#/contact
Warning:
There is some annoying red background
.ng-invalid{
background-color:red;
}
}]);
That's how we know it is loading invalidly.
The annoying red background is the form, since you have a very generic rule set by .ng-invalid, the class will be set on the form as well. You would need to make it more specific for the inputs and controls within the form.
Example:
input.ng-invalid,
textarea.ng-invalid {
background-color:red;
}
Or just reset rule for form.ng-invalid
To add on there is nothing called ngModel it is ng-model. using the former one doesn't do anything but adds a dummy attribute on the element, it has no effect. It is angular way of directive naming, since html is case insensitive the one way angular can identify the directive from attribute or element name (based on the restriction). It converts it to camelCasing to evaluate and process respective directive (or directives attribute bindings). When you do not have ng-model specified and if the form or control does not have novalidate attribute, then the browser's HTML5 validation kicks in that is what you see as inconsistency. Using HTML5 novalidate attribute makes sure no native validation happens on the form.
ng-model is when u write the view (html part).
ngModel is used when one write a custom directive. It is placed in the "require:" param so that u can access,
variables like ngModel.$modelValue
ngModel.$modelValue will have the latest content which has been typed by the user at realtime. So, it can be used for validations, etc.
View code:-
<!doctype html>
<html ng-app="plankton">
<head>
<script src="/bower_components/angular/angular.min.js"></script>
<script src="/scripts/emailing/emailing.directive.js"></script>
</head>
<body ng-controller="EmailingCtrl">
<div>
<label>Enter Email: </label>
<emailing id="person_email" ng-model="email_entered"></emailing>
</div>
</body>
</html>
Custom directive:-
(function() {
'use strict';
angular.module('plankton', [])
.directive('emailing', function emailing(){
return {
restrict: 'AE',
replace: 'true',
template: '<input type="text"></input>',
controllerAs: 'vm',
scope: {},
require: "ngModel",
link: function(scope, elem, attrs, ngModel){
console.log(ngModel);
scope.$watch(function(){ return ngModel.$modelValue;
}, function(modelValue){
console.log(modelValue);//awesome! gets live data entered into the input text box
});
},
};
})
.controller('EmailingCtrl', function($scope){
var vm = this;
});
})();
This has been plunked here:- here