Convenient way to mark a complete form as readonly - reactjs

I have an antd form with multiple form items and try to find a way to mark the complete form as readonly. I could for sure set each input component to 'disabled' but I wonder if there is a convenient way to do so on the form via an API call that I do not know yet.

Wrapping the antd form inside a fieldset and setting this to 'disabled' works pretty well.
<fieldset disabled={editorDisabled}>
<Form>
...
<Form/>
<fieldset/>

I don't see such an option in the form api, and I think it's the rare use case, so I doubt it exists. However, you can simply add variable which will track the disabled status, i.e.:
const YourAwesomeComponent = (props) => {
const disabled = someLogicToCalculateTheDisabledStatus(props);
return <Form ...>
<Input disabled={disabled} ... />
<Select disabled={disabled} ... />
<Button disabled={disabled} ... />
</Form>
}
Hope it helps.

As of version 4.21.0 (Jun 6, 2022) the disabled prop can be used in the form to disable all fields, i.e.:
<Form disabled={true}>
...
</Form>
It is enforced as long as a <Form.Item/> isn't explicitly marked as not disabled with disabled={false}. You can see the reference in antd docs here.

Related

Browser autocomplete issue

I am having a hard time trying to find a proper way to disable autocomplete from browser in antd forms.
In normal form inputs
<Input autoComplete="none" />
works perfectly.
the problem is that in Select and Autocomplete components it does not work, it does not accept this prop, the question is:
is it posible to disable browser autocomplete in the whole form?, I have been browin for hours for an answer to this issue, and so far all I found is that it is not posible, but just want to give it last try asking here.
Thanks a lot
I guess there is, Set autoComplete 'off' to the entire form.
const handleSubmit(e) {
e.preventDefault();
//... Submit Actions
}
return (
<form autoComplete='off' onSubmit={handleSubmit}>
// ... form contents ...
</form>
)

React autoFocus attribute is not rendered

I'm trying to render an <input> field with autofocus:
<input type="text" autoComplete="off" autoFocus={true} className="form-control" />
But it's being rendered as:
<input type="text" autocomplete="off" class="form-control">
And the input does not focus.
Why is that? How can I get the autofocus attribute to render?
This is expected, actually. React will manually call focus() on the relevant element, but it will not add the attribute itself.
Here is a quote from Dan Abramov from a ticket response from ~2 years ago:
This is intentional because it works very differently in different browsers. So instead of actually setting a DOM attribute, we polyfill its behavior in JavaScript.
Also, from another thread:
Chrome respects dynamically added elements with autoFocus if there hasn’t been one before. Even creating an input with autoFocus, adding it to the document, and removing it in the next tick is enough to disable autofocusing of any elements added in the future in Chrome.
Firefox just doesn’t care about any dynamically added elements with autoFocus at all.
And Safari always respects them.
That being said, you could still force the attribute with autofocus="true" instead of autoFocus={true} but it might not actually work in a reliable fashion. After all, there is a reason the React team polyfilled this.
In other words, if you want a reliable, consistent cross-browser behavior use autoFocus, if you must have the native attribute you can use autofocus="true", but know that you'll be at the mercy of letting the browser decide when and how said element will be focused.
You can try using a ref, and setting the focus when your component is ready.
const YourComponent = (props) => {
const inputEl = useRef(null);
useEffect(() => {
inputEl.current.focus();
}, []);
return <div>
<input autofocus="true" ref={inputEl} />
</div>;
}
Answer by Brandon works perfectly. Additionally, if you want to autofocus on an input on a Modal for example, just keep track of the modal state and use that to focus. For example:
const refModal = useRef(null);
useEffect(() => {
if(modalOpen) {
refModal.current.focus();
}
}, [modalOpen]);

antd Select turn off autocomplete

I am trying to turn off autocomplete on an ant design Select but nothing I try works. This is obviously problematic as chromes (only one tested at this point) autocomplete completely covers the Select Options (Image below).
I am using antd in React and have tried the following on both the Select tag and Form tag:
autoComplete="off"
autoComplete="nope"
autoComplete="business-state"
and none of them seem to turn it off.
Here is a link to a code sandbox with almost the same code but the problem is it does not reproduce the same issue. For some reason the exact same code on sandbox doesn't show the autocomplete. I added an email input to show to test if autocomplete works on that and sure enough it does. I am at a loss for why this is working differently in sandbox than from my server.
https://codesandbox.io/s/wovnzpl9xw
Here is what it looks like when a user is typing to search the Options
Here is what it should look like (it only looks like this on focus but as soon as a user types chrome shows autocomplete)
Figured out a solution to my question, thanks to #trkaplan for pointing me in the right direction. Not the most elegant solution I am guessing so if anyone else has a better way to implement this I am all ears. I essentially had to create an onFocus method for the Select that grabs all of the elements with class ant-select-search__field loops through them and utilize setAttribute to change the autocomplete attribute to an arbitrary value in order to disable it.
onFocus={() => {
if (!this.state.autocompleteDisabled) {
let i;
const el = document.getElementsByClassName(
"ant-select-search__field"
);
for (i = 0; i < el.length; i++) {
el[i].setAttribute(
"autocomplete",
"registration-select"
);
}
this.setState({ autocompleteDisabled: true });
}
}}
I have applied to add the autocomplete="none" into the Select and it is looking fine.
<Select
showSearch
optionFilterProp="children"
onChange={onChange}
onFocus={onFocus}
onBlur={onBlur}
onSearch={onSearchZipCode}
filterOption={(input, option) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
placeholder="Choose a City"
autoComplete="none"
// disabled={true}
>
{cities.map((city) => (
<Select.Option key={city.id} value={city.id}>
{city.name}
</Select.Option>
))}
</Select>;
You could also use AutoComplete instead of Select, because with this it's possible to change the underlying Input
<AutoComplete ... >
<Input autoComplete='off' id='myId' />
</AutoComplete>
AutoComplete is "new" and intended to be used if you are fetching data for the input instead of the data being static, but essentially its just a copy of Select and could be used the same way.
Another thing that worked very well for me was the following:
<FormItem>
<Select
autoComplete="new-state"
{...otherProps}
/>
</FormItem>
The autoComplete prop currently isn't documented properly and they don't even cater for TS users properly.
But this worked, if you've got that new word in the autoComplete prop, Chrome (and hopefully other browsers) will ignore that field.
For TypeScript users that have justifiably strict type rules:
<Select
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
// #ts-ignore
autoComplete="new-state"
{...otherProps}
/>
You have to set filterOption to false
See this code sandbox
The only "clean" way I found to have no autocomplete when the label of the input is supposed to be aucompleted according to chrome (see here: Which Attributes Does Chrome Autofill Expect?) is to modify the labels by inserting invisible character ‍ in the label
<Form.Item label={<>Cou‍ntry</>}>
for example will be interpreted as
<Form.Item label={<>Cou‍ntry</>}>
But chrome does not recognize it as a Country input field
In my project I had dynamic inputs so the solution was (using _.times):
<Form.Item key={id} label={_.times(name.length, num => <>{tag.name.charAt(num)}‍</>)}>
Hacky but future proof (I hope)
Going to the FormItem component you can set the name props to "stateEnum" and then your browser won't just assume the field name from the label.
It works nicely for me on Google Chrome.
Example:
<FormItem label="State" name="stateEnum">
...
</FormItem>

How to hide/show Field in FieldArray for Redux Form v6

given the fact in the example http://redux-form.com/6.0.5/examples/fieldArrays/. All the renderField.. functions are outside of the React Class. Hence how am i suppose to use react state or props to determine whether i want to hide or show a Field?
What I'm trying to do is to have a button to manipulate a state to display 'block' or 'none' for a Field. Can someone guide me? I tried to put the renderField variable inside the render of the React class, however this result in bugs.
All the props which are passed to Field are accessible to component props.
<Field
name={foo}
type="text"
component={TextField}
displayBlock={displayBlock}
/>
const TextField = props => {
if(props.displayBlock) {
...
}
return (
<div>
<input {...props.input} />
</div>
);
};
Thanks to Runaground who suggested an answer to me. I came to realized that I can pass in state as props to the FieldArray, such as
<FieldArray name="histories" component={renderHistories} openClose={this.state.openClose}/>
This allow me to utilize an array of state from this.state.openClose, to control which field i would like to hide or show.
<Field
name={`${histories}.details`}
type="text"
component={renderField}
style={{display: openClose[index] ? 'block' : 'none'}}
/>
However, even though I am able to control the display of the Field, the field array does not get rerendered.
Hence, I have to add on two functions
fields.push({});
setTimeout(()=>{
fields.pop();
}, 1);
that is taken from http://redux-form.com/6.0.5/docs/api/FieldArray.md/, to actually rerender the fieldarray whenever i hide or show the field. Hopefully there is a more elegant way to do this as the fieldarray does flicker.

Set focus on a react-bootstrap.Input field using refs.x.getDOMNode.focus

The JSX:
var Button = require("react-bootstrap").Button;
var Input = require("react-bootstrap").Input;
var MyClass = React.createClass({
onclick: function () {
this.refs.email.getDOMNode().focus();
console.log('DOM element', this.refs.email.getDOMNode());
}
render: function () {
return (
<div>
<Button onClick={this.onlick}>Login</Button>
<Input ref='email' type='email' />
</div>
);
},
});
Notes:
The input field is a react-bootstrap.Input class.
The getDOMNode().focus() concept is from Facebook react docs
When the button onclick handler runs, the email input field should be getting the focus, but it's not happening. I found that the react-bootstrap Input field class is rendering the real DOM input field inside a wrapping div. This is the output of the console.log:
<div class="form-group" data-reactid=".1awy8d4e9kw.1.3.$=1$3:0.0.0.1.0">
<input class="form-control" type="email" data-reactid=".1awy8d4e9kw.1.3.$=1$3:0.0.0.1.0.1:$input">
</div>
So looks like the input isn't getting the focus because the focus is applied on the wrapping div, which rejects it (I use document.activeElement in the console to check).
Question is, how to focus on the real input element?
Note:
A bit unrelated but React respects the autoFocus property. That's useful in case the focus is needed to be set immediately after rendering. Still, it's not a substitute for the programmatic way.
Try getInputDOMNode() instead of getDOMNode().
in render
<FormControl
type='text'
ref={(c)=>this.formControlRef=c}
/>
in event handler
someHandler() {
ReactDOM.findDOMNode(this.formControlRef).focus();
}
Since findDOMNode is no longer the suggested method and may be deprecated by Facebook, you should use the autoFocus property (which is provided by the React framework, not react-bootstrap). This is a cross-browser polyfill for the HTML autofocus attribute.
<FormControl
autoFocus
...
/>
Sources:
Focusing to the FormControl
Support needed for focusing inputs in modals?
FormControl provides inputRef attribute
try it,
<FormControl inputRef={ref => { this.input = ref; }} />
https://react-bootstrap.github.io/components/forms/
or you may use vanilla javascript and select the element and set focus
document.getElementById("myInput").focus()

Resources