locked
Displaying next question of the same exam after clicking on Next button RRS feed

  • Question

  • User1255309776 posted

    Hi guys, 

    Maybe some of you already heard, I'm creating an online quiz application where many users will take the same exam with multiple choice questions. Unlike many online quiz applications, in my app user will not see all the questions of the exam in one view, instead, after answering one question, they'll proceed to the next one after clicking on next button. (like in millionaire show). In one view, it is easy to get all the questions and their answers of the same exam by looping them in the view. But in this case, I face problem.

    First, let me introduce my basic models again.

     public class Exam
        {
            public Exam()
            {
                Questions = new HashSet<Question>();
            }
    
            public int Id { get; set; }
            public string Name { get; set; } 
            public DateTime ExamDate { get; set; }
            public ICollection<Question> Questions { get; set; }
        }

    Question

    public class Question
        {
            public Question()
            {
                Answers = new HashSet<Answer>();
            }
    
            public int Id { get; set; }
            public string Description { get; set; }
            public int ExamId { get; set; }
            public int Score { get; set; }
            public ICollection<Answer> Answers { get; set; }
            public ICollection<UserQuestion> UserQuestions { get; set; }
    
        }    

    Answer

    public class Answer
        {
            public int Id { get; set; }
            public string Description { get; set; }
            public int QuestionId { get; set; }
            public bool Correct { get; set; } 
        }

    My HomeController

      public class HomeController : Controller
        {
            private readonly IntellectDbContext _intellectDbContext;
            static int exam_id = 0;
    
            public HomeController(IntellectDbContext intellectDbContext)
            {
                _intellectDbContext = intellectDbContext;
            }
             
            public IActionResult Index()
            {
                return View();
            }
    
          
            [HttpGet]
            public async Task<IActionResult> Test(int Id)
            {
                exam_id = Id;
                AdminViewModel admodel = new AdminViewModel();
                admodel.Questions = await _intellectDbContext.Questions.Where(q => q.ExamId == Id).ToListAsync();
                admodel.Question = await _intellectDbContext.Questions.FirstOrDefaultAsync();
                return View(admodel);
    
            }
    
            public async Task<IActionResult> Question(int Id)
            {
                AdminViewModel admodel = new AdminViewModel();
                admodel.Question = await _intellectDbContext.Questions.Where(x => x.Id == Id).SingleOrDefaultAsync();
                admodel.Answers = await _intellectDbContext.Answers.Where(y => y.QuestionId == Id).ToListAsync();
                ViewBag.Equestions = await _intellectDbContext.Questions.Where(q => q.ExamId == exam_id).ToListAsync();
                return View(admodel);
            }
    
           

    Test view from where exam questions starts:

    @model Intellect.Models.ViewModels.AdminViewModel
    
    @{
        Layout = "AdminLayout";
    }
    
    <div class="questioncontainer">
        <div class="row">
            <div class="col-lg-3"></div>
            <div class="col-lg-6 col-sm-12">
               
            </div>
            <div class="col-lg-3"></div>
        </div>
        <div class="row">
            <div class="col-lg-3 col-sm-0"></div>
          
            <div class="col-lg-3"></div>
        </div>
        <div class="row">
            <div class="col-lg-6 col-sm-4">
    
            </div>
            <div class="col-lg-3 col-sm-4">
    
            </div>
            <div class="col-lg-3 col-sm-4">
                <div class="nextbtn">
                    <button class="next" name="qbutton" value="tap">
                        <a asp-action="Question" asp-controller="Home" asp-route-id="@Model.Question.Id">Start</a> 
                    </button>
                </div>
            </div>
        </div>
        </div>
    

    Question view (in Shared folder) where questions must change on each click on Next button getting their Id

    @model Intellect.Models.ViewModels.AdminViewModel
    
    @{
        Layout = "AdminLayout";
    }
    
    <div class="questioncontainer">
        <div class="row">
            <div class="col-lg-3"></div>
            <div class="col-lg-6 col-sm-12">      
               <div class="question">@Model.Question.Description </div>
            </div>
            <div class="col-lg-3"></div>
        </div>
        <div class="row">
            <div class="col-lg-3 col-sm-0"></div>
            @{
                int n = 0;
            }
            @foreach (Answer item in Model.Answers)
            {
                if (n == 0 || n == 2)
                {
                    @: <div class="col-lg-3 col-sm-12">
                        @: <div class="firstpart">
                        }
    
                        <button class="answer">@item.Description</button>
                        <br>
                        if (n == 1 || n == 3)
                        {
                        @:</div>
                    @:</div>
                }
    
                n++;
            }
            <div class="col-lg-3"></div>
        </div>
        <div class="row">
            <div class="col-lg-6 col-sm-4">
    
            </div>
            <div class="col-lg-3 col-sm-4">
    
            </div>
            <div class="col-lg-3 col-sm-4">
                <div class="nextbtn"> 
                    <button id="move" class="next" name="qbutton" value="tap">
                        @foreach (var element in ViewBag.Equestions)
                        {
                            <a asp-action="Question" asp-controller="Home" asp-route-id="@element.Id">Next</a>
                        }
                    </button>         
                </div>
            </div>
        </div>
    </div>

    As you see, I used Viewbag to get link to all the questions as my Model takes only one by question Id, but in foreach, it gets all links in one view whereas I need only one of them to be seen: the one which will proceed test taker to the next question. Probably, there is  a way using JavaScript to hide all buttons (links) except the one which gets the View to the next question.  But I'd like to implement it using C#, if possible. Note that don't think about incrementing Id by 1 (+=1) logic as the particular exams' questions Id's aren't in sequence. 

    Currently, my question View is like this:

    https://prnt.sc/qq9800

    As you see, I need Next button to be one in each question view, and to be able to proceed to the next on the click.  Please, ignore minor syntax and note that I already configured my startup, program, and have database working, no problem at all with them.

    Hope, one of you will find solution to this problem.

    Thanks in advance!

    Monday, January 20, 2020 1:28 PM

Answers

  • User-854763662 posted

    Hi FaridGN ,

    For your demand , you could use a count variable and the NextQuestion property to get the remaining question and the next question .Refer to the working demo as shown:

    Model

    public class AdminViewModel
    {
            public List<Question> Questions { get; set; }
            public List<Answer> Answers { get; set; }
            public Question CurrentQuestion { get; set; }
            public Question NextQuestion { get; set; }
    }

    HomeController

    public class HomeController : Controller
        {
            private readonly IntellectDbContext _intellectDbContext;
            static int exam_id = 0;
            static List<Question> RemainQuestions= new List<Question>();
    
            public HomeController( IntellectDbContext intellectDbContext)
            {
                _intellectDbContext = intellectDbContext;
            }
    
            [HttpGet]
            public async Task<IActionResult> Test(int Id=Id)
            {
                exam_id = Id;
                AdminViewModel admodel = new AdminViewModel();
                admodel.Questions = await _intellectDbContext.Question.Include(q=>q.Answers).Where(q => q.ExamId == Id).ToListAsync();
                admodel.CurrentQuestion = await _intellectDbContext.Question.Where(q => q.ExamId == Id).FirstOrDefaultAsync();
    
                RemainQuestions = admodel.Questions;
                return View(admodel);
    
            }
    
            public async Task<IActionResult> Question(int Id,int count)
            {
                AdminViewModel admodel = new AdminViewModel();
                admodel.CurrentQuestion = await _intellectDbContext.Question.Where(x => x.Id == Id).SingleOrDefaultAsync();
                admodel.Answers = await _intellectDbContext.Answer.Where(y => y.QuestionId == Id).ToListAsync();
                admodel.Questions = await _intellectDbContext.Question.Where(q => q.ExamId == exam_id).ToListAsync();
                if (count > 1)
                {
                    var question = RemainQuestions.Single(r => r.Id == admodel.CurrentQuestion.Id);
    
                    RemainQuestions.Remove(question);
                    admodel.NextQuestion = RemainQuestions[0];
                    count -= 1;
                }
                else
                {
                    admodel.NextQuestion = RemainQuestions[0];
                    count -= 1;
                }
                ViewBag.Equestions = count;
                return View(admodel);
            }
    
        }

    Test View

    <button class="next" name="qbutton" value="tap">
        <a asp-action="Question" asp-controller="Home" asp-route-id="@Model.CurrentQuestion.Id" asp-route-count="@Model.Questions.Count">Start</a>
    </button>

    Question View

    <div class="col-lg-3 col-sm-4">
                <div class="nextbtn">
                    @if (ViewBag.Equestions > 0)
                    {
                        <button id="move" class="next" name="qbutton" value="tap">
    
                            <a asp-action="Question" asp-controller="Home" asp-route-id="@Model.NextQuestion.Id" asp-route-count="@ViewBag.Equestions">Next</a>
    
                        </button>
                    }
    
                </div>
            </div>

    Best Regards,

    Sherry

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, January 22, 2020 10:11 AM

All replies

  • User-854763662 posted

    Hi FaridGN ,

    For your demand , you could use a count variable and the NextQuestion property to get the remaining question and the next question .Refer to the working demo as shown:

    Model

    public class AdminViewModel
    {
            public List<Question> Questions { get; set; }
            public List<Answer> Answers { get; set; }
            public Question CurrentQuestion { get; set; }
            public Question NextQuestion { get; set; }
    }

    HomeController

    public class HomeController : Controller
        {
            private readonly IntellectDbContext _intellectDbContext;
            static int exam_id = 0;
            static List<Question> RemainQuestions= new List<Question>();
    
            public HomeController( IntellectDbContext intellectDbContext)
            {
                _intellectDbContext = intellectDbContext;
            }
    
            [HttpGet]
            public async Task<IActionResult> Test(int Id=Id)
            {
                exam_id = Id;
                AdminViewModel admodel = new AdminViewModel();
                admodel.Questions = await _intellectDbContext.Question.Include(q=>q.Answers).Where(q => q.ExamId == Id).ToListAsync();
                admodel.CurrentQuestion = await _intellectDbContext.Question.Where(q => q.ExamId == Id).FirstOrDefaultAsync();
    
                RemainQuestions = admodel.Questions;
                return View(admodel);
    
            }
    
            public async Task<IActionResult> Question(int Id,int count)
            {
                AdminViewModel admodel = new AdminViewModel();
                admodel.CurrentQuestion = await _intellectDbContext.Question.Where(x => x.Id == Id).SingleOrDefaultAsync();
                admodel.Answers = await _intellectDbContext.Answer.Where(y => y.QuestionId == Id).ToListAsync();
                admodel.Questions = await _intellectDbContext.Question.Where(q => q.ExamId == exam_id).ToListAsync();
                if (count > 1)
                {
                    var question = RemainQuestions.Single(r => r.Id == admodel.CurrentQuestion.Id);
    
                    RemainQuestions.Remove(question);
                    admodel.NextQuestion = RemainQuestions[0];
                    count -= 1;
                }
                else
                {
                    admodel.NextQuestion = RemainQuestions[0];
                    count -= 1;
                }
                ViewBag.Equestions = count;
                return View(admodel);
            }
    
        }

    Test View

    <button class="next" name="qbutton" value="tap">
        <a asp-action="Question" asp-controller="Home" asp-route-id="@Model.CurrentQuestion.Id" asp-route-count="@Model.Questions.Count">Start</a>
    </button>

    Question View

    <div class="col-lg-3 col-sm-4">
                <div class="nextbtn">
                    @if (ViewBag.Equestions > 0)
                    {
                        <button id="move" class="next" name="qbutton" value="tap">
    
                            <a asp-action="Question" asp-controller="Home" asp-route-id="@Model.NextQuestion.Id" asp-route-count="@ViewBag.Equestions">Next</a>
    
                        </button>
                    }
    
                </div>
            </div>

    Best Regards,

    Sherry

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, January 22, 2020 10:11 AM
  • User1255309776 posted

    Thank you Sherry for this answer, this code worked out to get Next question on each click, and in the last step, I want to change Next button with Finish which will implement post method that will take all correct answers clicked by the user and sum them to calculate result.

    This is how I did it and didn't get update in the database for user's Result.

    In the homeController I 've added : (with yellow)

     public class HomeController : Controller
        {
            private readonly IntellectDbContext _intellectDbContext;
            private readonly UserManager<ExamUser> _userManager;
            private readonly SignInManager<ExamUser> _signInManager;
            static int exam_id = 0;
            static List<Question> RemainedQuestions = new List<Question>();
            static List<Answer> SelectedAnswers = new List<Answer>();
    
            public HomeController(IntellectDbContext intellectDbContext, UserManager<ExamUser> userManager, SignInManager<ExamUser> signInManager)
            {
                _intellectDbContext = intellectDbContext;
                _userManager = userManager;
                _signInManager = signInManager;
            }
             
            public IActionResult Index()
            {
                return View();
            }
    
            [HttpGet]
            public async Task<IActionResult> Test(int Id)
            {
                exam_id = Id;
                AdminViewModel admodel = new AdminViewModel();
                admodel.Equestions = await _intellectDbContext.Questions.Include(q => q.Answers).Where(q => q.ExamId == Id).ToListAsync();
                admodel.CurrentQuestion = await _intellectDbContext.Questions.Where(q => q.ExamId == Id).FirstOrDefaultAsync();
                RemainedQuestions = admodel.Equestions;
                return View(admodel);
            }
    
            public async Task<IActionResult> Question(int Id, int count, Answer choice)
            {
                AdminViewModel admodel = new AdminViewModel();
                admodel.CurrentQuestion = await _intellectDbContext.Questions.Where(x => x.Id == Id).SingleOrDefaultAsync();
                admodel.Answers = await _intellectDbContext.Answers.Where(y => y.QuestionId == Id).ToListAsync();
                admodel.Equestions = await _intellectDbContext.Questions.Where(q => q.ExamId == exam_id).ToListAsync();

    int correctId = _intellectDbContext.Answers.Where(a => a.QuestionId == Id && a.Correct == true).SingleOrDefault().Id;
    if(choice.Id == correctId)
    {
    SelectedAnswers.Add(choice);
    } if(count > 1) { var question = RemainedQuestions.Single(r => r.Id == admodel.CurrentQuestion.Id); RemainedQuestions.Remove(question); admodel.NextQuestion = RemainedQuestions[0]; count -= 1; } else { admodel.NextQuestion = RemainedQuestions[0]; count -= 1; } ViewBag.Equestions = count; return View(admodel); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Finish() { if (_signInManager.IsSignedIn(User)) { ExamUser user = await _userManager.GetUserAsync(HttpContext.User); // examTaker.TestTaker.Result = await _intellectDbContext.TestTakers.Include(t => t.UserQuestions).Select(u => new Question().Score).SingleOrDefaultAsync(); foreach(Answer item in SelectedAnswers) { user.TestTaker.Result += 1; await _intellectDbContext.SaveChangesAsync(); } return RedirectToAction(nameof(Index)); } else { return RedirectToAction(nameof(AccountController.Login), "Account"); } }

    Here, ExamUser is user that inherited from IdentityUser, and TestTaker is  mediator class that user holds (they have 1 to 1 relationship). The only difference is that all registered people are users, but those that take exam are also testTakers.

    Question view:

    @model Intellect.Models.ViewModels.AdminViewModel
    
    @{
        Layout = "AdminLayout";
    }
    
    <div class="questioncontainer">
        <div class="row">
            <div class="col-lg-3"></div>
            <div class="col-lg-6 col-sm-12">      
               <div class="question">@Model.CurrentQuestion.Description </div>
            </div>
            <div class="col-lg-3"></div>
        </div>
        <div class="row">
            <div class="col-lg-3 col-sm-0"></div>
            @{
                int n = 0;
            }
            @foreach (Answer item in Model.Answers)
            {
                if (n == 0 || n == 2)
                {
                    @: <div class="col-lg-3 col-sm-12">
                        @: <div class="firstpart">
                        }
    
                        <button class="answer" name="choice" value="@item.Id">@item.Description</button>
                        <br>
                        if (n == 1 || n == 3)
                        {
                        @:</div>
                    @:</div>
                }
    
                n++;
            }
            <div class="col-lg-3"></div>
        </div>
        <div class="row">
            <div class="col-lg-6 col-sm-4">
    
            </div>
            <div class="col-lg-3 col-sm-4">
              
            </div>
            <div class="col-lg-3 col-sm-4">
                <div class="nextbtn">
                    @if (ViewBag.Equestions == 0)
                    {
                        <form method="post" asp-action="Finish" asp-controller="Home">
                            <button id="move" class="next" name="qbutton">Tesdiq</button>
                        </form>
    
                    }
                    else
                    {
                        <button id="move" class="next" name="qbutton">
                            <a asp-action="Question" asp-controller="Home" asp-route-id="@Model.NextQuestion.Id" asp-route-count="@ViewBag.Equestions">Next</a>
                        </button>
                    }
    
                </div>
            </div>
        </div>
    </div>

    While testing, after taking exam I don't get any error, but the result property of TestTaker is not changed (remained 0) though I know that I chose correct answers. 

    Maybe on click it doesn't recognized the value(id) of answer button. Also, I'd like to add that after registering, user also enrolls to Exam who becomes TestTaker, and by default, I assigned 0 for TestTaker's Result.

     public class TestTaker
        {
            public int Id { get; set; }
            [Required]
            public string Name { get; set; }
            public string Phone { get; set; }
            public string Education { get; set; }
            public string Job { get; set; }
            public DateTime Birth { get; set; }
            public ExamUser ExamUser { get; set; }
            public int Result { get; set; }
            public ICollection<UserQuestion> UserQuestions { get; set; }
        }
      [HttpPost]
            [ValidateAntiForgeryToken]
            public async Task<IActionResult> Enroll(TestTaker testTaker, DateTime birthdate)
            {
                if (ModelState.IsValid)
                {
                    testTaker.Birth = birthdate;
                    testTaker.Result = 0;
                    _intellectDbContext.TestTakers.Add(testTaker);
                    await _intellectDbContext.SaveChangesAsync();
                    return RedirectToAction(nameof(Proceed));
                }
                else
                {
                    ModelState.AddModelError("", "Kecmedi");
                    return View();
                }
            }

    Hope this info helps too.

    Please, help.

    Thursday, January 23, 2020 9:11 PM
  • User-854763662 posted

    Hi FaridGN ,

    in the last step, I want to change Next button with Finish which will implement post method that will take all correct answers clicked by the user and sum them to calculate result.

    It is a new issue , please post a new thread for help .

    If my workaround for the original issue works , could you mark the  my reply as answer ?

    Best Regards,

    Sherry

    Monday, January 27, 2020 1:46 AM
  • User1255309776 posted

    Ok, but please, take a look at my new thread (last step)

    Monday, January 27, 2020 5:31 PM