hungryturtlecode

Hungry Turtle Code Logo

AngularJS Quiz App Tutorial Part 9 - More About Factories

Please share this post if you enjoy it!

Mocking An API Request With Angular Factories

We have already covered how to build a basic factory when we built the quizMetrics factory in a previous part. In this part, we will build another factory; this time to mock data coming from an API. Let’s continue using Angular factories to further separate concerns.

Earlier, we mentioned that we won’t be using an actual API in this application but will instead copy and paste the JSON into the scripts. This is what we did for the list data in the list controller.

The only problem with that is that now we have the JSON copied into the list controller and now if we followed that same idea we would copy the quiz JSON into the quiz controller. Things are starting to get messy.

To solve this problem we will create a factory and copy all of the data into that instead (including refactoring the data out of the list controller and into the factory). That way our controllers have no knowledge of where the data is coming from, just that it is getting the data.

This way, we can copy the data in for now, but at a later date we can actually make API requests and put all the logic into the factory and the controller still receives the data in the exact same way. We won’t have to touch the controllers to make this change. Can you see how this allows our application to grow over time?

We don’t have to completely refactor the entire codebase to allow API calls, which is something we would have to do if we copied the data into each controller separately. We now put all that data into a factory and separate all of our concerns. It’s all coming together nicely.

If you want to see the app for yourself, check it out here.

The git repo can be found here.

The next part can be found here

Get On With It!

Ok, enough chat, let’s write some code.

We will create another script in the factories directory and call it dataservice.js. We will start this in the exact same way we started the quizMetrics factory.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(function(){

  angular
    .module("turtleFacts")
    .factory("DataService", DataService);


  function DataService(){

    var dataObj = {
      // We will add properties to this object soon
    };

    return dataObj;
  }

})();

Now we will add the object we will return and start adding in some of the data that we need. The turtlesData variable that contained the JSON from our list controller will be copied over into this factory and we will create a new variable to hold the JSON for the quiz questions.

1
2
3
4
5
6
7
8
9
function DataService(){

  var dataObj = {
    turtlesData: turtlesData,
    quizQuestions: quizQuestions
  };

  return dataObj;
}

We can see that we have created some entries in the dataObj which reference variables that contain JSON that we have copied into the script at the bottom. This is then returned from the factory so that our controllers can use this data.

The turtlesData is the exact same JSON as we used before. The quizQuestions is new JSON which can be seen here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
var quizQuestions  = [
  {
    type: "text",
    text: "How much can a loggerhead weigh?",
    possibilities: [
        {
            answer: "Up to 20kg"
        },
        {
            answer: "Up to 115kg"
        },
        {
            answer: "Up to 220kg"
        },
        {
            answer: "Up to 500kg"
        }
    ],
    selected: null,
    correct: null
  },
  {
    type: "text",
    text: "What is the typical lifespan of a Green Sea Turtle?",
    possibilities: [
        {
            answer: "150 years"
        },
        {
            answer: "10 years"
        },
        {
            answer: "80 years"
        },
        {
            answer: "40 years"
        }
    ],
    selected: null,
    correct: null
  },
  {
    type: "image",
    text: "Which of these is the Alligator Snapping Turtle?",
    possibilities: [
        {
            answer: "https://c1.staticflickr.com/3/2182/2399413165_bcc8031cac_z.jpg?zz=1"
        },
        {
            answer: "http://images.nationalgeographic.com/wpf/media-live/photos/000/006/cache/ridley-sea-turtle_688_600x450.jpg"
        },
        {
            answer: "https://static-secure.guim.co.uk/sys-images/Guardian/Pix/pictures/2011/8/13/1313246505515/Leatherback-turtle-007.jpg"
        },
        {
            answer: "https://upload.wikimedia.org/wikipedia/commons/e/e3/Alligator_snapping_turtle_-_Geierschildkr%C3%B6te_-_Alligatorschildkr%C3%B6te_-_Macrochelys_temminckii_01.jpg"
        }
    ],
    selected: null,
    correct: null
  },
  {
    type: "image",
    text: "Which of these is the Green Turtle?",
    possibilities: [
        {
            answer: "http://www.what-do-turtles-eat.com/wp-content/uploads/2014/10/Sea-Turtles-Habitat.jpg"
        },
        {
            answer: "https://upload.wikimedia.org/wikipedia/commons/7/7f/Kemp's_Ridley_sea_turtle_nesting.JPG"
        },
        {
            answer: "https://upload.wikimedia.org/wikipedia/commons/e/e3/Alligator_snapping_turtle_-_Geierschildkr%C3%B6te_-_Alligatorschildkr%C3%B6te_-_Macrochelys_temminckii_01.jpg"
        },
        {
            answer: "http://assets.worldwildlife.org/photos/163/images/carousel_small/SCR_290360hawskbill-why-matter-LG.jpg?1345565532"
        }
    ],
    selected: null,
    correct: null
  },
  {
    type: "text",
    text: "Where does the Kemp's Ridley Sea Turtle live?'",
    possibilities: [
        {
            answer: "Tropical waters all around the world"
        },
        {
            answer: "Eastern Australia"
        },
        {
            answer: "Coastal North Atlantic"
        },
        {
            answer: "South pacific islands"
        }
    ],
    selected: null,
    correct: null
  },
  {
    type: "text",
    text: "What is the most common turtle in US waters?",
    possibilities: [
        {
            answer: "Loggerhead turtle"
        },
        {
            answer: "Leatherback turtle"
        },
        {
            answer: "Hawksbill Turtle"
        },
        {
            answer: "Alligator Snapping Turtle"
        }
    ],
    selected: null,
    correct: null
  },
  {
    type: "text",
    text: "What is the largest sea turtle on earth?",
    possibilities: [
        {
            answer: "Eastern Snake Necked Turtle"
        },
        {
            answer: "Olive Ridley Sea Turtle"
        },
        {
            answer: "Kemp's Ridley Sea Turtle'"
        },
        {
            answer: "Leatherback"
        }
    ],
    selected: null,
    correct: null
  },
  {
    type: "image",
    text: "Which of these is the Olive Ridley Turtle?",
    possibilities: [
        {
            answer: "http://i.telegraph.co.uk/multimedia/archive/02651/loggerheadTurtle_2651448b.jpg"
        },
        {
            answer: "http://assets.worldwildlife.org/photos/163/images/carousel_small/SCR_290360hawskbill-why-matter-LG.jpg?1345565532"
        },
        {
            answer: "http://images.nationalgeographic.com/wpf/media-live/photos/000/006/cache/ridley-sea-turtle_688_600x450.jpg"
        },
        {
            answer: "https://upload.wikimedia.org/wikipedia/commons/7/7f/Kemp's_Ridley_sea_turtle_nesting.JPG"
        }
    ],
    selected: null,
    correct: null
  },
  {
    type: "text",
    text: "How Heavy can a leatherback turtle be?",
    possibilities: [
        {
            answer: "900kg"
        },
        {
            answer: "40kg"
        },
        {
            answer: "110kg"
        },
        {
            answer: "300kg"
        }
    ],
    selected: null,
    correct: null
  },
  {
    type: "text",
    text: "Which of these turtles are herbivores?",
    possibilities: [
        {
            answer: "Loggerhead Turtle"
        },
        {
            answer: "Hawksbill Turtle"
        },
        {
            answer: "Leatherback Turtle"
        },
        {
            answer: "Green Turtle"
        }
    ],
    selected: null,
    correct: null
  }
];

Taking a closer look at the JSON for each quiz question we see that it has a type (text, or image) which allows us to have different styles of questions, the text of the question itself, four possible answers and two flags called selected and correct which are initialised to null. We will discuss all of this more later.

Angular Factories leads to Dependency Injection

We have seen it all before. So we will just go back to our controllers and add the “dataservice” factory to the array of dependencies. Also adding it as an argument to the controller functions. </span>

Here is the quiz controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(function(){

  angular
    .module("turtleFacts")
    .controller("quizCtrl", QuizController);

  QuizController.$inject = ['quizMetrics', 'DataService'];

  function QuizController(quizMetrics, DataService){
    var vm = this;

    vm.quizMetrics = quizMetrics; 
    vm.dataService = DataService;
      
  }

})();

We also do the same in the list controller. While we are in there we can delete the turtlesData variable that we now have in the factory (if you haven’t already removed it).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(function(){

  angular
    .module("turtleFacts")
    .controller("listCtrl", ListController);

  ListController.$inject = ['quizMetrics', 'DataService'];

  function ListController(quizMetrics, DataService){

    var vm = this;
    
    vm.quizMetrics = quizMetrics;
    vm.data = DataService.turtlesData; 
    vm.activeTurtle = {}; 
    vm.changeActiveTurtle = changeActiveTurtle; 
    vm.activateQuiz = activateQuiz; 
    vm.search = ""; 

    function changeActiveTurtle(index){
      vm.activeTurtle = index;
    }

    function activateQuiz(){
      quizMetrics.changeState(true);
    }
  }

})();

Notice that we previously had a line vm.data = turtlesData but of course, the turtlesData variable doesn’t exist in the controller, it is now inside the dataservice factory. So we change that line to vm.data = DataService.turtlesData.

Before we forget, we need to add the new dataservice factory script to the tags at the bottom of our HTML. The scripts area of the HTML now looks like this:

1
2
3
4
5
<script src="js/app.js"></script>
<script src="js/controllers/list.js"></script>
<script src="js/controllers/quiz.js"></script>   
<script src="js/factories/quizMetrics.js"></script>
<script src="js/factories/dataservice.js"></script>

Onwards And Upwards

In part 10 we will use the quiz JSON we now have access to and start creating the bootstrap markup for the quiz.

See you over there.

Adrian

Please give this post a share if you enjoyed it. Everyone needs that awesome friend to send them amazing stuff.