Skip to content

Commit f803c22

Browse files
author
David Milne
committed
Added ability to go back to previous pages, and tests to check page rules
1 parent 2fd8b05 commit f803c22

File tree

5 files changed

+321
-617
lines changed

5 files changed

+321
-617
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ The `state` provides a list of `fields`, which are clones of the `schema.fields`
140140
* `field.visible` is set to **false** if the field is not part of the current page, or if it has been hidden by a field rule. The idea is that, to present the survey, you do an `ng-repeat` over all fields, and use `ng-if` or `ng-show` to only present the visible ones.
141141
* `field.answered` is set to **true** if the field has been satisfactorily answered.
142142
* `field.missing` is set to **true** if one has attempted to continue, but has not satisfactorily answered this (mandatory) question. The idea is that you should visibly flag such fields to the user.
143-
* `field.pageIndex` indicates which page this field is shown with. This is an index into the `scope.pages` array described in the next section.
143+
* `field.pageIndex` indicates which page this field is shown with. This is an index into the `state.pages` array described in the next section.
144144

145145
For each field, you will want to inspect the `field.type` variable to display the appropriate widget (text input, radio buttons, etc). You will also probably want to ignore any fields with `field.type=='pagebreak'` since these get treated a bit differently (below).
146146

@@ -156,7 +156,7 @@ Each entry of in the array is a field (with `type=pagebreak`), along with a few
156156
* `page.pageIndex` is the index of this page in the `state.pages` array
157157
* `page.current` is set to **true** if the user is currently on this page
158158

159-
The index of the current page is also stored in `scope.response.pageIndex`
159+
The index of the current page is also stored in `response.pageIndex`, and for convenience the current page is stored in `state.page`
160160

161161
###Extra notes
162162

ask-logic.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ var AskLogic = angular.module('ask-logic', [])
244244
else
245245
return 'sw' ;
246246
}
247-
248247
}
249248

250249

@@ -404,6 +403,13 @@ var AskLogic = angular.module('ask-logic', [])
404403
f.relevantTriggers.push(trigger) ;
405404
}) ;
406405
}) ;
406+
407+
_.each(this.pageRules, function(rule) {
408+
_.each(rule.triggers, function(trigger) {
409+
if (trigger.questionId == f.id)
410+
f.relevantTriggers.push(trigger) ;
411+
}) ;
412+
}) ;
407413
} ;
408414

409415

@@ -485,6 +491,40 @@ var AskLogic = angular.module('ask-logic', [])
485491

486492
}
487493

494+
SurveyState.prototype.handleBack = function() {
495+
496+
this.response.completed = false ;
497+
498+
if (this.response.pageIndex == 0) {
499+
return ;
500+
}
501+
502+
var prevUnskippedPage ;
503+
504+
for (var i = this.response.pageIndex - 1 ; i>=0 ; i--) {
505+
506+
var p = this.pages[i] ;
507+
508+
var skipState = _.find(p.pageRuleStates, function (state) {
509+
return state == 'skip' ;
510+
}) ;
511+
512+
if (!skipState) {
513+
prevUnskippedPage = p ;
514+
break ;
515+
}
516+
}
517+
518+
if (prevUnskippedPage) {
519+
this.response.pageIndex = prevUnskippedPage.pageIndex ;
520+
} else {
521+
//this should never happen, but if it does then just jump to first page
522+
this.response.pageIndex = 0 ;
523+
}
524+
525+
this.handleCurrentPageChanged() ;
526+
}
527+
488528
SurveyState.prototype.handleCurrentPageChanged = function() {
489529

490530
if (this.response.pageIndex == null) {
@@ -494,6 +534,8 @@ var AskLogic = angular.module('ask-logic', [])
494534
this.response.pageIndex = 0 ;
495535
}
496536

537+
this.page = this.pages[this.response.pageIndex] ;
538+
497539
_.each(this.pages, function(page, pageIndex) {
498540
page.current = (pageIndex == this.response.pageIndex) ;
499541
}, this) ;
@@ -552,6 +594,9 @@ var AskLogic = angular.module('ask-logic', [])
552594

553595
SurveyState.prototype.handleTriggerStateChanged = function(trigger) {
554596

597+
//console.log("trigger state changed") ;
598+
//console.log(trigger);
599+
555600
var ruleType, rule ;
556601

557602
if (trigger.fieldRuleIndex != null) {
@@ -630,6 +675,9 @@ var AskLogic = angular.module('ask-logic', [])
630675

631676
SurveyState.prototype.handlePageRuleStateChanged = function(rule) {
632677

678+
//console.log("rule state changed")
679+
//console.log(rule) ;
680+
633681
//identify earliest effected page, which is the next page after the last trigger
634682
var earliestEffectedPageIndex ;
635683
_.each(rule.triggers, function(trigger) {

test/genderAndParenthood-spec.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
describe('genderAndParenthood', function() {
2+
3+
var SurveyStates ;
4+
5+
beforeEach(function() {
6+
7+
module('ask-logic') ;
8+
9+
inject(function ($injector) {
10+
SurveyStates = $injector.get('SurveyStates') ;
11+
}) ;
12+
13+
jasmine.getJSONFixtures().fixturesPath='base/test/schemas';
14+
15+
schema = getJSONFixture('genderAndParenthood.json') ;
16+
17+
}) ;
18+
19+
it("Should handle no gender (skip to end)", function() {
20+
21+
//can skip right to end if user doesn't give a gender,
22+
//and then skip right back to start if they ask to go back
23+
24+
var response = {
25+
answers:{
26+
qGender:{choice:"I'd rather not say"},
27+
qDependents: {number:2}
28+
}
29+
} ;
30+
var state = SurveyStates.init(schema, response) ;
31+
32+
state.handleContinue() ;
33+
expect(response.completed).toEqual(true) ;
34+
35+
state.handleBack() ;
36+
expect(response.pageIndex).toEqual(0) ;
37+
expect(response.completed).toEqual(false) ;
38+
}) ;
39+
40+
it("Should handle male with no dependents", function() {
41+
42+
var response = {
43+
answers:{
44+
qGender:{choice:"Male"},
45+
qDependents: {number:0}
46+
}
47+
} ;
48+
var state = SurveyStates.init(schema, response) ;
49+
50+
state.handleContinue()
51+
expect(state.page.id).toEqual('pMales') ;
52+
expect(response.completed).toEqual(false) ;
53+
54+
state.handleContinue() ;
55+
expect(response.completed).toEqual(true) ;
56+
57+
state.handleBack()
58+
expect(state.page.id).toEqual('pMales') ;
59+
expect(response.completed).toEqual(false) ;
60+
61+
state.handleBack()
62+
expect(response.pageIndex).toEqual(0) ;
63+
expect(response.completed).toEqual(false) ;
64+
}) ;
65+
66+
it("Should handle male with dependents", function() {
67+
68+
var response = {
69+
answers:{
70+
qGender:{choice:"Male"},
71+
qDependents: {number:2}
72+
}
73+
} ;
74+
var state = SurveyStates.init(schema, response) ;
75+
76+
state.handleContinue()
77+
expect(state.page.id).toEqual('pMales') ;
78+
expect(response.completed).toEqual(false) ;
79+
80+
state.handleContinue() ;
81+
expect(state.page.id).toEqual('pFathers') ;
82+
expect(response.completed).toEqual(false) ;
83+
84+
state.handleContinue() ;
85+
expect(response.completed).toEqual(true) ;
86+
87+
state.handleBack()
88+
expect(state.page.id).toEqual('pFathers') ;
89+
expect(response.completed).toEqual(false) ;
90+
91+
state.handleBack()
92+
expect(state.page.id).toEqual('pMales') ;
93+
expect(response.completed).toEqual(false) ;
94+
95+
state.handleBack()
96+
expect(response.pageIndex).toEqual(0) ;
97+
expect(response.completed).toEqual(false) ;
98+
}) ;
99+
100+
it("Should handle female with no dependents", function() {
101+
102+
var response = {
103+
answers:{
104+
qGender:{choice:"Female"},
105+
qDependents: {number:0}
106+
}
107+
} ;
108+
var state = SurveyStates.init(schema, response) ;
109+
110+
state.handleContinue()
111+
expect(state.page.id).toEqual('pFemales') ;
112+
expect(response.completed).toEqual(false) ;
113+
114+
state.handleContinue() ;
115+
expect(response.completed).toEqual(true) ;
116+
117+
state.handleBack()
118+
expect(state.page.id).toEqual('pFemales') ;
119+
expect(response.completed).toEqual(false) ;
120+
121+
state.handleBack()
122+
expect(response.pageIndex).toEqual(0) ;
123+
expect(response.completed).toEqual(false) ;
124+
}) ;
125+
126+
it("Should handle female with dependents", function() {
127+
128+
var response = {
129+
answers:{
130+
qGender:{choice:"Female"},
131+
qDependents: {number:2}
132+
}
133+
} ;
134+
var state = SurveyStates.init(schema, response) ;
135+
136+
state.handleContinue()
137+
expect(state.page.id).toEqual('pFemales') ;
138+
expect(response.completed).toEqual(false) ;
139+
140+
state.handleContinue() ;
141+
expect(state.page.id).toEqual('pMothers') ;
142+
expect(response.completed).toEqual(false) ;
143+
144+
state.handleContinue() ;
145+
expect(response.completed).toEqual(true) ;
146+
147+
state.handleBack()
148+
expect(state.page.id).toEqual('pMothers') ;
149+
expect(response.completed).toEqual(false) ;
150+
151+
state.handleBack()
152+
expect(state.page.id).toEqual('pFemales') ;
153+
expect(response.completed).toEqual(false) ;
154+
155+
state.handleBack()
156+
expect(response.pageIndex).toEqual(0) ;
157+
expect(response.completed).toEqual(false) ;
158+
}) ;
159+
160+
}) ;
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{
2+
"title" : "Gender and Parenthood",
3+
"fields" : [ {
4+
"id" : "qGender",
5+
"text" : "Gender",
6+
"hidden" : false,
7+
"optional" : false,
8+
"choices" : [ {
9+
"name" : "Male"
10+
}, {
11+
"name" : "Female"
12+
}, {
13+
"name" : "I'd rather not say"
14+
} ],
15+
"allowOther" : false,
16+
"type" : "singlechoice"
17+
}, {
18+
"id" : "qDependents",
19+
"text" : "Number of dependents",
20+
"hidden" : false,
21+
"optional" : false,
22+
"type" : "numeric"
23+
}, {
24+
"id" : "pMales",
25+
"text" : "For men",
26+
"hidden" : false,
27+
"type" : "pageBreak"
28+
}, {
29+
"id" : "iMales",
30+
"text" : "This is where we ask questions for all men",
31+
"hidden" : false,
32+
"type" : "instruction"
33+
}, {
34+
"id" : "pFathers",
35+
"text" : "For fathers",
36+
"hidden" : false,
37+
"type" : "pageBreak"
38+
}, {
39+
"id" : "iFathers",
40+
"text" : "This is where we ask questions for all fathers",
41+
"hidden" : false,
42+
"type" : "instruction"
43+
}, {
44+
"id" : "pFemales",
45+
"text" : "For women",
46+
"hidden" : false,
47+
"type" : "pageBreak"
48+
}, {
49+
"id" : "iFemales",
50+
"text" : "This is where we ask questions for all women",
51+
"hidden" : false,
52+
"type" : "instruction"
53+
}, {
54+
"id" : "pMothers",
55+
"text" : "For mothers",
56+
"hidden" : false,
57+
"type" : "pageBreak"
58+
}, {
59+
"id" : "iMothers",
60+
"text" : "This is where we ask questions for all mothers",
61+
"hidden" : false,
62+
"type" : "instruction"
63+
} ],
64+
"pageRules" : [ {
65+
"triggers" : [ {
66+
"questionId" : "qGender",
67+
"condition" : "is",
68+
"value" : "I'd rather not say"
69+
} ],
70+
"actions" : [ {
71+
"action" : "skipToEnd"
72+
} ]
73+
}, {
74+
"triggers" : [ {
75+
"questionId" : "qGender",
76+
"condition" : "is",
77+
"value" : "Male"
78+
} ],
79+
"actions" : [ {
80+
"action" : "skip",
81+
"pageId" : "pFemales"
82+
}, {
83+
"action" : "skip",
84+
"pageId" : "pMothers"
85+
} ]
86+
}, {
87+
"triggers" : [ {
88+
"questionId" : "qGender",
89+
"condition" : "is",
90+
"value" : "Female"
91+
} ],
92+
"actions" : [ {
93+
"action" : "skipTo",
94+
"pageId" : "pFemales"
95+
} ]
96+
}, {
97+
"triggers" : [ {
98+
"questionId" : "qDependents",
99+
"condition" : "equal",
100+
"value" : 0
101+
} ],
102+
"actions" : [ {
103+
"action" : "skip",
104+
"pageId" : "pMothers"
105+
}, {
106+
"action" : "skip",
107+
"pageId" : "pFathers"
108+
} ]
109+
} ]
110+
}

0 commit comments

Comments
 (0)