Your Ad Here
Google

Tuesday, March 06, 2007

 

Simple ajax Quiz using Atlas

Introduction


As my first article on Code Project, I'd like to talk about a little proof of
concept I created around AJAX/Atlas techologies. When I saw href="http://channel9.msdn.com/ShowPost.aspx?PostID=204588">this screencast,
I thought it could be easily used for a simple AJAX quiz system, so here I
am :) Code is really simple and there's no error management, but maybe later
I'll update this code to create a more complete solution.

Pre required


To be able to use this code, you'll have to do the following :



SQL code


AjaxQuiz database contains three tables : t_Questions, t_Answers and
t_UserAnswers. There's also a single stored procedure called by our Web Method
to process the data :

CREATE PROCEDURE dbo.ProcessNextQuestion

(

@intQuestionID int = 0,

@intAnswerID int = 0,

@intUserID int = 0

)

AS

IF @intQuestionID > 0 AND @intAnswerID > 0 AND @intUserID > 0

BEGIN

INSERT INTO t_UserAnswers(UserID, AnswerID, QuestionID)

VALUES(@intUserID, @intAnswerID, @intQuestionID)

END

SELECT TOP 1 QuestionID, QuestionText

FROM t_Questions

WHERE QuestionID > @intQuestionID


As you can see, if sql input parameter aren't null, the stored procedure
inserts results of the previous question in the database. Then it returns text
and id for the next question. As QuestionID is autoincremented, the next
question is always returned, but it supposes that ids correspond to the desired
question order.


Markup code


There's only one web page in this small project : Default.aspx. First, I
have to describe the ScriptManager element :

<atlas:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true">

<Scripts>

<atlas:ScriptReference Path="AjaxQuiz.js" />

</Scripts>

<Services>

<atlas:ServiceReference Path="QuestionService.asmx" />

</Services>

</atlas:ScriptManager>


That's where Atlas is really nice : we just need to reference our JS client
script and our Web Service, and Atlas will do the rest (client code to consume
the WS, etc.) In our JS code, we will also be able to create instances of
classes defined in server code. Atlas is doing the correspondance for us.

<div id="StartForm">

<input id="btnStart" type="button" value="Start the Ajax Quiz !"

onclick="Callback()" />

</div>

<div id="QuizForm" style="display: none;">

<div id="QuestionText"></div><br />

<input type="radio" id="YesAnswer" name="Answer" checked="checked" />&nbsp;Yes

<input type="radio" id="NoAnswer" name="Answer" />&nbsp;No

<input type="radio" id="DontKnowAnswer" name="Answer" />&nbsp;?<br /><br />

<input id="btnCallBack" type="button" value="Next" onclick="Callback()" />&nbsp;

<img id="imgUpdate" src="Images/spinner.gif" alt="Updating data" style="display: none;" />

<input id="QuestionID" type="hidden" value="0" />

</div>

<div id="EndForm" style="display: none;">

Thank you, this quiz is now finished !&nbsp;

</div>


Then, we add some HTML controls to create our form. First of all, there are
three DIVs : one that shows on startup, another for quiz questions, and a last
one displayed when the quiz is finished.
The main section, QuizForm, contains
three radio buttons for answers, a button to call our Web Service and an image
shown during AJAX calls.


Web Service


The Web Service contains only one WebMethod, StoreAnswer :

/// <summary>

/// That's the only Web Method used. It both stores the answer to the current question

/// and sends data for the next one.

/// </summary>

/// <param name="previousQuestion">A Question object containing user answer.</param>

/// <returns>Returns a Question object containing data for the next question (question ID, question Text)</returns>

[WebMethod]

public Question StoreAnswer(Question previousQuestion)

{

// We initialize a Question object to null. It will be our return value.

Question nextQuestion = null;

if (previousQuestion == null)

{

// If no previous question is submitted, we create a new one with default values.

previousQuestion = new Question(0, "", 0);

}

// SQL connection initialization (connection string is in web.config file)

using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["AjaxQuizConnectionString"].ConnectionString))

{

try

{

// Then we call our stored procedure

SqlCommand cmd = new SqlCommand("dbo.ProcessNextQuestion", cn);

cmd.CommandType = CommandType.StoredProcedure;

// First parameter for question ID.

SqlParameter parm = new SqlParameter("@intQuestionID", SqlDbType.Int);

parm.Value = previousQuestion.QuestionID;

parm.Direction = ParameterDirection.Input;

cmd.Parameters.Add(parm);

// Second parameter for answer ID.

SqlParameter parm2 = new SqlParameter("@intAnswerID", SqlDbType.Int);

parm2.Value = previousQuestion.AnswerID;

parm2.Direction = ParameterDirection.Input;

cmd.Parameters.Add(parm2);

// Third parameter for user ID.

SqlParameter parm3 = new SqlParameter("@intUserID", SqlDbType.Int);

parm3.Value = userID;

parm3.Direction = ParameterDirection.Input;

cmd.Parameters.Add(parm3);

// Opening sql connection

cn.Open();

using (SqlDataReader rd = cmd.ExecuteReader(CommandBehavior.CloseConnection))

{

while (rd.Read())

{

// We read data returned by our SP. It only returns one row.

nextQuestion = new Question(rd.GetInt32(0), rd.GetString(1));

}

}

// We make the web service sleep for one second, so that we can see the spinner image appear.

// This line should be removed if you seriously think about using this code ^^

Thread.Sleep(1000);

}

finally

{

// Important : we always have to close the sql connection

cn.Close();

}

}

return nextQuestion;

}


The WebMethod calls a stored procedure, then returns the next question as a
Question object. I made the thread sleep for one second, so that I can see the
spinner image.


Javascript Code


Javascript code might be the hardest part, because we have to reference all
our HTML objects and get/set their values. There's certainly a simpler way to do
the job, don't hesitate to propose something in comments. OnTimeout and OnError
functions aren't included but are present in the zip file.

// Called on 'Next' button click event.

function Callback()

{

// Gets a reference to the hidden field containing the question ID

var questionID = document.getElementById('QuestionID');

// Gets references to the 3 possible answers

var answer1 = document.getElementById('YesAnswer');

var answer2 = document.getElementById('NoAnswer');

var answer3 = document.getElementById('DontKnowAnswer');

// Initializes a variable to hold the user answer

var answerID = 0;

// Gets user answer

if(answer1.checked) answerID = 1;

if(answer2.checked) answerID = 2;

if(answer3.checked) answerID = 3;

// Creates a new Question object. Atlas makes the translation for us : JS and ASP.NET

// know exactly the same class !

var object = new Question();

object.QuestionID = questionID.value;

object.AnswerID = answerID;

// Displays a image during the AJAX call

DisplayUpdateImage(true);

// Ajax call. QuestionService is the Web Service we registered in Atlas ScriptManager

// and as you can see, we can directly call our WebMethod. Isn't it nice ? :)

// We also add 3 events : one when process is completed, another when a timeout occurs,

// and a last one if an error occurs.

QuestionService.StoreAnswer(object, OnComplete, OnTimeout, OnError);

}

 

// Called when Ajax request is done

function OnComplete(response)

{

// 3 references to our 3 DIVs

var StartForm = document.getElementById('StartForm');

var QuizForm = document.getElementById('QuizForm');

var EndForm = document.getElementById('EndForm');

// A reference to the hidden field used to hold the question ID

var questionID = document.getElementById('QuestionID');

// A reference to the DIV which will contain the next question text

var questionText = document.getElementById('QuestionText');

// If there is a next question

if(response != null)

{

StartForm.style.display = 'none';

EndForm.style.display = 'none';

QuizForm.style.display = 'block';

questionID.value = response.QuestionID;

questionText.innerHTML = response.QuestionText;

}

// If there's no more questions, we display the EndForm div.

else

{

EndForm.style.display = 'block';

QuizForm.style.display = 'none';

}

// We hide the updating image

DisplayUpdateImage(false);

}

Conclusion


And that's all ! When the user clicks on the start button, he sees the first
question appear. No result is inserted in the database, because the input
Question object is null. Then, when he clicks on the next button, results are
stored in the database and our stored procedure sends the next question ... and
so on until the SP doesn't return anything. In that case, finally, the user sees
the ending message.


OK, I know this is a quite simple article but I think it can illustrate what
you can do with Atlas and how easy it is. I hope my english isn't too bad too
:)



Labels:


Comments: Post a Comment



<< Home

This page is powered by Blogger. Isn't yours?