Join Alexander Zanfir for an in-depth discussion in this video Quiz play component, part of Building Applications with Angular, ASP.NET Core, and Entity Framework Core.
- [Instructor] Let's create the Play Quiz component that loads once we click on a quiz in our quiz playlist. Let's begin by creating a new file in our app folder. We'll call it playQuiz.component.ts and let's add an HTML as well. Then, lets use our play.component as a template that we can paste into our playQuiz.component.
Let's take out the selector, then update the template URL to playQuiz and the class name to playQuiz. Then, in our ngOnInIt we'll need to get all the questions from this specific quiz. To do that we'll use a URL parameter once again, so let's open up a component that uses URL parameters as a reference. We'll open up question.component.ts. The first thing we'll need to do is import the activated route, so let's copy that and I'll paste it to the top.
Then let's copy the constructor over and update ours. Next let's copy line 16, that gives us the URL parameter and paste that in to out ngOnInIt. Then let's create the quizID property. Finally instead of quizzes we'll be showing questions, so let's update it to that listing instead. And I'll close out the question.component.ts, now let's take a look at our API service.
We'll need to call out getQuestions function that gets all of the questions that are part of a quiz by passing in the quizId. So we'll use .getQuestions and we'll pass in the quizId, and then we'll set this.questions to the response. Then let's copy our play.cpmponent.html file as a template for our playQuiz.component.html file and pass that in.
We'll update line 3 with out ngfor to "let question of questions" iterate through the questions list, and we'll remove the click binding for now. Then instead of quiz.title for the text I'll have question.text. Next let's register our playQuiz components with our app module. So I'll copy line 27 and paste it below and update it to PlayQuiz.Component from playquiz.component.
And let's add it to the declarations list and then let's add a new path as well, with the path name being playQuiz and the component, PlayQuizComponent. And I'll update the path so that we can support a URL parameter to pass in the quizId just like we have on line 33. Let's save this and give it a try. Make sure you're logged in, and you have one quiz created.
We'll go to that quiz, hit edit questions, and add a few questions. Then let's manually go over to our PlayQuizComponent with the quiz ID one, since we know the first quiz we created will have an ID of one. And as you can see, the questions are being shown. Then I'd like to modify the layout of the PlayQuizComponent so that instead of using a list we'll use an angular material component that would match the purpose better.
Let's take a look at the angular material website to see what component options we have. So let's scroll down to Layout and then under Layout we can see we have an expansion panel. If we go to examples, we can see the middle example allows us to navigate through different panels which would be perfect for going through different questions. Let's go over to the API and import the module.
So I've added it to our materials import list, then let's add it to imports. Next let's go back to examples and look at the source code for the middle example. And we can see that the md accordion wraps the md-expansion-panel class, and the expansion panel class keeps repeating for every new item. And so that's where we'll put our ngfor.
So I'll go view the source for the first example and copy that source. Then let's go back to our HTML component for playQuiz and let's replace the list. And I'll paste it below the list, then I'll take the ngfor from line three, copy it, and I'll paste it in the md-expansion-panel element. Then I'll copy the question.Text, then I'll remove the list.
And on line six instead of Personal data, I'll use double curly braces, and put in question.text. I'm going to save that. Then, in the panel description, I'll take out the text and leave that as is, then I'll take out the md-form-fields, and I'll put in question.correctAnswer with end double curly braces.
And I'm going to save that and give it a try. So the first think we'll need to do in our Web app, is modify the questions in our first quiz. We'll click on the first question and add a correct answer, and add wrong answers as well. Then I'll hit edit. Next let's do the same for our second question.
Now let's manually go to our playQuiz component, and we can see that it's working. Next I'd like to add the Next button as we saw in the second example under expansion-panel. So let's scroll down, and we can see that there is an md-action-row with a button. I'll copy that, and I'll paste it below the correctAnswer.
Let's give that a try. We can see there's a Next button, but we're getting an error that there's no next step as a function. So if we go back to the example and we look at the type script tab, we can see there's some code in order to manage the steps. So let's copy this, and then let's paste it in our quiz component. So below ngOnInIt I'll paste this.
Then back in the example, let's look at the HTML further. We can see that there's a couple bindings under expanded and opened. And if we scroll down we can see that the step count increments for both with each new panel. So let's copy these two bindings and paste them into our md-expansion-panel. We'll paste it in beside the ngfor, and then we'll modify our ngfor to get access to the index which we'll use to increment through the steps.
So we'll add a semicolon, then let i = index. The we'll set step to i, and pass in i into the setStep function. Let's save that and give it a try. Now if we hit Next, we can see our second question is loaded. The next thing we need to do is use some form options for all the answer options we have.
So let's go over to angular materials and look at the components under the form controls. And here we can see Radio button which allows us to choose one of several options. Let's take a look at the API and copy the module. The let's add that to our module list. So I've added that to the import list for angular material, and then let's add it to the imports list on line 57.
Then let's go back to our HTML and since this line is big I'll set it to multi-line. And now let's look at the example code in angular material. So look at the source, and then we'll grab the code above the div. And let's paste that in in place of our question.correctAnswer.
Next since we don't have a list of answers we'll need to create one because that's an easy way to randomize the order so that it's not obvious which one's the right answer versus the incorrect answers. If we maintain the order that our answers are currently in now, the correct answer would always be first and there wouldn't much challenge to the quiz. So let's create an answer list by going to our playQuizComponent, and we will create an answers list for every single question.
In order to do that, we'll need to use a forEach on the questions list. So we'll use this.questions.forEach and change this to q, and now we're iterating through every question in the questions list. Then let's create a new answers list. So we'll create the answers list dynamically, and we'll add them in the same order that they're currently in, with correct answer being first followed by answer one, two, and three, and then afterwords we'll look into shuffling the answers list.
For the first element I'll add as q.correctAnswer followed by q.answer1 q.answer2 and q.answer3. And I can probably put this all on one line. The next thing we need to do is shuffle our answers since we have them currently in a list and we'd like the correct answer to have a random position in that list. So I've done a quick search on stackoverflow for the best array shuffle solution and from the top rated solution I'll be using the ES6 short version so I'll just copy the shuffle function and paste it at the bottom below my class.
Next I'll call shuffle and pass in question .answers. Finally let's go to our HTML file and let's change the value to answer and the text I pulled to be displaying to answer as well on line 14. Let's save that and give it a try. And we can see that our different answers are showing up correctly and if we hit refresh they should have a different order.
And as we can see the order is different as the app reloads. Though notice that if I select one answer, it effects the answer I selected on a previous question or the next question. The reason for that is in our HTML file, our ng model is set to a global called favoriteSeason from the example earlier.
We need to set that to question.selectedAnswer. That way we have a selected answer per question. Let's give that another try, so I'll select yes, and you can see that it's left the second question alone. And if I select another answer on the second question, it hasn't effected the first answer selection. Next let's stack all these answers vertically. So I'll change the class on line 13 for our radio button element to fullwidth, I'll also get rid of the class on line 12 since we don't have that.
Let's save that and take a look. And so now they're stacked vertically, and that's looking a bit cleaner. Next let's look at how to finally calculate the users score. We'll being by adding a Finish button at the bottom that will give us the score. So let's scroll down and after line 23 in out md-card-actions let's add a button. I'll just copy the button from line 18.
And the click binding, we'll call a function that we'll create called Finish.
- Creating and configuring the Angular project
- Creating forms with Angular
- Creating get and post routes in an ASP.NET Core controller
- Updating Angular service to post to API
- Persistence with Entity Framework
- Displaying and editing data in Angular with ASP.NET Core
- Navigating to different views in Angular
- Associations between entities with Angular and Entity Framework
- Setting up Identity Framework