├── .gitattributes
├── LICENSE
├── README.md
├── app
├── address
│ ├── address.component.js
│ ├── address.controller.js
│ └── address.html
├── app.module.js
├── form
│ ├── form.component.js
│ ├── form.controller.js
│ ├── form.html
│ └── formData.value.js
├── personal
│ ├── personal.component.js
│ ├── personal.controller.js
│ └── personal.html
├── result
│ ├── result.component.js
│ ├── result.controller.js
│ └── result.html
└── work
│ ├── work.component.js
│ ├── work.controller.js
│ └── work.html
├── content
├── css
│ ├── riliwan-rabo.css
│ └── style.css
└── images
│ └── favicon.ico
├── index.html
├── screen-address.png
├── screen-personal.png
├── screen-result.png
└── screen-work.png
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.html linguist-language=JavaScript
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Cathy Wun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Series 2: Tutorials on building a Multi-Step Wizard using UI-Router 1.0 with Angular 1.5+ component-based architecture
2 |
3 | #### [Demo site](http://multi-step-wizard.azurewebsites.net/series-2/#/form/personal)
4 |
5 | *** Personal ***
6 | 
7 |
8 | *** Work ***
9 | 
10 |
11 | *** Address ***
12 | 
13 |
14 | *** Result ***
15 | 
16 |
--------------------------------------------------------------------------------
/app/address/address.component.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('wizardApp')
6 | .component('addressComponent', {
7 | templateUrl: 'app/address/address.html',
8 | controller: 'AddressController',
9 | controllerAs: 'vm',
10 | require: {
11 | // access to the functionality of the parent component called 'formComponent'
12 | parent: '^formComponent'
13 | },
14 | bindings: {
15 | // send a changeset of 'formData' upwards to the parent component called 'formComponent'
16 | formData: '<'
17 | }
18 | })
19 | })();
--------------------------------------------------------------------------------
/app/address/address.controller.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('wizardApp')
6 | .controller('AddressController', AddressController);
7 |
8 | AddressController.$inject = [];
9 |
10 | function AddressController() {
11 | var vm = this;
12 | vm.title = 'Where do you live?';
13 | vm.formData = {};
14 |
15 | vm.$onInit = activate;
16 |
17 | ////////////////
18 |
19 | function activate() {
20 | // get data from the parent component
21 | vm.formData = vm.parent.getData();
22 | console.log('Address feature loaded!');
23 | }
24 | }
25 | })();
--------------------------------------------------------------------------------
/app/address/address.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/app.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | // Creating our angular app and inject ui-router
5 | // =============================================================================
6 | var app = angular.module('wizardApp', ['ui.router'])
7 |
8 | // Configuring our states
9 | // =============================================================================
10 | app.config(['$stateProvider', '$urlRouterProvider',
11 |
12 | function($stateProvider, $urlRouterProvider) {
13 |
14 | // For any unmatched url, redirect to /wizard/personal
15 | $urlRouterProvider.otherwise('/form/personal');
16 |
17 | $stateProvider
18 | // PARENT STATE: form state
19 | .state('form', {
20 | url: '/form',
21 | component: 'formComponent'
22 | })
23 |
24 | // NESTED STATES: child states of 'form' state
25 | // URL will become '/form/personal'
26 | .state('form.personal', {
27 | url: '/personal',
28 | component: 'personalComponent'
29 | })
30 |
31 | // URL will become /form/work
32 | .state('form.work', {
33 | url: '/work',
34 | component: 'workComponent'
35 | })
36 |
37 | // URL will become /form/address
38 | .state('form.address', {
39 | url: '/address',
40 | component: 'addressComponent'
41 | })
42 |
43 | // URL will become /form/result
44 | .state('form.result', {
45 | url: '/result',
46 | component: 'resultComponent'
47 | })
48 | }
49 | ]);
50 |
51 | })();
52 |
--------------------------------------------------------------------------------
/app/form/form.component.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('wizardApp')
6 | .component('formComponent', {
7 | templateUrl: 'app/form/form.html',
8 | controller: 'FormController',
9 | controllerAs: 'vm'
10 | })
11 | })();
--------------------------------------------------------------------------------
/app/form/form.controller.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('wizardApp')
6 | .controller('FormController', FormController);
7 |
8 | FormController.$inject = ['FormDataModel'];
9 |
10 | function FormController(FormDataModel) {
11 | var vm = this;
12 | vm.title = 'Multi-Step Wizard';
13 | // we will store all of our form data in this object
14 | vm.formData = new FormDataModel();
15 |
16 | vm.$onInit = activate;
17 | vm.getData = getData;
18 |
19 | ////////////////
20 |
21 | function activate() {
22 | console.log(vm.title + ' loaded!');
23 | }
24 |
25 | function getData() {
26 | return vm.formData;
27 | }
28 | }
29 | })();
30 |
--------------------------------------------------------------------------------
/app/form/form.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
47 |
48 |
49 |
50 |
54 |
55 |
56 |
57 |
58 |
{{ vm.formData | json }}
59 |
60 |
61 |
--------------------------------------------------------------------------------
/app/form/formData.value.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('wizardApp')
6 | .value('FormDataModel', FormDataModel);
7 |
8 | function FormDataModel() {
9 | this.firstName = '';
10 | this.lastName = '';
11 | this.email = '';
12 | this.work = 'Code';
13 | this.street = '';
14 | this.city = '';
15 | this.state = '';
16 | this.zip = '';
17 | }
18 | })();
--------------------------------------------------------------------------------
/app/personal/personal.component.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('wizardApp')
6 | .component('personalComponent', {
7 | templateUrl: 'app/personal/personal.html',
8 | controller: 'PersonalController',
9 | controllerAs: 'vm',
10 | require: {
11 | // access to the functionality of the parent component called 'formComponent'
12 | parent: '^formComponent'
13 | },
14 | bindings: {
15 | // send a changeset of 'formData' upwards to the parent component called 'formComponent'
16 | formData: '<'
17 | }
18 | })
19 | })();
--------------------------------------------------------------------------------
/app/personal/personal.controller.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('wizardApp')
6 | .controller('PersonalController', PersonalController);
7 |
8 | PersonalController.$inject = [];
9 |
10 | function PersonalController() {
11 | var vm = this;
12 | vm.title = 'Please tell us about yourself.';
13 | vm.formData = {};
14 |
15 | vm.$onInit = activate;
16 |
17 | ////////////////
18 |
19 | function activate() {
20 | // get data from the parent component
21 | vm.formData = vm.parent.getData();
22 | console.log('Personal feature loaded!');
23 | }
24 |
25 | }
26 | })();
27 |
--------------------------------------------------------------------------------
/app/personal/personal.html:
--------------------------------------------------------------------------------
1 |
2 |
{{vm.title}}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/result/result.component.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('wizardApp')
6 | .component('resultComponent', {
7 | templateUrl: 'app/result/result.html',
8 | controller: 'ResultController',
9 | controllerAs: 'vm',
10 | require: {
11 | // access to the functionality of the parent component called 'formComponent'
12 | parent: '^formComponent'
13 | }
14 | })
15 | })();
--------------------------------------------------------------------------------
/app/result/result.controller.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('wizardApp')
6 | .controller('ResultController', ResultController);
7 |
8 | ResultController.$inject = [];
9 |
10 | function ResultController() {
11 | var vm = this;
12 | vm.title = 'Thanks for staying tuned!';
13 | vm.formData = {};
14 |
15 | vm.$onInit = activate;
16 |
17 | ////////////////
18 |
19 | function activate() {
20 | // get data from the parent component
21 | vm.formData = vm.parent.getData();
22 | console.log('Result feature loaded!');
23 | }
24 | }
25 | })();
26 |
--------------------------------------------------------------------------------
/app/result/result.html:
--------------------------------------------------------------------------------
1 |
2 |
{{vm.title}}
3 |
4 | Made with by Cathy Wun. Check out the tutorials here.
5 |
6 |
7 | Here is a copy of the information you have entered:
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{vm.formData.firstName + ' ' + vm.formData.lastName}}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | {{vm.formData.email}}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | {{vm.formData.work}}
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | {{vm.formData.street}}
50 |
51 | {{vm.formData.city + ' ' + vm.formData.state + ' ' + vm.formData.zip}}
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app/work/work.component.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('wizardApp')
6 | .component('workComponent', {
7 | templateUrl: 'app/work/work.html',
8 | controller: 'WorkController',
9 | controllerAs: 'vm',
10 | require: {
11 | // access to the functionality of the parent component called 'formComponent'
12 | parent: '^formComponent'
13 | },
14 | bindings: {
15 | // send a changeset of 'formData' upwards to the parent component called 'formComponent'
16 | formData: '<'
17 | }
18 | })
19 | })();
--------------------------------------------------------------------------------
/app/work/work.controller.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('wizardApp')
6 | .controller('WorkController', WorkController);
7 |
8 | WorkController.$inject = [];
9 |
10 | function WorkController() {
11 | var vm = this;
12 | vm.title = 'What do you do?';
13 | vm.formData = {};
14 |
15 | vm.$onInit = activate;
16 |
17 | ////////////////
18 |
19 | function activate() {
20 | // get data from the parent component
21 | vm.formData = vm.parent.getData();
22 | console.log('Work feature loaded!');
23 | }
24 | }
25 | })();
--------------------------------------------------------------------------------
/app/work/work.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/content/css/riliwan-rabo.css:
--------------------------------------------------------------------------------
1 | @import url(http://fonts.googleapis.com/css?family=Roboto+Condensed:400,700);
2 | /* written by riliwan balogun http://www.facebook.com/riliwan.rabo*/
3 | .board{
4 | width: 75%;
5 | margin: 60px auto;
6 | height: 500px;
7 | background: #fff;
8 | /*box-shadow: 10px 10px #ccc,-10px 20px #ddd;*/
9 | }
10 | .board .nav-tabs {
11 | position: relative;
12 | /* border-bottom: 0; */
13 | /* width: 80%; */
14 | margin: 40px auto;
15 | margin-bottom: 0;
16 | box-sizing: border-box;
17 |
18 | }
19 |
20 | .board > div.board-inner{
21 | background: #fafafa url(http://subtlepatterns.com/patterns/geometry2.png);
22 | background-size: 30%;
23 | }
24 |
25 | p.narrow{
26 | width: 60%;
27 | margin: 10px auto;
28 | }
29 |
30 | .liner{
31 | height: 2px;
32 | background: #ddd;
33 | position: absolute;
34 | width: 80%;
35 | margin: 0 auto;
36 | left: 0;
37 | right: 0;
38 | top: 50%;
39 | z-index: 1;
40 | }
41 |
42 | .nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
43 | color: #555555;
44 | cursor: default;
45 | /* background-color: #ffffff; */
46 | border: 0;
47 | border-bottom-color: transparent;
48 | }
49 |
50 | span.round-tabs{
51 | width: 70px;
52 | height: 70px;
53 | line-height: 70px;
54 | display: inline-block;
55 | border-radius: 100px;
56 | background: white;
57 | z-index: 2;
58 | position: absolute;
59 | left: 0;
60 | text-align: center;
61 | font-size: 25px;
62 | }
63 |
64 | span.round-tabs.one{
65 | color: rgb(34, 194, 34);border: 2px solid rgb(34, 194, 34);
66 | }
67 |
68 | li.active span.round-tabs.one{
69 | background: #fff !important;
70 | border: 2px solid #ddd;
71 | color: rgb(34, 194, 34);
72 | }
73 |
74 | span.round-tabs.two{
75 | color: #febe29;border: 2px solid #febe29;
76 | }
77 |
78 | li.active span.round-tabs.two{
79 | background: #fff !important;
80 | border: 2px solid #ddd;
81 | color: #febe29;
82 | }
83 |
84 | span.round-tabs.three{
85 | color: #3e5e9a;border: 2px solid #3e5e9a;
86 | }
87 |
88 | li.active span.round-tabs.three{
89 | background: #fff !important;
90 | border: 2px solid #ddd;
91 | color: #3e5e9a;
92 | }
93 |
94 | span.round-tabs.four{
95 | color: #f1685e;border: 2px solid #f1685e;
96 | }
97 |
98 | li.active span.round-tabs.four{
99 | background: #fff !important;
100 | border: 2px solid #ddd;
101 | color: #f1685e;
102 | }
103 |
104 | span.round-tabs.five{
105 | color: #999;border: 2px solid #999;
106 | }
107 |
108 | li.active span.round-tabs.five{
109 | background: #fff !important;
110 | border: 2px solid #ddd;
111 | color: #999;
112 | }
113 |
114 | .nav-tabs > li.active > a span.round-tabs{
115 | background: #fafafa;
116 | }
117 | .nav-tabs > li {
118 | /*width: 20%;*/
119 | width: 25%;
120 | }
121 | /*li.active:before {
122 | content: " ";
123 | position: absolute;
124 | left: 45%;
125 | opacity:0;
126 | margin: 0 auto;
127 | bottom: -2px;
128 | border: 10px solid transparent;
129 | border-bottom-color: #fff;
130 | z-index: 1;
131 | transition:0.2s ease-in-out;
132 | }*/
133 | .nav-tabs > li:after {
134 | content: " ";
135 | position: absolute;
136 | left: 45%;
137 | opacity:0;
138 | margin: 0 auto;
139 | bottom: 0px;
140 | border: 5px solid transparent;
141 | border-bottom-color: #ddd;
142 | transition:0.1s ease-in-out;
143 |
144 | }
145 | .nav-tabs > li.active:after {
146 | content: " ";
147 | position: absolute;
148 | left: 45%;
149 | opacity:1;
150 | margin: 0 auto;
151 | bottom: 0px;
152 | border: 10px solid transparent;
153 | border-bottom-color: #ddd;
154 |
155 | }
156 | .nav-tabs > li a{
157 | width: 70px;
158 | height: 70px;
159 | margin: 20px auto;
160 | border-radius: 100%;
161 | padding: 0;
162 | }
163 |
164 | .nav-tabs > li a:hover{
165 | background: transparent;
166 | }
167 |
168 | .tab-content .tab-pane{
169 | position: relative;
170 | padding-top: 50px;
171 | }
172 | .tab-content .head{
173 | font-family: 'Roboto Condensed', sans-serif;
174 | font-size: 25px;
175 | text-transform: uppercase;
176 | padding-bottom: 10px;
177 | }
178 | .btn-outline-rounded{
179 | padding: 10px 40px;
180 | margin: 20px 0;
181 | border: 2px solid transparent;
182 | border-radius: 25px;
183 | }
184 |
185 | .btn.green{
186 | background-color:#5cb85c;
187 | /*border: 2px solid #5cb85c;*/
188 | color: #ffffff;
189 | }
190 |
191 |
192 |
193 | @media( max-width : 585px ){
194 |
195 | .board {
196 | width: 90%;
197 | height:auto !important;
198 | }
199 | span.round-tabs {
200 | font-size:16px;
201 | width: 50px;
202 | height: 50px;
203 | line-height: 50px;
204 | }
205 | .tab-content .head{
206 | font-size:20px;
207 | }
208 | .nav-tabs > li a {
209 | width: 50px;
210 | height: 50px;
211 | line-height:50px;
212 | }
213 |
214 | .nav-tabs > li.active:after {
215 | content: " ";
216 | position: absolute;
217 | left: 35%;
218 | }
219 |
220 | .btn-outline-rounded {
221 | padding:12px 20px;
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/content/css/style.css:
--------------------------------------------------------------------------------
1 | .btn-default {
2 | border-color: #ccc;
3 | }
4 |
5 | .tab-content .choice {
6 | text-align: center;
7 | cursor: pointer;
8 | margin-top: 38px;
9 | }
10 |
11 | .tab-content .choice i {
12 | font-size: 32px;
13 | line-height: 55px;
14 | }
15 |
16 | .btn-radio {
17 | width: 100%;
18 | }
19 | .img-radio {
20 | opacity: 0.8;
21 | margin-bottom: 5px;
22 | }
23 |
24 | .space-20 {
25 | margin-top: 20px;
26 | }
27 |
28 | /* active buttons */
29 | #status-buttons a.active span.round-tabs.one {
30 | background: rgb(34, 194, 34);
31 | color: #fff
32 | }
33 |
34 | #status-buttons a.active span.round-tabs.two {
35 | background: #febe29;
36 | color: #fff
37 | }
38 |
39 | #status-buttons a.active span.round-tabs.three {
40 | background: #3e5e9a;
41 | color: #fff
42 | }
43 |
44 | #status-buttons a.active span.round-tabs.four {
45 | background: #f1685e;
46 | color: #fff
47 | }
48 |
49 |
50 | .iradio_buttons {
51 | display: inline-block;
52 | vertical-align: middle;
53 | margin: 0;
54 | padding: 0;
55 | width: 22px;
56 | height: 22px;
57 | background: #febe29 no-repeat;
58 | border: none;
59 | cursor: pointer;
60 | }
61 | .iradio_buttons {
62 | background-position: -120px 0;
63 | }
64 | .iradio_buttons.hover {
65 | background-position: -144px 0;
66 | }
67 | .iradio_buttons.checked {
68 | background-position: -168px 0;
69 | }
--------------------------------------------------------------------------------
/content/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwun/ng-multi-step-wizard-ui-router1/bdb090c5582936b1a2778bd8a3db87eafc038419/content/images/favicon.ico
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Multi-Step Wizard using AngularJS 1.5+ And UI-Router 1.0 by Cathy Wun
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/screen-address.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwun/ng-multi-step-wizard-ui-router1/bdb090c5582936b1a2778bd8a3db87eafc038419/screen-address.png
--------------------------------------------------------------------------------
/screen-personal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwun/ng-multi-step-wizard-ui-router1/bdb090c5582936b1a2778bd8a3db87eafc038419/screen-personal.png
--------------------------------------------------------------------------------
/screen-result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwun/ng-multi-step-wizard-ui-router1/bdb090c5582936b1a2778bd8a3db87eafc038419/screen-result.png
--------------------------------------------------------------------------------
/screen-work.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwun/ng-multi-step-wizard-ui-router1/bdb090c5582936b1a2778bd8a3db87eafc038419/screen-work.png
--------------------------------------------------------------------------------