I'm fairly new Ionic React and trying to create an App using Ionic framework. I have a use case where I would like to redirect to another page upon a button press and validation from a function.
Here is the component I am working with
import React, { useState } from 'react';
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonInput,
IonButton,
IonRow,
IonCol,
useIonViewWillEnter,
useIonViewWillLeave,
IonLabel
} from '#ionic/react';
import './LogIn.css'
import MainMenu from "../MainMenu";
const LogIn: React.FC<{register?: boolean; onClose?: any}> = () => {
const [email, setEmail] = useState<string>();
const [password, setPassword] = useState<string>();
function handleLoginButtonPress() {
if (validateEmail() && password !== undefined && password.trim() !== "") {
// Network call here to check if a valid user
console.log("valid email")
return <MainMenu/>
}
}
function validateEmail () {
if (email !== undefined && email.trim() !== "") {
const regexp = /^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return regexp.test(email);
}
return false;
}
useIonViewWillEnter(() => {
console.log("Entering LogIn");
});
useIonViewWillLeave(() => {
console.log("Exiting LogIn");
});
return (
<IonPage id="loginIonPage">
<IonHeader>
<IonToolbar color="primary" mode="md">
<IonTitle class="ion-text-center">Login or Register</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent id="loginPage">
<div id="loginDialog">
<form>
<IonLabel>Email</IonLabel>
<IonRow>
<IonCol id="userName">
<IonInput
name="email"
value={email}
onIonChange={e => setEmail(e.detail.value!)}
clearInput
type="email"
placeholder="Email"
class="input"
padding-horizontal
clear-input="true"
required/>
</IonCol>
</IonRow>
<IonLabel>Password</IonLabel>
<IonRow>
<IonCol id="password">
<IonInput
clearInput
name="password"
value={password}
onIonChange={e => setPassword(e.detail.value!)}
type="password"
placeholder="Password"
class="input"
padding-horizontal
clear-input="true"
required/>
</IonCol>
</IonRow>
</form>
</div>
<div id="buttons">
<IonButton
onClick={handleLoginButtonPress}
type="submit"
strong={true}
expand="full"
style={{ margin: 20 }}>
Log in
</IonButton>
<IonButton
routerLink="/registerUser"
type="submit"
strong={true}
expand="full"
style={{ margin: 20 }}>
Register
</IonButton>
</div>
</IonContent>
</IonPage>
);
};
export default LogIn;
How can I redirect to another page from the function handleLoginButtonPress which is invoked upon clicking on Login button. I tried reading through ionic doc but that did not help as it redirects whenever there is button click or click on an IonElement like IonLabel.
Use react-router-dom hooks
https://reacttraining.com/react-router/web/api/Hooks/usehistory
import { useHistory } from "react-router-dom";
const ExampleComponent: React.FC = () => {
const history = useHistory();
const handleLoginButtonPress = () => {
history.push("/main");
};
return <IonButton onClick={handleLoginButtonPress}>Log in</IonButton>;
};
export default ExampleComponent;
Thanks, #MUHAMMAD ILYAS and it would have worked for me. But I managed to leverage the react state as below, I have added as an answer as I could not format the code there,
const ValidateUser : React.FC = () => {
const [login, setLogin] = useState(false);
function isUserLoggedIn() {
return login;
}
return(
<IonReactRouter>
<IonRouterOutlet>
<Route path="/" render={() => isUserLoggedIn()
? <MainMenu/>
: <LogIn setUserLoggedIn={() => {setLogin(true)}}/>} exact={true}/>
</IonRouterOutlet>
</IonReactRouter>
);
};
const LogIn: React.FC<{setUserLoggedIn?: any}> = ({setUserLoggedIn}) => {
.
.
function onLoginButtonPress() {
setUserLoggedIn();
}
}
Thanks
Related
I was trying to take example from react but seem it doesn't work as I expected.
I am trying to open a modal when user click the button in parent component, but function to open modal is located in the child component.
Parent component just where i try to invoke a modal:
<label class="text-white m-5 p-1">
<input type="checkbox" checked={false} onChange={handleCheck} />
I have read and agree to the <button onClick={}>Privacy</button>
<Portal>
<TermsModal />
</Portal>
</label>
How to do that?
If you want the Modal component to control the state, instead of it being passed with props you can use this pattern:
const { Modal, openModal } = createModal();
return (
<>
<button type="button" onClick={openModal}>
open modal
</button>
<Modal />
</>
);
It may be weird at first, but it's really fun and powerful :)
https://playground.solidjs.com/anonymous/d3a74069-e3d4-4d3e-9fb2-bafc5d6f5bf5
Create a signal inside parent component and pass it to the child component. Bind the visibility of the modal window to the signal's value:
import { Component, createSignal, Show } from 'solid-js';
import { Portal, render } from 'solid-js/web';
const Modal: Component<{ show: boolean }> = (props) => {
return (
<Show when={props.show}>
<div>Modal Window</div>
</Show>
);
};
const App = () => {
const [show, setShow] = createSignal(false);
const toggleShow = () => setShow(prev => !prev);
return (
<div>
<div>Show: {show() ? 'true': 'false'} <button onClick={toggleShow}>Toggle Show</button></div>
<Portal><Modal show={show()} /></Portal>
</div>
)
};
render(() => <App />, document.body);
https://playground.solidjs.com/anonymous/adf9ba7a-3e1b-4ce9-92b2-e78ff3fa55ec
You can further improve your component by creating a close handler and passing it to the child component. Now, modal window can show a close button.
Also you can add an event handler to the document in order to close the window whenever Escape key is pressed:
import { Component, createSignal, onCleanup, onMount, Show } from 'solid-js';
import { Portal, render } from 'solid-js/web';
const Modal: Component<{ show: boolean, close: () => void }> = (props) => {
const handleKeydown = (event) => {
if (event.key === 'Escape') {
props.close();
}
};
onMount(() => {
document.addEventListener('keydown', handleKeydown);
});
onCleanup(() => {
document.removeEventListener('keydown', handleKeydown);
});
return (
<Show when={props.show}>
<div>Modal Window <button onclick={props.close}>close</button></div>
</Show>
);
};
const App = () => {
const [show, setShow] = createSignal(false);
const close = () => setShow(false);
const toggleShow = () => setShow(prev => !prev);
return (
<div>
<div>Show: {show()} <button onClick={toggleShow}>Toggle Show</button></div>
<Portal><Modal show={show()} close={close} /></Portal>
</div>
)
};
render(() => <App />, document.body);
https://playground.solidjs.com/anonymous/0ae98cf1-19d6-487f-80dc-72d3c2e554dc
You can use a state in the parent component to keep track of whether the modal should be open or closed, and pass a function as a prop to the child component that updates that state:
const [isModalOpen, setIsModalOpen] = React.useState(false);
const handleButtonClick = () => {
setIsModalOpen(true);
};
return (
<div>
<label className="text-white m-5 p-1">
<input type="checkbox" checked={false} onChange={handleCheck} />
I have read and agree to the <button onClick={handleButtonClick}>Privacy</button>
<Portal>
<TermsModal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} />
</Portal>
</label>
</div>
);
// Child component
const TermsModal = ({ isOpen, onClose }) => {
if (!isOpen) {
return null;
}
return (
<div className="modal">
{/* Modal content */}
<button onClick={onClose}>Close</button>
</div>
);
};
I am trying to implement tailwindcss styles into Formik to style forms. But the styles declared through className are not being applied?
I think Formik is using classname to define input type and tailwind uses the same method to declare styles?
my form page:
import React from "react";
import * as yup from "yup"
import { Formik, Form } from 'formik'
import { MyTextInput } from "../components/FormParts";
let mainFormSchema = yup.object().shape({
name: yup.string().trim().required()
})
const tryMainForm = () => {
const initValues = {
name: ''
}
return (
<div>
<h1>Any place in your app!</h1>
<Formik
initialValues={initValues}
validationSchema={mainFormSchema}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({ isSubmitting }) => (
<Form>
<MyTextInput
label="name"
name="name"
type="text"
placeholder="name"
/>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>
)}
</Formik>
</div>
)
}
export default tryMainForm
formpart component:
import { useField } from "formik";
export const MyTextInput = ({ label, ...props }) => {
const [field, meta] = useField(props)
return (
<>
<label htmlFor={props.id || props.name} className="block text-sm font-medium text-gray-700">{label}</label>
<input className="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" {...field} {...props} />
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
)
}
can you suggest a way to deal with this?
tailwind applies styles properly to label but doesn't with input?
This issue applied the standard implementation without additional customisations.
The is no value on submit of the form and onChange does not fire with the current value.
<Form onSubmit={handleSubmit(this.onSubmit)}>
<Form.Group>
<Field
component={SemanticDatepicker}
name="working"
dateFormat="DD/MM/YYYY"
label="Date of birth"
placeholder="select your DOB"
size="small"
onChange={(e, value) => {
console.log(e, value);
}}
/>
</Form.Group>
<Form.Field
control={Button}
color="purple"
className="submit-btn"
type="submit"
width={6}
>
Save
</Form.Field>
</Form>
A minimal version can be found here https://github.com/chrishj59/datepickerIssue
I could capture the onChange value by creating a custom component which wrapped the <SemanticDatepicker />. I also added mapStateToProps to log the redux-form values. You can consider this as a starting point and work on this.
//SongList.js
state = {
date: "",
};
render(){
const DatePick = () => {
return (
<SemanticDatepicker
onChange={this.datePickerOnChange}
format="YYYY-MM-DD"
/>
);
};
return (
<Form onSubmit={handleSubmit(this.onSubmit)}>
<Form.Group>
<Field
component={DatePick}
name="working"
dateFormat="DD/MM/YYYY"
label="Date of birth"
placeholder="select your DOB"
size="small"
/>
</Form.Group>
<Form.Field
control={Button}
color="purple"
className="submit-btn"
type="submit"
width={6}
>
Save
</Form.Field>
</Form>
);
}
//App.js
import { connect } from "react-redux";
const App = (props) => {
console.log(props.form);
return (
<div>
{" "}
<SongList />
</div>
);
};
const mapStateToProps = (state) => ({
form: state.form,
});
export default connect(mapStateToProps)(App);
I managed to resolve using semantic-ui-redux-form-fields to wrap the Semantic UI component. This now gives the correct format and value appearing in the validate function and formProps.
import React from "react";
imp ort { fieldEnhance } from "semantic-ui-redux-form-fields";
import { compose } from "redux";
import SemanticDatepicker from "react-semantic-ui-datepickers";
import "react-semantic-ui-datepickers/dist/react-semantic-ui-datepickers.css";
const DatePickerPure = (props) => {
const { currentValue, input, ...rest } = props;
const defaultProps = {
format: "DD/MMM/YYYY",
onChange: (event, data) => input.onChange(data.value),
value: currentValue,
...rest,
};
return <SemanticDatepicker {...props} {...defaultProps} />;
};
export default compose(fieldEnhance)(DatePickerPure);
I have to implement a form with a bit less than 30 different fields.
So I decided to split them in 2 differents container component with two tabs to navigate between them.
I use redux-form to handle the data binding.
For on component I can get the value from handleSubimit of one component. But the final validation must be in the last tab only. From here I only have access to the value of the second tab. Like the data from the store where wipe out.
How can I access the store where my previous data should be ?
TabNavigationBar.js
import React from 'react';
import TabNavigationItem from './TabNavigationItem';
const TabNavigationBar = ({ onTabChange, activeTab }) => {
const tabList = [
{ hasIcon: 'fas fa-user-circle', hastext: 'Information Utilisateur' },
{ hasIcon: 'fas fa-file-alt', hastext: 'Informations contrat' }
];
const clickOnTab = tabNumer => {
onTabChange(tabNumer);
};
return (
<div className="columns">
<div className="column is-offset-one-quarter-desktop is-offset-one-thirds-tablet is-half-desktop is-one-thirds-tablet">
<div className="tabs is-toggle is-fullwidth">
<ul>
{tabList.map((tab, i) => (
<TabNavigationItem
key={i}
tabSelected={() => clickOnTab(i)}
hasClass={activeTab === i ? 'is-active' : ''}
hasIcon={tab.hasIcon}
hasText={tab.hastext}
/>
))}
</ul>
</div>
</div>
</div>
);
};
export default TabNavigationBar;
UserForm.js
import React from 'react';
import { reduxForm } from 'redux-form';
import CiviliteRadioButton from './CiviliteRadioButton';
import NameInputs from './NameInputs';
import AddressInputs from './AddressInputs';
import MailAndDOB from './MailAndDOB';
import TelephoneInputs from './TelephoneInputs';
let UserForm = ({ handleSubmit }) => {
return (
<div>
<CiviliteRadioButton />
<NameInputs />
<AddressInputs />
<MailAndDOB />
<TelephoneInputs />
</div>
);
};
UserForm = reduxForm({
form: 'form1',
initialValues: {
user: {
adresse: {
country: 'France'
},
civilite: 'Madame'
}
}
})(UserForm);
export default UserForm;
ContractForm.js
import React from 'react';
import { reduxForm } from 'redux-form';
import InputItem from '../InputItem';
import ContratInputsList from './contratInputList';
let ContratForm = ({ handleSubmit }) => {
const submit = values => {
console.log(values);
};
return (
<div>
<div className="columns is-multiline ">
{ContratInputsList.map((item, i) => {
return (
<div className="column is-half" key={i}>
<InputItem spec={item.spec} />
</div>
);
})}
</div>
<div className="columns">
<div className="column">
<div className="field is-grouped is-grouped-right">
<input
className="button is-primary"
onClick={handleSubmit(submit)}
type="submit"
value="Envoyer"
/>
</div>
</div>
</div>
</div>
);
};
ContratForm = reduxForm({
form: 'form2'
})(ContratForm);
export default ContratForm;
EDIT
When I click on my tabs, redux-form/DESTROY is called and erase form1's data.
Try setting destroyOnUnmount flag to false in reduxForm(options).
In the following code I have two checkboxes. On-click, they change the state of the component to their respective values.
I am building a form that will need over 100 checkboxes and I don't want to write the "onChange" function for each checkbox.
Is there a way that I can write one OnChange function that will take a parameter, then set the state to that parameter?
I've tried many ways but this is still blocking me.
Thank you!
import React from 'react';
export default class InputSearch extends React.Component {
constructor(props) {
super(props);
this.state = {
inputInternship: '',
inputMidLevel: '',
};
this.handleSubmit = this.handleSubmit.bind(this);
this.onChangeInternship = this.onChangeInternship.bind(this);
this.onChangeMidLevel = this.onChangeMidLevel.bind(this);
}
handleSubmit(e) {
e.preventDefault();
this.props.getJobData(this.state);
}
onChangeInternship(e) {
this.setState({
inputInternship: !this.state.inputInternship,
});
this.state.inputInternship == false? this.setState({ inputInternship: e.target.value }) : this.setState({ inputInternship: '' })
}
onChangeMidLevel(e) {
this.setState({
inputMidLevel: !this.state.inputMidLevel,
});
this.state.inputMidLevel == false? this.setState({ inputMidLevel: e.target.value }) : this.setState({ inputMidLevel: '' })
}
render() {
return (
<div className="search-form">
<form onSubmit={this.handleSubmit}>
<input type="checkbox" value="level=Internship&" checked={this.state.inputInternship} onChange={this.onChangeInternship} /> Internship <br />
<input type="checkbox" value="level=Mid+Level&" checked={this.state.inputMidLevel} onChange={this.onChangeMidLevel} /> Mid Level <br />
<div>
<button
type="submit"
>Search
</button>
</div>
</form>
</div>
);
}
}
You need to create a function that would return specific onChange function:
import React from 'react';
export default class InputSearch extends React.Component {
constructor(props) {
...
this.onChange = this.onChange.bind(this);
}
...
onChange(fieldName) {
return (event) => {
this.setState({
[fieldName]: !this.state[fieldName],
});
if (this.state[fieldName]) {
this.setState({ [fieldName]: '' })
} else {
this.setState({ [fieldName]: e.target.value })
}
}
}
...
render() {
return (
<div className="search-form">
<form onSubmit={this.handleSubmit}>
<input type="checkbox" value="level=Internship&" checked={this.state.inputInternship} onChange={this.onChange('inputInternship')} /> Internship <br />
<input type="checkbox" value="level=Mid+Level&" checked={this.state.inputMidLevel} onChange={this.onChange('inputMidLevel')} /> Mid Level <br />
<div>
<button
type="submit"
>Search
</button>
</div>
</form>
</div>
);
}
}
By the way, are what is the point of changing state twice in your onChangeXYZ functions?
{
this.setState({
[fieldName]: !this.state[fieldName],
});
if (this.state[fieldName]) {
this.setState({ [fieldName]: '' })
} else {
this.setState({ [fieldName]: e.target.value })
}
}