OnsenUI | Working With Tab Bar Pattern

Written by on April 9, 2015

OnsenUI | Working With Tab Bar Pattern

Code overview

Like AngularJS, OnsenUI is all about MVC (or to be more precise Model–View–Whatever) so let’s discuss basic application details.

First thing we need to do is to initialize our application, unlike AngularJS, OnsenUI doesn’t require defined ng-app directive.

We only need to initialize it through JavaScript:

var module = ons.bootstrap('my-app', ['onsen']);

For OnsenUI to work properly, we need to set the [‘onsen’] dependency, framework will not work without it.

This application will have two distinct tabs (third one exist but only for showcase), first tab (page1.html) will act as a search page while a second tab (page2.html) will act as a settings page. OnsenUI doesn’t use AngularJS routing system so we will programmatically create our application navigation system (we will discuss this later). Both application tabs (pages) will use the same controller:

module.controller('MainCtrl', function($scope, Movies) {

…Ctrl under controller name is an Angular convention. but you can give it any name. For better future readability stick to this convention. Another important thing to remember is $scope. This is Angular model, it’s just a JavaScript object. Each $scope will hold associated page data. As you can see you don’t need to call set or get functions if you want to change something inside a $scope (like in Backbone case), just access it like any other JavaScript object:

$scope.movies = movies;

Our first tab (page1.html) contains only two page structures (elements), our search input and an empty list holder template. Second tab (page2.html) contains one input box and two sliders. First tab templete system is made to mimic HandlebarsJS template engine (or older {{mustache}} engine). For example this is a list holder template we are using in our example:

<ons-list>
	<ons-list-item modifier="chevron" class="list-item-container" ng-repeat="movie in movies" ng-click="showDetail(movie.id)">
	   
	  <ons-row>
		<ons-col width="95px">
		  <img ng-src="https://image.tmdb.org/t/p/w92{{movie.poster_path}}" onerror="this.src = 'https://www.ginesisnatural.com/images/no_image.jpg';" class="thumbnail">
		</ons-col>
		<ons-col>
		  <div class="name">{{movie.original_title}}</div>
		  <div class="desc">{{movie.release_date}}</div>
		</ons-col>
		<ons-col width="40px"></ons-col>
	  </ons-row> 
	   
	</ons-list-item>
	 
</ons-list> 

{{movie.original_title}} represents object movie -> parameter original_title.

Now let’s talk about how this application works. First we need to write something into our search input, it looks like this:

<input type="search" class="search-input search-movies-input" placeholder="Search..." style="width: 100%;height: 45px;" ng-model="selected.movieName" ng-change="searchMovieDB()">

Because this input has an AngularJS attribute ng-model=”selected.movieName” everything we write will be bound to the $scope object selected:

  $scope.selected = {
    movieName : 'Batman'
  }

Another important thing about this input box is, each time we change something it will trigger change event bound to this input box (ng-change=”searchMovieDB()”). This event will trigger function searchMovieDB which is part of a Main controller (MainCtrl):

$scope.searchMovieDB = function() {

This is where things get a little bit complicated. This function will call another function which is part of a factory service called Movies. This :

Movies.list($scope.movie.name, function(movies) {

will call this:

list: getData,

which is a part of a Movies factory service. AngularJS factory lets you create a service that you can use with AngularJS dependency injection. This is recommended way of exposing data from the server to the Angular views. In a few words, dependency injection is a software design pattern in which an object is given its dependencies, rather than the object creating them itself. It is about removing the hard-coded dependencies and making it possible to change them whenever needed. Don’t worry if you don’t understand a word I’m saying, you will learn it sooner or later.

Movies factory function list will accept two parameters, a first one is movie name and second one is an anonymous callback function we will use to acquire a list of searched movies. Function list will call another function getData:

function getData(moviename, callback) {

This function will in turn initiate a REST call to moviedb service, acquire a list of movies and returning it back to the original scope using passed callback function. It will set original scope object $scope.movies, which will in turn trigger list template population:

<ons-list>	
	<ons-list-item modifier="chevron" class="list-item-container" ng-repeat="movie in movies" ng-click="showDetail(movie.id)">
	   
	  <ons-row>
		<ons-col width="95px">
		  <img ng-src="https://image.tmdb.org/t/p/w92{{movie.poster_path}}" onerror="this.src = 'https://www.ginesisnatural.com/images/no_image.jpg';" class="thumbnail">
		</ons-col>
		<ons-col>
		  <div class="name">{{movie.original_title}}</div>
		  <div class="desc">{{movie.release_date}}</div>
		</ons-col>
		<ons-col width="40px"></ons-col>
	  </ons-row> 
	   
	</ons-list-item>
 
</ons-list> 

Attribute ng-repeat=”movie in movies” will loop through the object movies and build page content using acquired JSON data and list template.

Second tab is dedicated to application settings: we can modify moviedb API key, change a number of displayed items, and set a minimum average movie score we would like to see:

<ul class="list">
  <li class="list__header">API Key</li>
  <li class="list__item">
	<input type="text" class="text-input text-input--underbar" ng-model="settings.apiKey" placeholder="underbar text" value="" style="width: 100%;">
  </li>
  <li class="list__header">Items Per Page {{settings.itemsPerPage}}</li>
  <li class="list__item">
	<ons-row>
	  <ons-col width="40px">
		<ons-icon icon="" class="lower">1</ons-icon>
	  </ons-col>
	  <ons-col class="range-wrapper">
		<input type="range" class="range" name="volume" min="1" max="10" value="4" ng-model="settings.itemsPerPage" style="width: 95%;">
	  </ons-col>
	  <ons-col width="40px">
		<ons-icon icon="">10</ons-icon>
	  </ons-col>
	</ons-row>           
  </li>
  <li class="list__header">Minimum Average Score: {{settings.minimumScore}}</li>
  <li class="list__item">
	<ons-row>
	  <ons-col width="40px">
		<ons-icon icon="" class="lower">1</ons-icon>
	  </ons-col>
	  <ons-col class="range-wrapper">
		<input type="range" class="range" name="volume" min="1" max="10" value="4" ng-model="settings.minimumScore" style="width: 95%;">
	  </ons-col>
	  <ons-col width="40px">
		<ons-icon icon="">10</ons-icon>
	  </ons-col>
	</ons-row>         
  </li>      
</ul>

All those settings are bound to MainCtrl object:

  $scope.settings = {
    apiKey : '470fd2ec8853e25d2f8d86f685d2270e',
    itemsPerPage : 5,
    minimumScore : 7
  }   

This way everything you change on the settings tab will effect movie list on the search tab (in real time).

That’s everything you need to know about this application. I hope this example will help you as much as possible. If you have additional questions please leave them in a comment section below.

Who Am I?

Between working as a senior Java developer in one of the largest insurance companies in the world and traveling, in my free time, I work as a professional mobile development adviser. I'm also a major jQuery Mobile supporter back at StackOverflow and a forum moderator at the official Ionic Framework forum.



Categories

Leave a Reply