- Ionic:Hybrid Mobile App Development
- Rahat Khanna Sani Yusuf Hoc Phan
- 1907字
- 2021-07-09 19:00:35
Introduction to Angular UI Router
The core of Ionic Framework is an open source routing module called Angular UI Router. It implements states that are a part of a state machine represented by the complete app. In a normal Angular app, we use ngRoute
, which defines different routes, each of which can be associated with only a single ng-view
and one corresponding templateUrl
. In the UI Router, routes are represented by states (discussed in the following chapter).
States and URLs
In an app using the UI Router, the views are not tied up to the URL and hence you can change the parts of the app even without changing the URL. In any mobile app, the views are not so simple that they can be changed wholly but there is a complex hierarchy of views and sub-views that change based on different states. Due to this reason it is better to maintain states instead of routes and hence Ionic chose to use the Angular UI Router instead of ngRoute
. States are also defined in the config
section of an angular
module.
We will learn how to create a state by adding a simple view for our sample app. A new state is created with the name homeView
, which will be the default view shown after the app is bootstrapped. It will be associated with a controller and a template string or template URL similar to the route:
angular.module('BookStore', ['ionic']) .config(['$stateProvider','$urlRouterProvider', function mainAppConfig($stateProvider,$urlRouterProvider){ $urlRouterProvider.otherwise('/home') $stateProvider.state('homeView', { url: '/home', template: '<p>App Home View!</p>', . controller: 'HomeController' }); }] )
The state that we have created will be accessible using the URL http://<domain-name>/home
and will be opened by default because of the code $urlRouterProvider.otherwise('/home')
.
We can associate a template like the previous one or give a URL to the html
template using the templateUrl
property on the state. Whenever this state is loaded, the template HTML code will be injected into the <ion-nav-view>
directive of the parent state. In this case, for the root state, we should be adding the <ion-nav-view>
directive to the index.html
file with some initial content:
<ion-nav-view> Loading View... </ion-nav-view>
The perfect way to decide whether a state should exist or not is by ensuring that a state adheres to the following principles:
- A state represents a section of your app that is navigable from one or multiple other states and displays important information about your app
- A state encapsulates important markup and logic in terms of a template and a controller
- Sometimes, multiple states would have some part of the view common and the best approach to implement such a requirement is to create separate states and use state hierarchy to model the commonalities
Nested states and views
We have already seen how to create a simple state, but we would hardly have an app with simple states. In order to represent a complex view, we would divide our state into some sub states representing sub sections of the views.
We can create nested states in the following multiple ways.
In order to create a nested state, we can use .
to write the name of the child state after the parent state. If the parent state is named as homeView
, we can register a nested state menu
by naming it .state ('homeView.menu', {})
. The Angular UI Router module by default sets the state before the dot operator as the parent of the state object.
Nested states can also be created by using a parent
property on the state object passed as a second argument to the state
method of $stateProvider
. The name of the parent state can be set as a string value to the parent
property:
$stateProvider.state('menu',{ parent: 'homeView', templateUrl:'homeView.menu.html', controller: 'MenuController' }
We can use objects instead of strings to set the parent
property of the state object:
var homeView = { name:'homeView', ... }; var menu = { name: 'homeView.menu', parent: homeView, templateUrl:'homeView.menu.html' }
States can be registered in any order and a child state can be registered before the parent state. It will queue the child state registration until the parent is registered.
No two state names can be the same. With the dot notation or without dot notation, the names of the states need to be different even if the two states have different parents.
If the application is in a particular state, and the state is active, then all its ancestor states are active by default. The child states will load their templates into their parents' <ion-nav-view>
directives.
In order to transition to a specific state or make any state active, there are multiple ways available.
You can use an anchor tag and set the href
attribute to #
followed by the URL for the state. For example, if the URL is set to /home/main
, then the href
attribute value should be #/home/main
:
<a href="#/home/main"> Main Link </a>
Another way to link states in HTML is to use the ui-sref
directive, which allows the developer to pass in the state name instead of state URL:
<a ui-sref="homeView.main"> Main Link </a>
If we want to transition to a particular state from JavaScript, then we can use a method on the $state
object that can be injected into any controller. Please note that in ui.sref
you give the value starting from the parent template. For example, if you are giving a link in index.html
then you should give ui-sref='homeView.main'
but if the link is in homeView.html
then give ui-sref='main'
:
<a ui-sref="homeView.main"> Main Link </a>
We will be working with a new Ionic Project using a blank
starter project template and use the code snippets given along with the chapters to build the BookStore e-commerce sample app.
In the UI router, there is a provision to define an abstract state, which can have child states but it cannot be activated itself. This state cannot be transitioned to and it is activated automatically when one of its descendants is activated. The use cases for using an abstract state is when we want to use a state for layouts only, when a URL needs to be appended to all states, and to provide common data or resolved dependencies to all child states.
Abstract states would still require a <ion-nav-view/>
directive in the template:
.state('homeView',{ abstract: true, url: '/home', templateUrl: 'homeView.html' });
Multiple and named views
In a complex mobile app, we would want multiple sections of our app to change according to different states. In Ionic also, we can have more than one <ion-nav-view/>
in a single template by giving them a named attribute. We can have only one maximum unnamed ion-nav-view
in a template.
When setting multiple views in a state, we have to use the views
property, which will contain the name of the views as keys and objects for each view (templateUrl
, controller
, and so on) as values.
Abstract states would still require a <ion-nav-view/>
directive in the template:
$stateProvider .state('homeView.books', { abstract:true, url:'/books', templateUrl: 'homeView.books.html' }) .state('homeView.books.list',{ views: { filters: { templateUrl:'homeView.books.filters.html' }, list: { templateUrl:'homeView.books.list.html' } } });
In a state where the views
property is set, it automatically ignores the template, templateUrl
or templateProvider
, so if you want to set a template, then you need to define an abstract state as a parent defining the layout. Also, remember that in Ionic, by default <ion-nav-view/>
has such CSS styling that it takes the whole screen size and the last named view will always be on top. You have to write custom CSS styling to achieve your design.
State parameters
URL routing is very important along with the state mechanics defined for the app. Using the power of URL routing we can send data or parameters in the URL itself, which helps in identifying the state along with some unique identifiers for the state. There are various types of parameter that can be passed to a route or state.
The URL can have dynamic parts, which can contain any value, and the controller should be able to access the value passed. You can use a :
character or { }
to define a dynamic URL part, which is called a basic parameter:
$stateProvider .state('homeView.books.detail', { url: "/books/:bookId", templateUrl: 'books.detail.html', controller: function ($stateParams) { // If we got here from a url of /contacts/42 expect($stateParams).toBe({bookId: "42"}); // expect() is a method of Jasmine Framework Unit Testing Code } })
Alternatively, the basic parameter can also be defined using curly braces:
url: "/books/{bookId}"
We can restrict the values passed in the url
parameters by using a regular expression inside curly brackets in the url
value of the state. For example:
url: "/books/{bookId:[0-9]{1,8}}"
The previous line specifies that the bookId
parameter can be digits only and its length can vary from 1 to 8.
Query parameters are similar to the HTML query string parameters commonly used. You can send parameters in the URL itself after the ?
symbol as key value pairs.
Sample URL structure to represent single query parameter is as follows:
url: "/books?bookId" // will match to url of /books?bookId=value
Sample URL structure to represent multiple query parameters is as follows:
url: "/books?bookId&category" // will match to /books?bookId=v1&category=c1
This is an in-built service in the UI Router module, which provides access to each state parameter in the controller. It is important to note that parameters only specific to that state and not its ascendants will be available in the controller. An example of URLs and corresponding $stateParams
object is as follows; in this example it is mentioned how the bookId
parameter can be accessed using $stateParams
:
// If url is of type 'books/:bookId/details?section' // Actual Url hit is /books/23/details?section=4 console.log($stateParams); // Console Output: { bookId: 23, section: 4}
State events and resolve
Important angular events are fired during the management of states in an app. Whenever the state changes from one state to another, there are a handful of events that are broadcasted from the $rootScope
and are available for all the controller scopes to listen. If we want to handle them globally, we can use the $on
method on the $rootScope
variable to handle them. The code would look like this:
$rootScope.$on('<event-name>',function handler(eventArgs ...){});
The lists of events available are as follows:

Each and every state can have a property resolve
, which is helpful to provide content or data to the controller. resolve
is an optional map of dependencies that should be injected to the controller.
A useful feature of resolve
is that it can also contain promises, which can help us to get data using AJAX requests. Also, the controller is not initialized for a state before all the resolve dependencies. The resolve
object contains key and value pairs. The key can be any string whereas the value can be a function or a string. If the value of resolve
is a string, it represents a service in the same module, and if it is a function, it should return a value or any promise.
The code for an example resolve
block in a state definition looks like this:
$stateProvider.state('homeView',{ url:'/home', resolve: { information: function(infoService) { return infoService.getInfo(); }, about: 'aboutService' } });
- Instant Node Package Manager
- Mastering Entity Framework Core 2.0
- Apache ZooKeeper Essentials
- 自己動手寫搜索引擎
- Python機器學習:數據分析與評分卡建模(微課版)
- HoloLens Beginner's Guide
- Unity Virtual Reality Projects
- Mastering Android Game Development
- SQL基礎教程(第2版)
- 單片機C語言程序設計實訓100例
- 0 bug:C/C++商用工程之道
- Internet of Things with ESP8266
- 奔跑吧 Linux內核
- Microsoft XNA 4.0 Game Development Cookbook
- Python全棧開發:數據分析