Meteor-React-Ionic Mobile App Part 4.3: Users and Settings

In this section, we’re going to give your users the ability to log in by filling out a form in a modal. If you don’t want to read through the two previous articles (Part 4.1 and Part 4.2), you can just clone the repo from where the previous article left off and start from there.

Step 8

Now that we can log out, we need to be able to log back in. We’re going to do this by launching a modal with a login form. Let’s remove the “Please log in” placeholder in our Profile component and add a conditional for the profile image. Our new render function for our Profile component should look like this:

...
render() {
if (this.data.userLoading) {
return <AppLoading />
};
return (
let loginStatus = this.getLoginStatus();
<div className="profile-wrapper">
<div className="image-wrapper">
{loginStatus ? <img src={this.data.user.profile.image} /> : <div></div>}
</div>
...

And change the following line to your styles.scss file to give yourself a placeholder div in place of the user profile:

..
img, div {
border: solid 1px #ccc;
...

Since the login modal is special, let’s create a new component in Settings.jsx that will contain the form:

LoginForm = React.createClass({
render() {
return (
<div>
<div className="list">
<label className="item item-input">
<span className="input-label">Username</span>
<input type="text" />
</label>
<label className="item item-input">
<span className="input-label">Password</span>
<input type="password" />
</label>
</div>
<div className="padding">
<button className="button button-block button-positive">
Sign In
</button>
</div>
</div>
)
}
})

Now let’s change the NotLoggedIn component to pass in the LoginForm component to our ion modal. We’re also passing the login() function to allow the user to log in from the modal:

...
render() {
return <a onClick={this.props.ionModal.bind(null, "Log in", <LoginForm login={this.login}/>)}>Login</a>
}

This won’t quite work yet because we first need to change our IonModal component to take in content, and we need to change our ionModal function to take in a second parameter. We can do this by changing the following lines in AppBody, which will pass the content as a prop called modalContent:

...  
ionModal(tab, content) {
this.setState({
modal: (
<IonModal modalContent={content}>
<div className="h1 title">{tab}</div>
...

And then changing the following lines in our IonModal component to move the children to the header and move modalContent to the body. Also note that we added the “has-header” class, which moves your content below the header:

...
<div className="modal">
<div className="bar bar-light bar-header">
{this.props.children}
</div>
<div className="content overflow-scroll has-header">
{this.props.modalContent}

</div>
</div>

Step 9

Now that we have the login modal in place, we need to allow the user to fill out the form and log in. We’re going to do this using Controlled Components, which makes validation much easier in forms. In Controlled Components, you temporarily store the form data in state.

To accomplish this, we need to set an initial state and add a function to handle a change to our form and update state. You can do this by adding the following code to the LoginForm component:

...
getInitialState() {
return {
user: "",
pass: ""
}
},
handleChange(input, e) {
if (input == "user") {
this.setState({
user: e.target.value
})
};
if (input == "pass") {
this.setState({
pass: e.target.value
})
}
},
...

Then within our render function, we need to set state to a variable that we can pass into our <input>, bind value to that variable, and set an onChange event to handleChange that passes the appropriate data. You can do this by changing the following lines of code in your LoginForm component:

render() {
var user = this.state.user;
var pass = this.state.pass;

return (
...
<input value={user} type="text" onChange={this.handleChange.bind(this, "user")} />
</label>
<label className="item item-input">
<span className="input-label">Password</span>
<input value={pass} type="password" onChange={this.handleChange.bind(this, "pass")} />
</label>
</div>
<div className="padding">
<button onClick={this.props.login.bind(null, this.state.user, this.state.pass)} className="button button-block button-positive">
Log in
</button>

...

The last thing we need to do is close the modal on login. We can do this by changing state of modal to false. This is not as easy as it sounds, since the only way to change the state of a parent component is through a function that you pass all the way down. Let’s define that now on AppBody:

setModalState(status) {
this.setState({
modal: status
})
},

…which then passes into our RouteHandler:

<ReactRouter.RouteHandler setModalState={this.setModalState} ionModal={this.ionModal} />

…which then passes into our Settings component:

<Profile setModalState={this.props.setModalState} ionModal={this.props.ionModal} />

…which then passes into our Profile component:

{loginStatus ? <LoggedIn ionModal={this.props.ionModal} /> : <NotLoggedIn setModalState={this.props.setModalState} ionModal={this.props.ionModal} />}

…which we can then use within our login() function in our NotLoggedIn component:

login(user, pass) {
Meteor.loginWithPassword(user, pass);
this.props.setModalState(false);
},

And now you have yourself a fully function (if barebones) user accounts system and settings page!


Sam Corcos is the lead developer and co-founder of Sightline Maps, the most intuitive platform for 3D printing topographical maps, as well as LearnPhoenix.io, an advanced tutorial site for building scaleable production apps with Phoenix and React.

Additional