This is the first part of Introducing Angular Multi-Step Wizard using UI-Router Series 2. You will learn how to build a multi-step wizard using the following technologies:
The source code for this tutorial series is published on GitHub. Demo application is hosted in Microsoft Azure.
Part 1: Create a SPA in UI-Router 1.0 with Angular 1.5+ component-based architecture
In the previous tutorial series, we created the Multi-Step Wizard in UI-Router legacy v 0.x for Angular 1. In this tutorial, we will continue to use Visual Studio Code to build the initial structure of the Single-Page Application (SPA) using UI-Router 1.0 with Angular 1.5+ component-based architecture.
Client-Side Technologies
The application structure, styles, and patterns follow the recommendations outlined in John Papa’s Angular 1 Style Guide. The application idea is inspired by Scotch.io’s tutorial.
Task 1. Set up the Project Structure
I prefer to organize my project based on features/modules. Building a maintainable, scalable, and well-organized application should start from the beginning. Using DRY and SRP patterns, we will create smaller code files that each handle one specific job. This approach has helped me personally to locate, enhance, and debug requested features from clients quickly.
Let’s creating folders and copying files to C:\_tutorials\ng-multi-step-wizard-ui-router1 from GitHub as listed below:
ng-multi-step-wizard-ui-router1
|--app
|--address
|--form
|--personal
|--result
|--work
|--content
|--css
|--riliwan-rabo.css
|--style.css
|--images
|--favicon.ico
Task 2. Create startup page
-
Add the index.html file to the ng-multi-step-wizard-ui-router1 folder.
-
Replace the code in this file with the following:
<!DOCTYPE html>
<html lang="en" ng-app="wizardApp">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Multi-Step Wizard using AngularJS 1.5+ And UI-Router 1.0 by Cathy Wun</title>
<!-- CSS Files -->
<link rel="icon" type="image/png" href="content/images/favicon.ico" />
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.css" />
<link rel="stylesheet" href="content/css/riliwan-rabo.css" />
<link rel="stylesheet" href="content/css/style.css" />
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="//oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<!-- views will be injected here -->
<section style="background:#efefe9;">
<div class="container">
<div ui-view></div>
</div>
</section>
<!-- Vendor js libraries -->
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/1.0.0-rc.1/angular-ui-router.min.js"></script>
<!-- Bootstrapping -->
<script src="app/app.module.js"></script>
<!-- Wizard - Form feature -->
<script src="app/form/form.component.js"></script>
<script src="app/form/form.controller.js"></script>
<script src="app/form/formData.value.js"></script>
<!-- Wizard - Personal feature -->
<script src="app/personal/personal.component.js"></script>
<script src="app/personal/personal.controller.js"></script>
<!-- Wizard - Work feature -->
<script src="app/work/work.component.js"></script>
<script src="app/work/work.controller.js"></script>
<!-- Wizard - Address feature -->
<script src="app/address/address.component.js"></script>
<script src="app/address/address.controller.js"></script>
<!-- Wizard - Result feature -->
<script src="app/result/result.component.js"></script>
<script src="app/result/result.controller.js"></script>
</body>
</html>
The index.html file serve as our startup page. It loads all our resources (.css and .js) and adds a ui-view
directive of UI-Router
to inject our views. It also uses ng-app
directive to designate wizardApp
as the root module of our Angular application.
Task 3. Create our application and routes
The app.module.js file creates our angular app and configures state-based routes. It is defined inside our root module named wizardApp
,
-
Add the app.module.js file to the app folder.
-
Replace the code in this file with the following:
(function() {
'use strict';
var app = angular.module('wizardApp', ['ui.router'])
app.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/form/personal');
$stateProvider
.state('form', {
url: '/form',
component: 'formComponent'
})
.state('form.personal', {
url: '/personal',
component: 'personalComponent'
})
.state('form.work', {
url: '/work',
component: 'workComponent'
})
.state('form.address', {
url: '/address',
component: 'addressComponent'
})
.state('form.result', {
url: '/result',
component: 'resultComponent'
})
}
]);
})();
Line 6 is loading our app-specific module (ui.router
) in the app.
-
Lines 19 – 47 have our state-based routes created. Each state has its own url
and component
.
The form
state is the parent state to the following child states:
form.personal
form.work
form.address
form.result
When one of the child states is active, the parent state is implicitly active as well. Child state will route to its component. The child component is placed inside its parent’s ui-view
.

Task 4. Create the Form feature
The Form
feature contains the following files:
- form.component.js: is a component controls the view, the controller, and input data for the
Form
- formData.value.js: contains input data
- form.controller.js: is a controller contains the properties and functions that are bound to the
Form
view. It also contains the presentation logic for the Form
view, and is the glue between the data and the view. - form.html: is an AngularJS HTML template that defines the view for the
Form
Task 5. Add component to the Form feature
-
Add the form.component.js file to the form folder.
-
Replace the code in this file with the following:
(function () {
'use strict';
angular
.module('wizardApp')
.component('formComponent', {
templateUrl: 'app/form/form.html',
controller: 'FormController',
controllerAs: 'vm'
})
})();
-
The FormComponent
is defined inside our root module named wizardApp
. It has its own templateUrl (view file) and controller.
Task 6. Add data model to the Form feature
-
Add the formData.value.js to the form folder.
-
Replace the code in this file with the following:
(function () {
'use strict';
angular
.module('wizardApp')
.value('FormDataModel', FormDataModel);
function FormDataModel() {
this.firstName = '';
this.lastName = '';
this.email = '';
this.work = 'Code';
this.street = '';
this.city = '';
this.state = '';
this.zip = '';
}
})();
The FormDataModel
is defined inside a root module named wizardApp
. It contains all input data.
Task 7. Add controller and view to the Form feature
-
Add the form.controller.js file to the form folder.
-
Replace the code in this file with the following:
(function () {
'use strict';
angular
.module('wizardApp')
.controller('FormController', FormController);
FormController.$inject = ['FormDataModel'];
function FormController(FormDataModel) {
var vm = this;
vm.title = 'Multi-Step Wizard';
vm.formData = new FormDataModel();
vm.$onInit = activate;
vm.getData = getData;
function activate() {
console.log(vm.title + ' loaded!');
}
function getData() {
return vm.formData;
}
}
})();
The FormController
is defined inside our root module named wizardApp
. We will store our data into formData
object. After the form feature has been loaded successfully, we use the console.log()
method to write Multi-Step Wizard loaded! into the browser console. To see the result, activate the browser console with F12, and select Console in the menu.
-
Add the form.html file to the form folder.
-
Replace the code in this file with the following:
<div class="row">
<div class="board">
<!-- Circular Tab Area -->
<div class="board-inner" id="status-buttons">
<ul class="nav nav-tabs" id="myTab">
<div class="liner"></div>
<!-- circular user icon -->
<li>
<a ui-sref-active="active" ui-sref=".personal" data-toggle="tab" title="personal">
<span class="round-tabs one">
</span>
</a>
</li>
<!-- circular tasks icon -->
<li>
<a ui-sref-active="active" ui-sref=".work" data-toggle="tab" title="work">
<span class="round-tabs two">
</span>
</a>
</li>
<!-- circular home icon -->
<li>
<a ui-sref-active="active" ui-sref=".address" data-toggle="tab" title="address">
<span class="round-tabs three">
</span>
</a>
</li>
<!-- circular ok icon -->
<li>
<a ui-sref-active="active" ui-sref=".result" data-toggle="tab" title="result">
<span class="round-tabs four">
</span>
</a>
</li>
</ul>
<div class="clearfix"></div>
</div>
<!-- End Circular Tab Area -->
<!-- Content Area -->
<div class="tab-content">
<!-- Nested view -->
<div ui-view></div>
</div>
<!-- End Content Area -->
</div>
<!-- For Debugging: show our formData as it is being typed -->
<pre>{{ vm.formData | json }}</pre>
</div>
The form.html uses Bootstrap for quick styling and building responsive layouts. It also provides circular tab menus to navigate to different view. To highlight active circular tab menus, we add ui-sref-active
directive of UI-Router
to them. The active class will be activated if the current state matches the state in ui-sref
.
-
We have the following circular icons in tab menu:

-
Lines 9-15 assign the circular user
icon to the Personal
tab. When you click the user
icon in the tab menu, the Personal
view will be placed in the content area of the form
view:

-
Lines 18-24 assign the circular tasks
icon to the Work
tab. When you click the circular tasks
icon in the tab menu, the Work
view will be placed in the content area of the form
view:

-
Lines 27-33 assign the circular home
icon to the Address
tab. When you click the circular home
icon in the tab menu, the Address
view will be placed in the content area of the form
view:

-
Lines 36-42 assign the circular ok
icon to the Result
tab. When you click the circular ok
icon in the tab menu, the Result
view will be placed in the content area of the form
view:

-
Line 58 will always display our formData
object in real time.

In the next tutorial, we will build the Personal, Work, Address, and Result Features using UI-Router 1.0 with Angular 1.5+ component-based architecture.
The source code for this tutorial series is published on GitHub. Demo application is hosted in Microsoft Azure.
References