Getting values from FormPanel in ExtReact 6.6.0 - javascript

How should I be getting values from a FormPanel using ext-react 6.6.0?
According to the API documentation I should be using getValues function, that works in 6.5.1 but I get error _this.form.getValues is not a function in 6.6.0
Code
Works in 6.5.1: https://fiddle.sencha.com/?extreact#view/editor&fiddle/2n05
Fails in 6.6.0 (see console for error): https://fiddle.sencha.com/?extreact#view/editor&fiddle/2n04

I get error _this.form.getValues is not a function in 6.6.0
The reason ref={form => this.form = form}. In extreact-6.6.0 the form variable is not exact formpanel. So for this you need to access like this
ref={form => this.form = (this.form || form.cmp)}}
Another way you use button.up('formpanel') to get the formpanel component. This button is first parameter of your handler.
button.up('formpanel').getValues()
You can check here with working fiddle.
Code Snippet
import React, { Component } from 'react';
import {launch} from '#sencha/ext-react';
import { ExtReact } from '#sencha/ext-react';
import { Container, Label, FormPanel, TextField, Button } from '#sencha/ext-modern';
class App extends Component {
state = {
values:JSON.stringify({
fname: 'null',
lname: 'null'
})
}
submit = (btn) => {
const values = btn.up('formpanel').getValues();
console.log('Values using up selector',values);
console.log('Values using up ref',this.form.getValues());
this.setState({values:JSON.stringify(this.form.getValues())});
}
render() {
return (
<Container defaults={{ margin: 10, shadow: true }}>
<FormPanel title="Form" ref={form => this.form = (this.form || form.cmp)}>
<TextField name="fname" label="First Name"/>
<TextField name="lname" label="Last Name"/>
<Button handler={this.submit} text="Submit"/>
</FormPanel>
<Label padding={'10'} html={this.state.values} />
</Container>
)
}
}
launch(<ExtReact><App /></ExtReact>);

Related

Using nested object names in formik/<ErrorMessage/>

Formik's documentation states you can use a lodash type dot path to name/access nested objects (e.g. name.firstName). This is also supposed to apply to it's built in <ErrorMessage/> component. I was working with a React-Typescript tutorial app that uses formik for form inputs and it seemed to work fine under the hood when paired with backend code, but I did notice that the fields that fed nested object values would not throw any errors in the UI. The error itself would be generated, but the <ErrorMessage/> component didn't seem to want to render.
A pared down version of the app is below. The "Field is Required" error should be thrown if you exit a form field without a valid input but again it doesn't work for the nested object fields (first/last name). I was wondering if anyone else has run across this issue. It's a little annoying.
I've seen that formik seems to be paired frequently with Yup for validation, which may make this issue moot, but I haven't gotten quite that far yet. Thanks!
import React from 'react';
import ReactDOM from 'react-dom';
import { ErrorMessage, Field, FieldProps, Formik } from "formik";
import { Form, Button } from "semantic-ui-react";
interface TextProps extends FieldProps {
label: string;
placeholder: string;
}
const TextField: React.FC<TextProps> = ({
field, label, placeholder
}) => (
<Form.Field>
<label>{label}</label>
<Field placeholder={placeholder} {...field} />
<div style={{ color: "red" }}>
<ErrorMessage name={field.name} />
</div>
</Form.Field>
);
const App: React.FC = () => {
return (
<Formik
initialValues={{
name: {
firstName: "",
lastName: ""
},
job: "",
}}
onSubmit={()=>console.log("submitted")}
validate={values => {
const requiredError = "Field is required";
const errors: { [field: string]: string } = {};
if (!values.name.firstName) {
errors["name.firstName"] = requiredError;
}
if (!values.name.lastName) {
errors["name.lastName"] = requiredError;
}
if (!values.job) {
errors["job"] = requiredError;
}
return errors;
}}
>
{({ isValid, dirty}) => {
return (
<Form>
<Field label="First Name" name="name.firstName" component={TextField} />
<Field label="Last Name" name="name.lastName" component={TextField} />
<Field label="Job" name="job" component={TextField} />
<Button type="submit" disabled={!dirty || !isValid}> Add</Button>
</Form>
);
}}
</Formik>
);
};
Have you tried using formik's inbuilt function getIn()? It is used to extract values from deeply nested objects. Check this Q&A
Try changing validate function to this
validate={values => {
const requiredError = "Field is required";
const errors: any = {name: {}};
if (!values.name.firstName) {
errors.name.firstName = requiredError;
}
if (!values.name.lastName) {
errors.name.lastName = requiredError;
}
if (!values.job) {
errors["job"] = requiredError;
}
return errors;
}}

Using react-dates with redux-form results in an error

I am trying to use react-dates with redux-form. Did a render thing for it. I have handled text input and select fields pretty much the same way. Those are working fine.
Getting a funky error on either DateRangePicker or even SingleDatePicker, which I cannot make sense of. Any ideas/suggestions are greatly appreciated.
Did a render component as:
const renderDateRangePicker = ({
input,
focusedInput,
onFocusChange,
startDatePlaceholderText,
endDatePlaceholderText
}) => (
<DateRangePicker
onDatesChange={(start, end) => input.onChange(start, end)}
onFocusChange={onFocusChange}
startDatePlaceholderText={startDatePlaceholderText}
endDatePlaceholderText={endDatePlaceholderText}
focusedInput={focusedInput}
startDate={(input.value && input.value.startDate) || null}
startDateId="startDateId"
endDateId="endDateId"
endDate={(input.value && input.value.endDate) || null}
minimumNights={0}
/>
)
My class is just a form as:
class ActivityForm extends Component {
// values: ['startDate', 'endDate']
state = {
focusedInput: null
}
onFocusChange(focusedInput) {
this.setState({ focusedInput });
}
render () {
const { focusedInput } = this.state
const { handleSubmit, teams } = this.props
return (
<form onSubmit={handleSubmit} className="activity__form">
<div className="activity__form_row">
<Field
name="name"
label="Activity name"
component={renderTextField}
margin="normal"
validate={[required]}
className="activity__form_field_name"
InputLabelProps={{
shrink: true,
}}
/>
<div className="activity__form_spacer"/>
<Field
name="daterange"
onFocusChange={this.onFocusChange}
focusedInput={focusedInput}
component={renderDateRangePicker}
/>
<div className="activity__form_spacer"/>
<Button className="activity__form_button" type="submit">Save</Button>
</div>
</form>
)
}
}
export default reduxForm({ form: 'activity' })(ActivityForm)
For some reason, DateRangePicker causes a strange error: Uncaught TypeError: Cannot read property 'createLTR' of undefined.
What am I missing?
I believe this error is caused by missing or misplaced import of the initialization of react-dates, you can take a look at the Initialize section in (https://github.com/airbnb/react-dates)
import 'react-dates/initialize';
It also looks like there is an update to DateRangePicker:
So include starteDateId and endDateId as props to the DateRangePicker component.
<DateRangePicker
startDateId="2" // PropTypes.string.isRequired,
endDateId="1" // PropTypes.string.isRequired,
startDate={this.props.filters.startDate}
endDate={this.props.filters.endDate}
onDatesChange={this.onDatesChange}
focusedInput={this.state.calendarFocused}
onFocusChange={this.onFocusChange}
showClearDates={true}
numberOfMonths={1}
isOutsideRange={() => false}
/>
It worked for me.

Trying to re-use component in other class and getting error: "Warning: setState(...): Can only update a mounted or mounting > component. "

I'm putting together a little POC, where one piece of it is executing a Search function. The idea is that "Search" will be responsible for the following things:
- Displaying the search input form (e.g., text, date and locations parameters)
- Hit the backend AWS Lambda search API
- Return the result object back to the Search object
- Be able to be re-used on multiple pages
I have two different pages that I want to leverage the "search" functionality, but render the results in different ways.
The search component/form works stand-alone, but I can't figure out how to embed it on the other web pages. Whenever I try to input anything into the "Search form" the console throws the following error:
index.js:2178 Warning: setState(...): Can only update a mounted or mounting > component. This usually means you called setState() on an unmounted component. > This is a no-op.
Please check the code for the Search component.
The "Search" code is below, along with the start of page to display results. I'm a novice to front-end dev so I may be doing something stupid here...looking for input on what I'm doing wrong! Thanks for the help.
import React, { Component } from "react";
import {FormGroup, FormControl, ControlLabel } from "react-bootstrap";
import LoaderButton from "../components/LoaderButton";
import "./Home.css";
import DatePicker from 'react-datepicker';
import moment from 'moment';
import PlacesAutocomplete from 'react-places-autocomplete';
import { searchAssets } from "../libs/awsLib";
import 'react-datepicker/dist/react-datepicker.css';
// CSS Modules, react-datepicker-cssmodules.css
import 'react-datepicker/dist/react-datepicker-cssmodules.css';
export default class Search extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
freeText: "",
startDate: null,
endDate: null,
radius: 5,
address: '',
searchResults: null
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleStartDateChange = this.handleStartDateChange.bind(this);
this.handleEndDateChange = this.handleEndDateChange.bind(this);
this.handleLocationChange = this.handleLocationChange.bind(this);
}
// Basic change handling
handleChange = event => {
this.setState({ [event.target.id]: event.target.value });
}
// Location change needed because event.id cannot be taken from places object
handleLocationChange = address => {
console.log("The address has been changed: " + address);
this.setState({ address: address });
}
// I need two separate handlers for each piece of the date range
handleStartDateChange = date => {
this.setState({ startDate: date });
}
handleEndDateChange = date => {
this.setState({ endDate: date });
}
//
handleSubmit = async event => {
event.preventDefault();
console.log(event);
// Construct the query string for AWS search
var queryStatement = {
accountId: 'account1', // Dummy
groupId: 'group1', // Dummy
caseId: '999', // Dummy
freeText: this.state.freeText,
radius: this.state.radius
};
if (this.state.startDate != null) {
queryStatement['startDate'] = this.state.startDate.format("MM/DD/YYYY");
}
if (this.state.endDate != null) {
queryStatement['endDate'] = this.state.endDate.format("MM/DD/YYYY");
}
if (this.state.address !== '') {
queryStatement['address'] = this.state.address
}
console.log(queryStatement);
this.setState({ isLoading: true });
// Submit to the search API and load the Payload as a JSON object
try {
var resultSet;
resultSet = await searchAssets(queryStatement);
if (resultSet['StatusCode'] !== 200) {
console.log("Error in lambda function");
}
console.log(JSON.parse(resultSet['Payload']));
this.setState({
isLoading: false,
searchResults: JSON.parse(resultSet['Payload'])
});
}
catch (e) {
console.log(e);
this.setState({ isLoading: false });
}
}
render() {
const autoLocationProps = {
value: this.state.address,
onChange: this.handleLocationChange,
}
console.log(this.state);
// Only fetch suggestions when the input text is longer than 3 characters.
const shouldFetchSuggestions = ({ value }) => value.length > 3
return (
<div className="Search">
<form onSubmit={this.handleSubmit}>
<FormGroup controlId="freeText" bsSize="large">
<ControlLabel>Enter text to search on</ControlLabel>
<FormControl
type="textarea"
onChange={this.handleChange}
value={this.state.freeText}
placeholder="Enter any values to search on"
/>
</FormGroup>
<FormGroup controlId="address" bsSize="large">
<ControlLabel>Enter search location</ControlLabel>
<PlacesAutocomplete
inputProps={autoLocationProps}
shouldFetchSuggestions={shouldFetchSuggestions}
placeholderText="Start typing an address"
/>
</FormGroup>
<FormGroup controlId="radius" bsSize="large">
<ControlLabel>Enter search radius</ControlLabel>
<FormControl
onChange={this.handleChange}
type="text"
value={this.state.radius}
/>
</FormGroup>
<FormGroup controlId="startDate" bsSize="large">
<ControlLabel>Enter start date</ControlLabel>
<DatePicker
onChange={this.handleStartDateChange}
selected={this.state.startDate}
placeholderText="Enter start date"
isClearable={true}
maxDate={this.state.endDate}
/>
</FormGroup>
<FormGroup controlId="endDate" bsSize="large">
<ControlLabel>Enter end date</ControlLabel>
<DatePicker
onChange={this.handleEndDateChange}
selected={this.state.endDate}
placeholderText="Enter end date"
isClearable={true}
minDate={this.state.startDate}
/>
</FormGroup>
<LoaderButton
block
bsSize="large"
type="submit"
isLoading={this.state.isLoading}
text="Search for files"
loadingText="Searching..."
/>
</form>
</div>
);
}
}
View as a table:
import React, { Component } from "react";
import {Table, thead, tbody, th, tr, td } from "react-bootstrap";
import Search from "./Search";
import "./Home.css";
export default class Viewfiles extends Component {
constructor(props) {
super(props);
}
render() {
// Eventually we will render the results in the table...
var resultsObject = new Search();
return (
<div className="Viewfiles">
{resultsObject.render()}
<hr/>
<Table striped condensed responsive >
<thead>
<tr>
<th>Thumbnail</th>
<th>Checksum</th>
<th>Address</th>
<th>Checksum</th>
<th>Description</th>
</tr>
</thead>
</Table>
</div>
);
}
}
React components are meant to be rendered in JSX, like <Search />. If you instantiate them on your own, React won't know to mount them properly, with state and everything.

Trying to wrap child elements on React

I'm doing a form helper to react-native, I also want to implement a feature called namespace to the forms to get a modular result.
An example:
return(
<Form
ref="configForm"
>
<Form.TextInput
name="firstName"
/>
<Form.TextInput
name="lastName"
/>
<Form.Namespace name="address">
<Form.TextInput
name="city"
/>
<Form.TextInput
name="address"
/>
<Form.TextInput
name="zip"
/>
</Form.Namespace>
</Form>
);
In this case the final result must be:
{
firstName: 'John',
lastName: 'Tompson',
address: {
city: 'New York',
address: '3th Av 231',
zip: '32132'
}
}
To get fields values I map recursivery the children of FormNamespace, and I clone the elements that own the property isFormValue (this property is owned by elements that provide values to Form, wich are FormNamespace and FormField):
wrapChildren(child) {
if (!child) {
return child;
}
if (Array.isArray(child)) {
return child.map((c) => this.wrapChildren(c));
}
if (child.type.isFormValue) {
let ref = child.ref || child.props.name;
child = React.cloneElement(child, {
ref: ref,
style: {borderWidth: 1, borderColor: 'red'}, // for debug
onUpdateValue: this.handleFieldChange.bind(this, ref),
});
}
if (child.props.children && !child.type.isFormStopableWrapper) {
child.props.children = this.wrapChildren(child.props.children);
}
return child;
}
render() {
let childs = this.wrapChildren(this.props.children);
debugger;
return(
<View style={this.props.style}>
{childs}
</View>
);
};
All logic is in FormNamespace component, in fact, the Form component only renders a FormNamespace:
render() {
return(
<Namespace
ref="formData"
name="mainNamespace"
style={[styles.form, this.props.style]}
onUpdateValue={(data) => this._handleUpdateData(data)}
>
{this.props.children}
</Namespace>
);
};
The Form component works correctly on the first depth, but when must to clone the FormNamespace element, by some issue it does not work as it should.
Debugging, I checked the React.cloneElement is applied to the FormNamespace but in the final render the FormNamespace never is referenced (nor the border style is applied), unlike the FormField that works well.
I don't know what is wrong here, the final FormNamespace rendered has not define the onUpdateValue prop. The objetive is the FormNamespace be taken like a simple value provider to Form (like fields components) and this manage theirs own childs.
The complete component code are here:
FormNamespace: https://paste.kde.org/pyz5gdsgb
Form: https://paste.kde.org/p4kmc2k9j

React - Unable to click or type in input form using react-tagsinput and react-mailcheck

I am using react-tagsinput, react-input-autosize, and react-mailcheck to create input tags that also suggests the right domain when the user misspell it in an email address.
I have gotten the react-tagsinput to work with react-input-autosize but when added with react-mailcheck my input form does not work at all, the form is un-clickable and unable to type and text into the field. I'm not getting any errors in the console and i'm not sure what is wrong with my code. I followed what they did in the react-mailcheck documentation: https://github.com/eligolding/react-mailcheck. I was hoping someone could look at it with a fresh pair of eyes to see what I am missing that is making it not work.
Thanks!
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import TagsInput from 'react-tagsinput';
import AutosizeInput from 'react-input-autosize';
import MailCheck from 'react-mailcheck';
class EmailInputTags extends Component {
static propTypes = {
label: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
};
constructor() {
super();
this.state = { tags: [], inputText: '' };
this.handleChange = this.handleChange.bind(this);
this.handleInputText = this.handleInputText.bind(this);
this.renderInput = this.renderInput.bind(this);
}
handleChange(tags) {
this.setState({ tags });
}
handleInputText(e) {
this.setState({ inputText: e.target.value });
}
renderInput({ addTag, ...props }) {
const { ...other } = props;
return (
<MailCheck email={this.state.inputText}>
{suggestion => (
<div>
<AutosizeInput
type="text"
value={this.state.inputText}
onChange={this.handleInputText}
{...other}
/>
{suggestion &&
<div>
Did you mean {suggestion.full}?
</div>
}
</div>
)}
</MailCheck>
);
}
render() {
const { label, name } = this.props;
return (
<div className="input-tag-field">
<TagsInput inputProps={{ placeholder: '', className: 'input-tag' }} renderInput={this.renderInput} value={this.state.tags} onChange={this.handleChange} />
<label htmlFor={name}>{label}</label>
</div>
);
}
}
export default EmailInputTags;
I have not tried this out.
Try passing as a prop to TagsInput the onChange function.
ie
{... onChange={(e) => {this.setState(inputText: e.target.value}}

Categories

Resources