react office js textfield controlled component - reactjs

i tried to use officejs react component in it and for osme reason i cant get it to work properly..effective here is the code. it works in codepen but when i put hte same code in excel addin project, i cant get the value in the textfields.
Code in codepen(it works):
[https://codepen.io/manish_shukla01/pen/ReWWmM][1]
Code in my project in app file(does not work in the sense that handlechange events are not getting fired i believe so value of my state.value1 remains blank even when i input anything):
import * as React from 'react';
import { Button, ButtonType } from 'office-ui-fabric-react';
import Header from './Header';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
import * as OfficeHelpers from '#microsoft/office-js-helpers';
export default class App extends React.Component<any,any,any>{
constructor(props) {
super(props);
this.state = {
value1: '',
value2:'',
message:'Helloooo'
};
this.handleChange1 = this.handleChange1.bind(this);
this.handleChange2 = this.handleChange2.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange1(event) {
this.setState({value1: event.target.value});
}
handleChange2(event) {
this.setState({value2: event.target.value});
}
handleSubmit = async () => {
event.preventDefault();
this.setState({message: 'i got clicked'});
try {
//event.preventDefault();
await Excel.run(async context => {
/**
* Insert your Excel code here
*/
const range = context.workbook.getSelectedRange();
// Read the range address
range.load('address');
// Update the fill color
range.format.fill.color = 'blue';
range.values = [[this.state.value2]];
await context.sync();
console.log(`The range address was ${range.address}.`);
});
} catch(error) {
OfficeHelpers.UI.notify(error);
OfficeHelpers.Utilities.log(error);
};
}
render() {
return (
<form className='ms-welcome' onSubmit={this.handleSubmit}>
<Header logo='assets/logo-filled.png' title='Excel Analytics' message={this.state.message} />
<TextField label="field1"
value={this.state.value1} onChange={this.handleChange1}
required
/>
<Button className='ms-welcome__action' buttonType={ButtonType.primary}
onClick={this.handleSubmit}>Run
</Button>
</form>
);
}
}

I also faced same issue with Office UI Fabric TextField's "onChange" event and solved it using "onChanged" instead, which is deprecated as they say. But it worked for me.
First, add onChanged handler to TextField as below:
<TextField name="fieldName" label="field1" value={this.state.value1} onChanged={val => this.handleChange1("fieldName", val)} />
Also, note that "name" attribute is added to identify control in handleChange1.
Now change handler implementation as below:
handleChange1(name, value) {
this.setState(prevState => ({
result: {
...prevState.result,
[name]: value
}
}));
}
Hope this helps. Thanks!

Related

I want to pass my input values from Form to handleSubmit ()

I want to pass my input values from form to handleSubmit () currently I am passing e.target.value and getting an error cannot property 'value' of undefined.
following is the form code block from where I want to get the values
<Input
label="Write a message..."
name="message"
type="text"
/>
and the following is the code block from where I am trying to access the value under
handleSubmit = (e) => {
this.props.sendNewMessage(e.target.value);
}
Full code for reference :
import React from 'react';
import SubMenu from './SubMenu';
import MessageForm from './form/MessageForm';
import { sendNewMessage } from '../../actions/messages.actions'
import {connect} from 'react-redux';
class Messages extends React.PureComponent {
handleSubmit = (e) => {
this.props.sendNewMessage(e.target.value);
}
render() {
return (
<section className="page-notifications">
<SubMenu/>
<MessageForm onSubmit={this.handleSubmit}/>
</section>
)
}
}
const mapDispatchToProps = dispatch => {
return {
sendNewMessage: (msg) => dispatch(sendNewMessage(msg)),
}
}
export default connect(null,mapDispatchToProps)(Messages)
This isn't how you should handle form submissions. Your messageForm should update your state using an onChange handler. Then handleSubmit should preventDefault() and dispatch your sendNewMessage action using the value(s) from state that have already been set.
The React docs are very helpful on this.
You need to bind your method for getting value from child component
constructer(props){
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
Now you shouldn't get undefined error
So #Will suggested me to add values to my handleSubmit(), it solved my problem thank you all.
following is code snippet where I made changes :
handleSubmit = (value) => {
this.props.sendNewMessage(value);
console.log(value)
}

how to open airbnb SingleDatePicker calendar on focus

Am trying to include a airbnb SingleDatePicker in my component as shown below
import "react-dates/initialize";
import "react-dates/lib/css/_datepicker.css";
import { SingleDatePicker } from "react-dates";
export default class ExpenseForm extends React.Component {
constructor(props) {
super(props);
this.state = {
createdAt: moment(),
calendarFocused: false
};
this.onDateChange = this.onDateChange.bind(this);
this.onFocusChange = this.onFocusChange.bind(this);
}
onDateChange(createdAt) {
this.setState(() => ({ createdAt }));
}
onFocusChange({ focused }) {
this.setState(() => ({
focused
}));
}
render() {
return (
<div>
<SingleDatePicker
date={this.state.createdAt}
startDateId="MyDatePicker"
onDateChange={this.onDateChange}
focused={this.state.calendarFocused}
onFocusChange={this.onFocusChange}
id="SDP"
/>
</div>
);
}
}
It shows the current date inside the inside the input box as shown below but when i click on the field nothing happens(Calendar widget not opening).
Am not getting any errors in the console to figure out what is the issue. Could anyone please help me on fixing this issue?
It looks like you are targeting the wrong state key in your onFocusChange function. Try changing the function to this:
onFocusChange({ focused }) {
this.setState({ calendarFocused: focused });
}
Notice I also removed the anonymous function from inside of your setState call. You can remove it from your onDateChange function as well:
onDateChange(createdAt) {
this.setState(({ createdAt }));
}
I also noticed that you're using the startDateId prop that is not available for the SingleDatePicker. Airbnb uses that for the DateRangePicker to determine which input to focus on. There is only one input in the SingleDatePicker, so it does not require that prop. You can remove that prop without any change to your functionality.
1- correct this handler
onFocusChange = ({ focused }) => {
this.setState(() => ({ calendarFocused: focused }));
};
2- import 'react-dates/initialize'; //dependency as of v13.0.0
3-since u r not using webpack
Create a CSS file with the contents of require.resolve('react-dates/lib/css/_datepicker.css') and include it in your html section.
To see this in action, you can check out https://github.com/majapw/react-dates-demo which adds react-dates on top of a simple create-react-app setup.

onChange not firing in React app

I'm having a difficult time getting my onChange event to fire when a field within a form in my component is changed. When i make a change in the form the edit is allowed but i get the following error in the browser console
bundle.js:57140 Uncaught TypeError: Cannot set property '' of null
Any thoughts on how to resolve this issue would be of great help!
import React from 'react';
import ReactDOM from 'react-dom';
import autoBind from 'react-autobind';
import Form from 'grommet/components/Form';
import TextInput from 'grommet/components/TextInput';
import NumberInput from 'grommet/components/NumberInput';
import DateTime from 'grommet/components/DateTime';
import FormFields from 'grommet/components/FormField';
export default class OverviewEditPane extends React.Component {
constructor(props) {
super(props);
autoBind(this);
this.onOverviewChange = this.onOverviewChange.bind(this)
}
onOverviewChange(event) {
let state = this.state;
let field = event.target.name;
let value = event.target.value;
console.log(field);
state[field] = value;
this.setState({state});
}
render () {
return (
<table>
<FormFields>
<tbody>
<tr>
<td>{this.props.overview.map((P) => {return <TextInput size='small' key={P.id} id={P.id} value={P.FName} onChange={this.onOverviewChange} />;})}</td>
</tr>...
{ state } is shorthand for { state: state }. What you really want to do is update just one field in the state, not set the entire current state as state key.
Also make sure you don't mutate the state object directly.
onOverviewChange(event) {
const { name, value } = event.target;
this.setState({ [name]: value });
}
What you're trying to achieve here is wrong, but what you need (want) to do is probably this:
//Here's your updated function:
onOverviewChange(e) {
const { name, value } = e.target; // Dectructuring name and value event
this.setState({ [name]: value }); // Setting the state to it's new key/ value pairs
}
.... later in your code, you'll use this function to trigger an onChange method, something like this:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.onOverviewChange = this.onOverviewChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
onOverviewChange(e) {
const { name, value } = e.target; // Dectructuring name and value event
this.setState({ [name]: value }); // Setting the state to it's new key/ value pairs
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text"
// Here we associate it with our state
name="value"
// Here's where we make use of our function
onChange={this.onOverviewChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(<Example/>, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>

Formsy-material-ui do not validate initial render

Is there any way, one can delay first validation of components in formsy-material-ui so that validations like isNotEmpty do not fire on first render of the form and mess the UX? I am using controlled components, therefore setting value from state on each render.
<FormsyText
name="name"
value={this.state.name}
floatingLabelText="Name"
onChange={partial(this._changeInputValue, ['name'])}
validations={{ isNotEmpty }}
validationError="Field shoud not be empty"
/>
I needed this solution too. I've been looking into the source code of formsy-material-ui, and it seems that the text field is setting its value right before it's mounted. That's why the field is marked changed (aka not pristine) when the rendering happens, so the validation error is shown.
Anyways, I wrote a hackish solution using a higher order component. I've been testing with text fields only, but should work with any fields having this problem. The core concept: if the formsy field doesn't have a "validationErrors" prop, it's not showing any errors.
import React, { Component, PropTypes } from 'react';
export const preventFirstValidation = (FormsyField) => {
return class extends Component {
static propTypes = { preventFirstValidation: PropTypes.bool };
static defaultProps = { preventFirstValidation: true };
constructor(props) {
super(props);
this.state = { isChanged: false };
}
render() {
const { preventFirstValidation, ...fieldProps } = this.props;
return (
<FormsyField
{...fieldProps}
onChange={(evt, val) => {
if (!this.state.isChanged) this.setState({ isChanged: true });
if (this.props.onChange) this.props.onChange(evt, val);
}}
validationErrors={(this.state.isChanged || !preventFirstValidation) ? this.props.validationErrors : undefined}
/>
);
}
};
};
How to use it:
import { Form } from 'formsy-react';
import FormsyTextField from 'formsy-material-ui/lib/FormsyText';
const TextField = preventFirstValidation(FormsyTextField);
const MyForm = () => (
<Form>
{/* You're using the transformed field, exactly like before */}
<TextField
name = "some_field"
validationErrors={{ isRequired: 'This is really required!' }}
required
preventFirstValidation={ /* you can enable or disable this feature */ }
/>
</Form>
);

How to onFocus and onBlur a React/Redux form field that's connected to React Date Picker?

I've got this simple v6 redux-form with an input that renders a custom component that populates its value using react-day-picker.
https://github.com/gpbl/react-day-picker
I chose react-day-picker over others because it doesn't depend on moment.js and works with my current set up.
When I focus the field, I want the datepicker to pop up, but if I click anywhere that's not the datepicker, I want it to disappear.
Essentially, I want my React datepicker to work like the jQueryUI one in:
https://jqueryui.com/datepicker/
The three scenarios I end up with are:
I click the field, the datepicker pops up, but will not disappear unless I select a date or click the field again (this is too rigid for our needs).
I click the field, the datepicker pops up, but will disappear TOO quickly if I click anywhere, as the input field's onBlur gets called before it processes the click event for the datepicker to populate the field with the chosen date.
I click the field, the datepicker pops up, gets auto-focused, blurs properly, except when I click anything that's not < body> or the datepicker.
I first tried to use a sibling empty div that wraps the whole page, so when I click the empty div, it'll toggle the datepicker properly. This worked OK with z-indexes and position: fixed until I changed the datepicker's month, which seems to re-render the datepicker and messed with the order of the clicking, which led to situation 2) again.
My most current attempt is to auto-focus the datepicker div when it pops up, so when I blur anything that's not the datepicker, it will toggle the datepicker. This worked in theory, except the datepicker is a component with many nested < div>'s inside it to control day, week, month, disabled days... so when I click a 'day', it registers a blur because I'm clicking the 'day' <div>, not the root 'datepicker' <div>, which is what was initially focused.
The solution to the above was to tweak 'datepicker' <div>'s onBlur such that it will only toggle the datepicker when document.activeElement is < body>, but that only works if I don't click another form field.
WizardFormPageOne.js:
function WizardFormPageOne({ handleSubmit }) {
return (
<form onSubmit={handleSubmit} className="col-xs-6">
<h1>WizardFormPageOne</h1>
<div className="card">
<div className="card-block">
<div className="form-group">
<label htmlFor="first">Label 1</label>
<Field type="text" name="first" component={DateInput} className="form-control" />
</div>
...
export default reduxForm({
form: 'wizardForm',
destroyOnUnmount: false,
})(WizardFormPageOne);
DateInput.js:
import React from 'react';
import styles from './styles.css';
import DatePicker from '../DatePicker';
class DateInput extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
dateValue: new Date(),
activeDateWidget: false,
};
}
changeDate = (date) => {
this.setState({
dateValue: date,
});
}
changeActiveDateWidget = (e) => {
e.stopPropagation();
this.setState({
activeDateWidget: !this.state.activeDateWidget,
});
}
render() {
const { input, meta } = this.props;
const { dateValue, activeDateWidget } = this.state;
return (
<div className={styles.dateInput}>
<input
{...input}
className="form-control"
type="text"
value={dateValue}
onClick={this.changeActiveDateWidget}
// onBlur={this.changeActiveDateWidget}
/>
{activeDateWidget ? (
<div>
<DatePicker
changeActiveDateWidget={this.changeActiveDateWidget}
changeDate={this.changeDate}
dateValue={dateValue}
/>
</div>
) : (
<div></div>
)}
</div>
);
}
}
export default DateInput;
DatePicker.js:
import React from 'react';
import 'react-day-picker/lib/style.css';
import DayPicker, { DateUtils } from 'react-day-picker';
import styles from './styles.css';
import disabledDays from './disabledDays';
class DatePicker extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
selectedDay: new Date(),
};
}
componentDidMount() {
if (this._input) {
this._input.focus();
}
}
handleDayClick = (e, day, { disabled }) => {
e.stopPropagation();
if (disabled) {
return;
}
this.setState({ selectedDay: day }, () => {
this.props.changeDate(day);
this.props.changeActiveDateWidget();
});
}
focusThisComponent = (e) => {
if (e) {
this._input = e;
}
}
focusIt = () => {
console.log('focusing');
}
blurIt = () => {
console.log('blurring');
setTimeout(() => {
if (document.activeElement == document.body) {
this.props.changeActiveDateWidget();
}
}, 1);
}
render() {
const { changeActiveDateWidget } = this.props;
const { selectedDay } = this.state;
return (
<div
className={styles.datePicker}
ref={this.focusThisComponent}
tabIndex="1"
onFocus={this.focusIt}
onBlur={this.blurIt}
>
<DayPicker
id="THISTHING"
initialMonth={selectedDay}
disabledDays={disabledDays}
selectedDays={(day) => DateUtils.isSameDay(selectedDay, day)}
onDayClick={this.handleDayClick}
/>
</div>
);
}
}
export default DatePicker;
Here's a screencast of the issue I'm having now:
http://screencast.com/t/kZuIwUzl
The datepicker toggles properly, except when clicking on another field, at which point it stops blurring/toggling properly. All my tinkering either led me make to one of the three scenarios listed above.
Bit old question but I couldn't find simple answer so here is what I did:
onBlur={(e) => {
var picker = document.querySelector(".DayPicker")
if(!picker.contains(e.relatedTarget)){
this.setState({showDayPicker: false})
}
}}
I'm setting a flag that hides DayPicker if the blur doesn't come from clicking on the DayPicker, otherwise it keeps the DayPicker displayed
You can take this example http://react-day-picker.js.org/examples/?overlay and do some small modifications to make it redux-form v6 compatible. Instead of using local state you should use this.props.input.value provided by redux-form Field component inside your render function. Additionally in the handleInputChange event handler you have to call this.props.input.onChange(e.target.value) instead of this.setState({ value: e.target.value }) and in handleInputBlur event call this.props.input.onBlur(e.target.value). That's all you have to do to make it work as a redux-form Field component.
I couldn't get Steffen's answer to work for my scenario, and the example in http://react-day-picker.js.org/examples/?overlay doesn't blur properly if you open the date picker, click the non-active parts of the date picker, then click outside the date picker. I may be nitpicking at this point, and his solution is probably far easier to implement, but here's what I did to solve it:
In DatePicker.js, set an empty array that will serve as a collection of valid < div>'s. When onBlur is triggered, invoke a recursive function that takes the root DatePicker < div>, parses all it's children, and add them to the empty array. After that, check document.activeElement to see if it's in the array. If not, then toggle the DatePicker widget, else, do nothing.
Note that the check for document.activeElement must be done one tick after the blur, or else activeElement will be < body>.
Related links:
Get the newly focussed element (if any) from the onBlur event.
Which HTML elements can receive focus?
/**
*
* DatePicker
*
*/
import React from 'react';
import 'react-day-picker/lib/style.css';
import DayPicker, { DateUtils } from 'react-day-picker';
import styles from './styles.css';
import disabledDays from './disabledDays';
class DatePicker extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
selectedDay: new Date(),
};
this.validElements = [];
}
componentDidMount() {
// Once DatePicker successfully sets a ref, component will mount
// and autofocus onto DatePicker's wrapper div.
if (this.refComponent) {
this.refComponent.focus();
}
}
setRefComponent = (e) => {
if (e) {
this.refComponent = e;
}
}
findDatePickerDOMNodes = (element) => {
if (element.hasChildNodes()) {
this.validElements.push(element);
const children = element.childNodes;
for (let i = 0; i < children.length; i++) {
this.validElements.push(children[i]);
this.findDatePickerDOMNodes(children[i]);
}
return;
}
}
handleDayClick = (e, day, { disabled }) => {
if (disabled) {
return;
}
this.setState({ selectedDay: day }, () => {
this.props.changeDate(day);
this.props.changeActiveDateWidget();
});
}
handleBlur = () => {
// Since DatePicker's wrapper div has been autofocused on mount, all
// that needs to be done is to blur on anything that's not the DatePicker.
// DatePicker has many child divs that handle things like day, week, month...
// invoke a recursive function to gather all children of root DatePicker div, then run a test against valid DatePicker elements. If test fails, then changeActiveDateWidget.
setTimeout(() => {
const rootDatePickerElement = document.getElementById('datePickerWidget');
this.findDatePickerDOMNodes(rootDatePickerElement);
if (!this.validElements.includes(document.activeElement)) {
this.props.changeActiveDateWidget();
}
}, 1);
}
render() {
const { selectedDay } = this.state;
return (
<div
className={styles.datePicker}
onBlur={this.handleBlur}
// tabIndex necessary for element to be auto-focused.
tabIndex="1"
ref={this.setRefComponent}
>
<DayPicker
initialMonth={selectedDay}
disabledDays={disabledDays}
selectedDays={(day) => DateUtils.isSameDay(selectedDay, day)}
onDayClick={this.handleDayClick}
id="datePickerWidget"
/>
</div>
);
}
}
DatePicker.propTypes = {
changeDate: React.PropTypes.func,
changeActiveDateWidget: React.PropTypes.func,
};
export default DatePicker;
and in DateInput.js, clicking the input may cause the toggle to trigger twice, so i just set it to always toggle true if clicking the input:
render() {
const { input, meta } = this.props;
const { dateValue, activeDateWidget } = this.state;
return (
<div className={styles.dateInput}>
<input
{...input}
className="form-control"
type="text"
value={dateValue}
onClick={() => { this.setState({ activeDateWidget: true }); }}
/>

Resources