I'm a novice back end developer and I've been tasked with fixing a feature and I'm stuck on the react portion. I can't seem to find the appropriate syntax to get a value from a select component and that's pretty much my whole issue. I've looked at lots of other posts and the react docs and nothing I'm trying is working. An example of markup is as follows(There's lots of select fields in this view):
<div className="grid-content noscroll medium-6 small-12" style={{overflow: 'visible'}}>
<div className="grid-content"><label>Program</label></div>
<div className="grid-content" style={{overflow: 'visible'}}>
<Select
key="program_key"
ref="program_key"
multi={false}
value={ jobData && jobData.program_key ? jobData.program_key : null}
options={programOptions}
onChange={this.changeField.bind(null, 'program_key')}
/>
</div>
</div>
Then the event handler is as follows:
changeField: function(propName) {
var field = this.refs[propName].getDOMNode();
console.log(field.input);
console.log(field);
var nextProp = field.value.length > 0 ? field.value : null;
var job = Object.assign({}, this.state.job);
job.payload.data[propName] = nextProp;
if(propName === 'user_id') {
this.changeUserId = true
}
this.setState({
job: job,
updated: false
});
}
The result of console.log(field) is:
<div class="Select is-searchable has-value" data-reactid=".0.0.2.0.1.0.1.1.0.1.1.$program_key">
<input type="hidden" value="NHDS" data-reactid=".0.0.2.0.1.0.1.1.0.1.1.$program_key.0">
It goes on from there but the 'value="NHDS"' is the piece that I need and I cannot figure out how to get to it for the life of me. Please let me know if I can clarify or improve this question. Thanks in advance.
Based on your example code it looks like you're using the react-select component https://github.com/JedWatson/react-select. onChange is going to fire with the new changed value as its prop. In your case the changeField method has a bound param ('program_key') that will be injected as the function's first parameter. The next parameter should be the selected value. To test this you can execute console.log(arguments) within the changeField function, it should return an array with 'program_key' and the new value. If that works just add a new param to changeField named newValue and use it where needed.
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
https://github.com/JedWatson/react-select#further-options
Related
I have question about the query selector, because I am passing a lot of function for my textboxes that will update different chart when using the dropdown list. I am always getting undefined in my console.log() Undefined screenshot this is my drop down Dropdown Screenshot.
const selectory = $('#needed').filter(":selected").val();
//const selectory = $("div.chartNine select").val();
function interactiveChart() {
console.log(selectory)
console.log(bzvaqxnufhyd1.value)
if (selectory == 'vdn6') {
updatingValue = [bzvaqxnufhyd1.value, bzvaqxnufhyd2.value, bzvaqxnufhyd3.value, bzvaqxnufhyd4.value, bzvaqxnufhyd5.value, bzvaqxnufhyd6.value, bzvaqxnufhyd7.value];
nineChart2.data.datasets[0].data = updatingValue;
nineChart2.update();
nineChart2.render(); }
}
needed is my id tag for my select under the div chartNine.
and this is my textbox
<input type="text3" id="bzvaqxnufhyd1" onchange="interactiveChart()" value="0" />
I will be using that function for 7 different charts that is why I need to have that select value so whenever I selected a new drop down, if else will do the magic in my interactiveChart function.
I hope someone can help me or lighten me up regarding my situation. Thank you.
Solved. I just added multifunction on my textbox which is this one.
<input type="text3" id="bzvaqxnufhyd1" onchange="interactiveChart()" value="0" />
I am trying to have the input fields tab over to the next field once maxlength is met but I keep getting an error Failed to execute 'querySelector' on 'Document': 'name=2' is not a valid selector.. Ive read over mozialla's explanation of a querySelector and I've tried using the elements id to focus() on but that gives the same error. I guess Im not understanding how to properly craft a selector to pass to the querySelector.
My Input fields ill only show 2 i have 3:
<Input
onChange={dateChange("month")}
value={date.month}
id="1"
maxLength={2}
type="number"
/>
<span className="sep">/</span>
<Input
onChange={dateChange("day")}
value={date.day}
name="2"
id="2"
maxLength={2}
type="number"
/>
My onChange:
const dateChange = (field) => (e) => {
const fieldIndex = e.target.name;
let fieldIntIndex = parseInt(fieldIndex, 10);
// format to fit
let value = e.target.value;
if (value.length === e.target.maxLength) {
if (fieldIntIndex < 3) {
const nextfield = document.querySelector(
`name=${fieldIntIndex + 1}`
);
console.log(nextfield);
if (nextfield !== null) {
nextfield.focus();
}
}
}
const d = { ...date, [field]: value };
setDate(d);
debounceCallback(handleDateInputChange, d);
};
Im still learning so any advice on this would be great :) thanks in advance!
First of all, I think that it is incorrect to set event listeners like this:
onChange={dateChange("month")}
If you do it that way, you actually execute that function during rendering of the page. The function should be executed when the event occurs. The correct way to do this would be:
onChange={dateChange}
If you also wanted to add parameters to your function then you should do it like this:
onChange={dateChange.bind(this, "month")}
Moreover, regarding the query selector, I think the correct syntax would be:
const nextfield = document.querySelector(`input[name='${fieldIntIndex + 1}']`);
Your name prop is set to an input element, so we use input[name].
Also name has a string value, so we use input[name=''].
Finally we want to set name value parametrically, so we use `input[name='${parameter}']`.
You can find the MDN documentation of bind JavaScript function here and the documentation of querySelector here.
Edit: Another alternative for navigating among inputs would be the tabindex attribute. You can find more about it here.
Name is a string So i think you should do it like this(add brackets)
name='${fieldIntIndex + 1}'
First of all, I don't see how the modal could have anything to do with this issue since its actually in this component's code, not a child. Still, this is in a modal, just in case.
I'm opting to not use FormArray since I need to keep track of my selections that may be added in a separate object, so I'm just creating unique IDs for the controls and adding them to the FormGroup. I can access the controls in the ts code, set values, get values, etc, but the form isn't binding and I can't figure out why not.
I can have an unknown number of items in this modal form, which will each have a selector (dropdown to select a property) and then the input to be able to modify some data. The input could be of different types, so it needs to be added and binded upon the choice from the selector.
<form [formGroup]="multiEditFormGroup" novalidate>
<div *ngFor="let item of multiEditSelections; let i = index">
<div>
<mdb-select [options]="availablePropsSelect"
placeholder="Choose property to edit"
class="colorful-select dropdown-main"
(selected)="multiEditSelectProp(item.selectorId, $event)"></mdb-select>
<label>Property to edit</label>
</div>
<div>
<div>
<input mdbActive
type="text"
class="form-control"
[formControlName]="item.controlId" />
</div>
</div>
</div>
</form>
Excepts of ts code:
public multiEditFormGroup = new FormGroup({});
onModalOpen():void {
const selectorId = this.utils.uuid();
this.multiEditFormGroup.addControl(selectorId, this.propSelector);
this.multiEditSelections.push({
selectorId: selectorId,
prop: '',
label: '',
type: '',
controlId: '' // not yet known since type hasn't been chosen
});
}
onSelect(selectorId: string, selectEvent: any): void {
let selection = this.multiEditSelections.find(selection => {
return selection.selectorId === selectorId;
});
const controlId = this.utils.uuid();
const prop = selectEvent.value;
this.multiEditFormGroup.get(selection.selectorId).setValue(prop);
this.multiEditFormGroup.markAsDirty();
this.multiEditFormGroup.markAsTouched();
const model = this.multiEditModel.find(model => {
return model.prop === prop;
});
this.multiEditFormGroup.addControl(controlId, this.testCtrl);
selection.controlId = controlId;
selection.prop = prop;
selection.label = model.label;
selection.type = model.type;
}
Logging to console shows that items are being added to the FormGroup, but the binding isn't happening to the DOM. For example, I can add a (keyup) event handler to my input and set the value in the form control which has already been created, and the FormGroup is updated. However, any input added in the front-end doesn't update the FG since it obviously isn't binding. Is this a timing issue or because the controlId is being updated later? I'm creating the FormControl before updating my array that is being iterated.
Oh and I get no errors in console on this.
I think you need to make this change:
[formControlName]="item.controlId"
needs to be:
formControlName="{{item.controlId}}"
Simple question, but is there a way to have the first item in the dropdown results be the selected item when ENTER is pressed?
An example of this is the user types in "PC0" and sees "PC001" listed as the first option, can we have it use "PC001" on the typeahead-on-select option when ENTER is hit?
I am currently using typeahead-on-select to run a function that calls the input via id and grabs the Value for use in the function. It seems to use what was entered into the textbox instead of the selected value, either on ENTER or Click.
HTML:
<input id="applicationComboBox"
type="text"
ng-model="applicationComboBox"
uib-typeahead="a as a.Value for a in applicationList | filter:$viewValue"
typeahead-on-select="getApplication()"
class="form-control">
JS for the getApplicationValue() looks like this:
$scope.getApplication = function () {
$scope.ApplicationValue = applicationComboBox.value;
}
The issue is the applicationComboBox.value is what text the user has typed into the input at the time of the click/enter instead of the clicked/highlighted value respectively. So in previous example "PC0" would be the value instead of "PC001".
When the user selects/press enter the ng-model applicationCombox is is updated automatically. If you want another value $scope.ApplicationValue to be updated after the selection, do the following
$scope.applicationCombox = ""; //your existing model.
$scope.getApplication = function () {
$scope.ApplicationValue = $scope.applicationCombox;
}
Let us know.
I was able to get a solution that worked for me.
HTML:
<input id="applicationComboBox"
type="text"
ng-model="applicationComboBox"
uib-typeahead="a as a.Value for a in applicationList | filter:$viewValue"
typeahead-on-select="onApplicationSelect($item, $model, $label, a)"
class="form-control">
JS:
$scope.onApplicationSelect = function (item, model, label, application) {
applicationComboBox.value= item.Value;
}
I'm trying to create a simple form with react, but facing difficulty having the data properly bind to the defaultValue of the form.
The behavior I'm looking for is this:
When I open my page, the Text input field should be filled in with the text of my AwayMessage in my database. That is "Sample Text"
Ideally I want to have a placeholder in the Text input field if the AwayMessage in my database has no text.
However, right now, I'm finding that the Text input field is blank every time I refresh the page. (Though what I type into the input does save properly and persist.) I think this is because the input text field's html loads when the AwayMessage is an empty object, but doesn't refresh when the awayMessage loads. Also, I'm unable to specify a default value for the field.
I removed some of the code for clarity (i.e. onToggleChange)
window.Pages ||= {}
Pages.AwayMessages = React.createClass
getInitialState: ->
App.API.fetchAwayMessage (data) =>
#setState awayMessage:data.away_message
{awayMessage: {}}
onTextChange: (event) ->
console.log "VALUE", event.target.value
onSubmit: (e) ->
window.a = #
e.preventDefault()
awayMessage = {}
awayMessage["master_toggle"]=#refs["master_toggle"].getDOMNode().checked
console.log "value of text", #refs["text"].getDOMNode().value
awayMessage["text"]=#refs["text"].getDOMNode().value
#awayMessage(awayMessage)
awayMessage: (awayMessage)->
console.log "I'm saving", awayMessage
App.API.saveAwayMessage awayMessage, (data) =>
if data.status == 'ok'
App.modal.closeModal()
notificationActions.notify("Away Message saved.")
#setState awayMessage:awayMessage
render: ->
console.log "AWAY_MESSAGE", this.state.awayMessage
awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else "Placeholder Text"
`<div className="away-messages">
<div className="header">
<h4>Away Messages</h4>
</div>
<div className="content">
<div className="input-group">
<label for="master_toggle">On?</label>
<input ref="master_toggle" type="checkbox" onChange={this.onToggleChange} defaultChecked={this.state.awayMessage.master_toggle} />
</div>
<div className="input-group">
<label for="text">Text</label>
<input ref="text" onChange={this.onTextChange} defaultValue={awayMessageText} />
</div>
</div>
<div className="footer">
<button className="button2" onClick={this.close}>Close</button>
<button className="button1" onClick={this.onSubmit}>Save</button>
</div>
</div>
my console.log for AwayMessage shows the following:
AWAY_MESSAGE Object {}
AWAY_MESSAGE Object {id: 1, company_id: 1, text: "Sample Text", master_toggle: false}
Another way of fixing this is by changing the key of the input.
<input ref="text" key={this.state.awayMessage ? 'notLoadedYet' : 'loaded'} onChange={this.onTextChange} defaultValue={awayMessageText} />
Update:
Since this get upvotes, I will have to say that you should properly have a disabled or readonly prop while the content is loading, so you don't decrease the ux experience.
And yea, it is most likely a hack, but it gets the job done.. ;-)
defaultValue is only for the initial load
If you want to initialize the input then you should use defaultValue, but if you want to use state to change the value then you need to use value. Personally I like to just use defaultValue if I'm just initializing it and then just use refs to get the value when I submit. There's more info on refs and inputs on the react docs, https://facebook.github.io/react/docs/forms.html and https://facebook.github.io/react/docs/working-with-the-browser.html.
Here's how I would rewrite your input:
awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else ''
<input ref="text" onChange={this.onTextChange} placeholder="Placeholder Text" value={#state.awayMessageText} />
Also you don't want to pass placeholder text like you did because that will actually set the value to 'placeholder text'. You do still need to pass a blank value into the input because undefined and nil turns value into defaultValue essentially. https://facebook.github.io/react/tips/controlled-input-null-value.html.
getInitialState can't make api calls
You need to make api calls after getInitialState is run. For your case I would do it in componentDidMount. Follow this example, https://facebook.github.io/react/tips/initial-ajax.html.
I'd also recommend reading up on the component lifecycle with react. https://facebook.github.io/react/docs/component-specs.html.
Rewrite with modifications and loading state
Personally I don't like to do the whole if else then logic in the render and prefer to use 'loading' in my state and render a font awesome spinner before the form loads, http://fortawesome.github.io/Font-Awesome/examples/. Here's a rewrite to show you what I mean. If I messed up the ticks for cjsx, it's because I normally just use coffeescript like this, .
window.Pages ||= {}
Pages.AwayMessages = React.createClass
getInitialState: ->
{ loading: true, awayMessage: {} }
componentDidMount: ->
App.API.fetchAwayMessage (data) =>
#setState awayMessage:data.away_message, loading: false
onToggleCheckbox: (event)->
#state.awayMessage.master_toggle = event.target.checked
#setState(awayMessage: #state.awayMessage)
onTextChange: (event) ->
#state.awayMessage.text = event.target.value
#setState(awayMessage: #state.awayMessage)
onSubmit: (e) ->
# Not sure what this is for. I'd be careful using globals like this
window.a = #
#submitAwayMessage(#state.awayMessage)
submitAwayMessage: (awayMessage)->
console.log "I'm saving", awayMessage
App.API.saveAwayMessage awayMessage, (data) =>
if data.status == 'ok'
App.modal.closeModal()
notificationActions.notify("Away Message saved.")
#setState awayMessage:awayMessage
render: ->
if this.state.loading
`<i className="fa fa-spinner fa-spin"></i>`
else
`<div className="away-messages">
<div className="header">
<h4>Away Messages</h4>
</div>
<div className="content">
<div className="input-group">
<label for="master_toggle">On?</label>
<input type="checkbox" onChange={this.onToggleCheckbox} checked={this.state.awayMessage.master_toggle} />
</div>
<div className="input-group">
<label for="text">Text</label>
<input ref="text" onChange={this.onTextChange} value={this.state.awayMessage.text} />
</div>
</div>
<div className="footer">
<button className="button2" onClick={this.close}>Close</button>
<button className="button1" onClick={this.onSubmit}>Save</button>
</div>
</div>
That should about cover it. Now that is one way to go about forms which uses state and value. You can also just use defaultValue instead of value and then use refs to get the values when you submit. If you go that route I would recommend you have an outer shell component (usually referred to as high order components) to fetch the data and then pass it to the form as props.
Overall I'd recommend reading the react docs all the way through and do some tutorials. There's lots of blogs out there and http://www.egghead.io had some good tutorials. I have some stuff on my site as well, http://www.openmindedinnovations.com.
it's extremely simple, make defaultValue and key the same:
<input defaultValue={myVal} key={myVal}/>
This is one of the recommended approaches at https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
To force the defaultValue to re-render all you need to do is change the key value of the input itself. here is how you do it.
<input
type="text"
key={myDynamicKey}
defaultValue={myDynamicDefaultValue}
placeholder="It works"/>
Maybe not the best solution, but I'd make a component like below so I can reuse it everywhere in my code. I wish it was already in react by default.
<MagicInput type="text" binding={[this, 'awayMessage.text']} />
The component may look like:
window.MagicInput = React.createClass
onChange: (e) ->
state = #props.binding[0].state
changeByArray state, #path(), e.target.value
#props.binding[0].setState state
path: ->
#props.binding[1].split('.')
getValue: ->
value = #props.binding[0].state
path = #path()
i = 0
while i < path.length
value = value[path[i]]
i++
value
render: ->
type = if #props.type then #props.type else 'input'
parent_state = #props.binding[0]
`<input
type={type}
onChange={this.onChange}
value={this.getValue()}
/>`
Where change by array is a function accessing hash by a path expressed by an array
changeByArray = (hash, array, newValue, idx) ->
idx = if _.isUndefined(idx) then 0 else idx
if idx == array.length - 1
hash[array[idx]] = newValue
else
changeByArray hash[array[idx]], array, newValue, ++idx
Related issue
Setting defaulValue on control din't not update the state.
Doing reverse works perfectly:
Set state to default value, and the control UI gets updated correctly as if defaulValue was given.
Code:
let defaultRole = "Owner";
const [role, setRole] = useState(defaultRole);
useEffect(() => {
setMsg(role);
});
const handleChange = (event) => {
setRole(event.target.value );
};
// ----
<TextField
label="Enter Role"
onChange={handleChange}
autoFocus
value={role}
/>
Define a state for your default value
Surround your input with a div and a key prop
Set the key value to the same value as the defaultValue of the input.
Call your setDefaultValue defined at the step 1 somewhere to re-render your component
Example:
const [defaultValue, setDefaultValue] = useState(initialValue);
useEffect(() => {
setDefaultValue(initialValue);
}, false)
return (
<div key={defaultValue}>
<input defaultValue={defaultValue} />
</div>
)
Give value to parameter "placeHolder".
For example :-
<input
type="text"
placeHolder="Search product name."
style={{border:'1px solid #c5c5c5', padding:font*0.005,cursor:'text'}}
value={this.state.productSearchText}
onChange={this.handleChangeProductSearchText}
/>
Use value instead of defaultValue and change the value of the input with the onChange method.