Meteor-React-Ionic Mobile App Part 3: Tabs and Modals

This article covers how to add tabs (like the toolbar you find on the bottom of most iPhone apps) and how to launch modals. We’re going to launch these modals from the tabs, which is not very good practice as far as UI goes, but it makes the demo simpler. Generally speaking, you’ll want to use the tabs for navigation and launch the modals from somewhere else.

This is primarily an exercise in integrating Ionic into React components and isn’t particularly difficult, but modals are very common in mobile apps so it’s worth going over. This should take about 10 minutes.

We’re going to build this on top of the basic template, which you can find the related article here, or if you just want to clone the repo and move forward, you can find the repo here.

Your app should eventually look like the demo to the left.

NOTE: Be aware that if you clone the repo, there are a few quirks with the meteoric:ionic-sass package that will crash the app the first time you run it. All you have to do to solve this is kill the process and re-run Meteor.

We’re going to add tabs to the AppBody component to give access to the tabs from any route. Each of these three tabs will launch a different modal.

We’re also going to control the modal by toggling state, which is a very React way to do it.

Step 1

First, we’re going to add the tabs to the bottom of the app. We’re going to add them to AppBody, but if you want to make tabs accessible to only certain routes, you can put the tabs into a separate component and add it to just the routes that you think should have tabs.

Personally, I don’t want the tab names to be hard-coded so I’ll store them in props. Add the following code:

...
getDefaultProps() {
return {
tabs: ["Tab 1", "Tab 2", "Tab 3"]
}
},
...

Now that we have our three tab names stored in props, we need to iterate over them to create the components that we intend to render. Within <div className=ionic-body”>, add the following code to create our tabs:

...
<div className="tabs tabs-icon-top">
{this.props.tabs.map((tab, i) => {
return (
<a className="tab-item" key={tab}>
<i className="icon ion-star"></i>
{tab}
</a>
)
})
}
</div>
...

Now you have properly formatted tabs! Ionic makes that bit really easy.

Step 2

Now that we have our tabs, we’re going to make each tab trigger a different modal. Unfortunately, Ionic is optimized for Angular so we’re going to have to do a little finagling to get it to work. We’re going to use the React.addons.CSSTransitionGroup for our modal animation instead of the Ionic animation.

The way we’re going to accomplish this is by toggling a state variable called modal. When set to false, no modal is displayed; when changed to true, a modal is created and transitioned into the view.

The first step is to create the IonModal component. This is pretty straight-forward html from Ionic. Create a new file called IonModal.jsx within your components directory and add the following code:

IonModal = React.createClass({
render() {
return (
<div className="modal-backdrop">
<div className="modal-wrapper">
<div className="modal">
<div className="bar bar-light bar-header">
<div className="content overflow-scroll">
<div className="padding">
{this.props.children}
</div>
</div>
</div>
</div>
</div>
</div>
)
}
})

Because we want to pass tab-specific information into the modal, we need to use this.props.children. It will become evident very soon how this works.

Now we need to set our initial state to false, because we don’t want a modal showing up before we click on anything. Within AppBody add the following code:

...
getInitialState() {
return {
modal: false
}
},
...

Ok, so now that we have our initial state, we need to create a function that allows us to change the state and render a modal on a click event. Within AppBody, add the following code:

...
ionModal(tab) {
this.setState({
modal: (
<IonModal>
<div className="h1 title">{tab}</div>
<button onClick={ () => this.setState({modal:false}) } className="button button-icon active">
<i className="icon ion-ios-close-empty"></i>
</button>
</IonModal>
)
})
},
...

Everything within the <IonModal> block is what gets passed in to this.props.children to make each modal unique. The button sets the state of modal to false, which removes it from the DOM.

We’re almost done! Add the following code within AppBody’s render() function, and within <div className=“ionic-body”>

...
{this.state.modal}
...

The last step is to connect the function to an onClick event within each of our tabs. You can do that by changing the following line of code within AppBody:

...
<div className="tabs tabs-icon-top">
{this.props.tabs.map((tab, i) => {
return (
<a className="tab-item" key={tab} onClick={this.ionModal.bind(null, tab)}>
<i className="icon ion-star"></i>
{tab}
</a>
)
})
}
</div>
...

You now have a functioning modal! But it doesn’t animate, which is lame. Let’s fix that.

Step 3

The next step is to use React.addons.CSSTransitionGroup to animate the modal. On top of AppBody.jsx, add the following code:

let Transition = React.addons.CSSTransitionGroup;

This gives us access to Transition, which we will use to animate the modal. Now we want to wrap {this.state.modal} in a CSSTransitionGroup. We can do this by changing the following code in AppBody:

<Transition transitionName="modal">
{this.state.modal}
</Transition>

You’ll note that we are also naming the Transition modal which we will use in our styles.scss file to tell the Transition how to animate.

There are two css properties in CSSTransitionGroup that we are going to use: enter and leave. When the component is created, we want it to start below the app. Then, we want it to transition up from the bottom. When the modal is closed, we want it to transition down to the bottom.

Within styles.scss, add the following code:

// Modal starts shifted 100% below the app.
.modal-enter {
transform: translateY(100%);
}
// When it's active, it transitions back to position.
.modal-enter.modal-enter-active {
transform: translateY(0);
transition: transform .3s ease;
}
// When the modal leaves, it starts out in normal position.
.modal-leave {
transform: translateY(0);
}
// But when active, it is shifted 100% below the app and destroyed.
.modal-leave.modal-leave-active {
transform: translateY(100%);
transition: transform .3s ease;
}

Your modal is now animated! But something is not quite right. The last thing we need to do is darken the background as soon as the modal is triggered. To solve this, we will create one more component called Backdrop which uses Ionic classes to solve this for us.

Within IonModal.jsx, add the following code:

Backdrop = React.createClass({
render() {
return (
<div className="modal-backdrop active"></div>
)
}
})

The last thing to do is to add a ternary conditional statement that checks to see if this.state.modal is truthy or falsy: if true, render the component, if false, nothing. Finally, within AppBody add the following line of code:

{this.state.modal ? <Backdrop /> : false}
<Transition transitionName="modal">
{this.state.modal}
</Transition>

And we’re done! We now have fully functional modals and tabs.


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