Getting Started With Ionic: JavaScript Components

Final product image
What You'll Be Creating

In this tutorial, we are going to build our first Ionic app together and learn the basics of Ionic's JavaScript components. These components provide your app easy access to features, such as navigation and navigation bars, infinite scroll, and lists. If you haven’t set up Ionic yet or need to refresh how to use the Ionic CLI, you can review the first tutorial of this series.

What Is a Component?

The term components is somewhat abused in frontend development as many frameworks have their own notion that describes a component. In fact, Web Components as an official HTML standard can further complicate the concept, so let’s clearly define what a component is in Ionic.

In a general sense, a component is an implementation of a set of features that are encapsulated by some form of coding convention. In other words, you can think of a component as a way to isolate a particular feature from the rest of the application. You can think about how in HTML there are different types of form inputs and each of them is a type of component that has specific features.

In Ionic, there are two types of components, CSS and JavaScript. CSS components are implemented as a set of CSS classes that modify an element to give it a specific appearance, such as a header bar.

JavaScript components are technically implemented as Angular directives and they are used as HTML elements in the application. They provide a richer set of features. This usually includes the ability for users to interact with it or the application to otherwise manage the component. Tabs, for example, allow content to be shown or hidden based on the user selecting a tab.

In this tutorial we are going to focus on some of the JavaScript components. Later in this series, we take a closer look at CSS components.

Sometimes, Ionic implements a component as both a CSS and a JavaScript component, such as the tabs component. This means you decide which one to use. I generally recommend opting for the JavaScript implementation. In most cases, the overhead of using the JavaScript component is negligible and I believe they make your code easier to work with.

Source Files

In this tutorial, we are going to create an app from scratch and we will continue enhancing the app in the rest of this series. The premise of this app is to create a civic informational app that provides users with information about their local facilities, such as libraries and parks.

In this tutorial, we start by building an app that displays a list of parks in Chicago and uses infinite scroll to keep loading results as long as they are available. We will expand the feature set of the app in the next tutorials.

I have created an API that provides the information the app needs. The API is based on the Google Maps APIs. You can run the API yourself, but it requires getting a custom API key from Google and instructions can be found on the API project. If there are any issues using the provided API, such as someone abuses the API and exceeding the API usage limits, running your own version should help.

You can preview the running app on Heroku and view the completed project on GitHub. However, I encourage you to follow along and build the app with me.

1. Setting Up the Project

First, you need to start a new project. We can do this by running the following command:

This downloads a starter bundle that includes an empty Ionic template to get us started (built for use with my book Ionic in Action). Enter the directory, cd civinfo, and run ionic serve.

You can now preview a blank app loading at http://localhost:8100 (or on the port set by Ionic). I recommend opening the developer tools of your browser to confirm that you see a blank screen. Yes, it should be a white screen. I also suggest using Chrome’s Device Emulation while previewing your app.

2. Setting Up Base Navigation Components

Navigation is so crucial that we should start here with designing our app. The primary navigation components are ionNavBar and ionNavView. The majority of apps have a design feature where there is a navigation bar with various titles and action buttons, and then the rest of the area is devoted to the content for the current view.

The ionNavBar and ionNavView components provide that functionality with some built-in intelligence to help us out. Our app is going to have several routes in the end, but we build only one in this tutorial.

Ionic uses the UI Router under the hood to manage navigation and routing. If you are familiar with it, then you recognize the implementation in Ionic. There are a lot of nuances, but we keep it simple in this tutorial. The most common and simple use is to define each of the various pages of your app as a state, which is the Ionic/UI Router way to define a particular view.

To get us started, we first include the two navigation components into www/index.html like you see below, placing it inside the body.

Once you’ve added the code to index.html, you can reload the app and should see a green bar appear along the top of the app.

App Showing Just the Navigation Bar Which Is Green

You have defined the ionNavBar component, which automatically appears at the top of the screen. Later, when we create individual views, those views will be able to pass a title and additional buttons to display. It is smart enough to know how tall the navigation bar should be for different devices. This is not consistent across platforms so this is very helpful. The navigation bar is given a class of bar-balanced to give it a green color.

Then there is the ionNavView, which is the placeholder that renders the content for each of the views. Once we define a view, it will render the resulting markup here and it automatically adjusts to take up the available space left over after the navigation bar is positioned.

The navigation components are examples of JavaScript components (also known as Angular directives). They look like custom HTML tags and, when used together, they are smart enough to keep the title bar in sync with the current view and render the correct content based on the user’s navigation choices. To see this in action, though, we need to add some states. Let’s start by making our first state that displays a list of parks.

3. Adding the Parks List View

The primary purpose of the app is to display a list of civic related resources. Initially, this will be a list of parks, but we will expand it to include other types of resources like libraries. We want to include a few features into this view:

  • update the navigation bar with a title
  • load a list of parks from the API
  • display the list of items in a mobile friendly format
  • allow more items to load if the bottom is reached, using infinite scroll
  • display an image with each item

Step 1: Setting Up the Places State, Controller, and Template

Now that we have a few goals for this view, let’s start with adding our JavaScript file that will register this view. Create a new file places.js at www/views/places/ and add the following to it:

We declare a new state for the UI Router using the $stateProvider.state() method. This is only available to be configured inside Angular's angular.config() method. When you declare a state, you first pass a string to name the route, in this case places. You then pass an object with various properties that defines the state, such as a URL, a controller, and a template. You can look at the UI Router documentation for all possible configuration options.

We have declared a new state, named it places, assigned it a URL of /places, named a controller using controller as syntax, and listed a templateUrl to load. This is a fairly common state definition and you see it used in mostly the same way with other states. The controller declared here is empty, but we will add to it soon.

This template is an essential part of the view and describes the visual aspects of that view. Most of the view logic and behavior will be managed in the controller and the template. Our state declares we want to load an HTML file for the template, but we haven’t made one yet. Let’s fix that by creating a new file places.html at www/views/places/ and adding the code below.

So far in this template, we have declared ionView and ionContent components. The ionView component is a wrapper that you place around a template that is intended to be loaded into the ionNavView component we declared earlier. The view-title attribute is also used to pass the title the navigation bar should display.

The ionContent component is a useful content wrapper, which helps to ensure that the content space is sized to the available screen space, helps manage scrolling, and can expose other less commonly used behaviors. When this view is loaded, you will see the navigation bar title appear as "Local Parks".

Now we need to make sure the app loads the script to execute by adding places.js to index.html like you see below. I recommend adding this right before the </head> tag.

You can view the app, but you still won’t see the view appear. To see the view, navigate to http://localhost:8100/#/places. The URL mapped in the state definition can be used to navigate to a route. It should then appear as in the following image with the title set to "Local Parks".

App Places View Showing Title In Navigation Bar

This isn’t too exciting yet, but this represents the most basic view that you’ll probably set up most of the time. Now let’s work on loading data and showing it on the screen.

Step 2: Loading Data

Before we can do much else, we need to load some data. To do this, we need to add an Angular service to help us manage geolocation. In a future tutorial, the location of a user will be detected by the device. Until then, we are going to manually set it to Chicago, one of my favorite cities.

Open www/js/app.js and add the following service to the end of the file. It should chain with the existing methods from angular.module.

This is an Angular service that returns an object that matches what the Google Maps API returns for Chicago. We now have details for the location so we can load parks there.

Next, we are going to update the controller to load the list from the API. For simplicity, I’m loading the data using the $http service in the controller. The best practice would be to abstract that out into a service. Open www/views/places/places.js again and update the controller like this:

The controller has a vm.load() method to execute the HTTP request and stores the results in vm.places. When you save this, you are to see the HTTP request firing in the developer tools of your browser. Even if you are familiar with Angular, you may not recognize this exact approach for storing data on the vm variable. I recommend reviewing John Papa’s post on why this is a recommended approach if you need some clarity.

To display the data, we need to update the template as well and loop over the list of parks to display them. Open www/views/places/places.html and update it as shown below.

In the template, we are using the ionList and ionItem components. The ionList component is one of the most useful components because lists are a very common design choice in mobile due to smaller screens and typical use in portrait orientation. Much like a list using ul and li, ionList wraps any number of ionItem elements.

Lists can adopt a number of different appearances and in this example the list item displays an image to the left by declaring the item-avatar class on the ionItem. The same approach could be used in a messaging app where you have a list of chats with an avatar of each person.

Inside the ionItem, you display the name and address. The default styling is to automatically truncate (using CSS) any text that overflows to keep items the same height.

App Showing All Items In a List

We have loaded a list of parks and displayed them as a list using ionList and ionItem. We can take this one step further and add infinite scrolling to load additional results as the user scrolls near the end of the list (if they are available).

Step 3: Adding Infinite Scroll to a List

To make the list automatically load additional items based on the user scrolling to the bottom, we can make use of the ionInfiniteScroll component. This component is placed at the end of a list, watches for when the user has scrolled to the end, and then calls a method that can load additional items. It also has a built-in loading spinner to indicate that more items are loading. The spinner is hidden when the response resolves.

Our API also has to support some form of pagination for this to work. In this case, the Google Maps API provides a token that must be passed to load the next set of results. We need to update the controller to manage this logic so let’s start with updating www/views/places/places.js as shown below.

We have added a new property, vm.canLoad, which is a boolean that indicates if there are additional items to load. This is true by default. Until a request is returned, we don't know if there are additional items available.

The vm.load() method is updated to append the token if available. The response handler now concatenates the results onto the array. This means the second page of results is added after the first page. The Google Maps API will return a next_page_token anytime there are more results that can be loaded. If that property is missing, we can assume there are no more items to load and vm.canLoad is set to false. The infinite scroll component uses this value to determine when to stop loading more items.

The final change is the addition of $scope.$broadcast('scroll.infiniteScrollComplete'). The infinite scroll component has no knowledge of when the HTTP request has completed or exactly when it is save to disable the loading symbol. Therefore, the component listens for events to update itself. In this case, the scroll.infiniteScrollComplete event tells the component to stop the spinner and continue watching for the user scrolling to the bottom.

The final piece is to enable this in the template. Open www/views/places/places.html and add the line between the end of the ionList and ionContent components.

The infinite scroll component is now enabled in your template. It starts to watch for when the component is visible, which is also triggered on load because no places are visible then and the infinite scroll component is visible. It calls the method declared in on-infinite once when it becomes visible (here it is vm.load()) and waits until the scroll complete event has been triggered.

The ngIf is used to disable the infinite scroll once the API has returned all possible results. In that case, scrolling to the bottom no longer triggers the load of more resources.

When using infinite scroll, it is important to use an ngIf to disable it. It can be easy to implement the component in such a way that the component tries to load and load and never stops.

This completes the places view. Looking back, there is quite a bit of functionality enabled by 12 lines of HTML in the template and about 20 lines of JavaScript in the controller.

Summary

We have taken a look at a number of components, which you will frequently use in your Ionic apps.

  • Ionic JavaScript components are used as HTML elements and can work in a coordinated manner.
  • Ionic has ionNavView and ionNavBar to support coordinated navigation with different views.
  • The ionList and ionItem components make it easy to build mobile friendly lists.
  • The ionInfiniteScroll component automatically triggers a call to load additional items and append them to the list.

The next tutorial will take a look at some of the useful services that Ionic provides, such as loading indicators and popovers.

Create an Ionic Template and Win $1000

If you're already comfortable with the Ionic framework, then you may want to consider entering Envato's Most Wanted contest for Ionic templates. How? Create a unique Ionic template and submit it to Envato Market by 27 April 2016.

The five best templates receive $1000. Interested? Read more on the contest's website for details about the contest's requirements and guidelines.

Tags:

Comments

Related Articles