Introduction
As my first article on Code Project, I'd like to talk about a little proof of concept I created around AJAX/Atlas technologies. When I saw this screencast, I thought it could be easily used for a simple AJAX quiz system, so here I am :) The Code is really simple and there's no error management, but maybe later, I'll update this code to create a more complete solution.
Prerequisites
To be able to use this code, you'll have to do the following:
- Install Atlas.
- Create an SQL database called AjaxQuiz and execute the SQL script which comes with the source code.
- Add a reference to the Atlas DLL (usually located in C:\Program Files\Microsoft ASP.NET\Atlas\v2.0.50727\Atlas).
- Modify the
AjaxQuiz
connection string in web.config.
SQL code
The 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 the SQL input parameter isn't null, the Stored Procedure inserts results of the previous question in the database. Then, it returns the text and ID for the next question. As the QuestionID
is auto-incremented, 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 (the client code to consume the WS, etc.) In our JS code, we will also be able to create instances of classes defined in the server code. Atlas will do the correspondence 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" /> Yes
<input type="radio" id="NoAnswer" name="Answer" /> No
<input type="radio" id="DontKnowAnswer" name="Answer" /> ?<br /><br />
<input id="btnCallBack" type="button" value="Next" onclick="Callback()" />
<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 !
</div>
Then, we add some HTML controls to create our form. First of all, there are three DIV
s: 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
:
[WebMethod]
public Question StoreAnswer(Question previousQuestion)
{
Question nextQuestion = null;
if (previousQuestion == null)
{
previousQuestion = new Question(0, "", 0);
}
using (SqlConnection cn = new SqlConnection(
ConfigurationManager.ConnectionStrings["AjaxQuizConnectionString"].ConnectionString))
{
try
{
SqlCommand cmd = new SqlCommand("dbo.ProcessNextQuestion", cn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter parm = new SqlParameter("@intQuestionID", SqlDbType.Int);
parm.Value = previousQuestion.QuestionID;
parm.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm);
SqlParameter parm2 = new SqlParameter("@intAnswerID", SqlDbType.Int);
parm2.Value = previousQuestion.AnswerID;
parm2.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm2);
SqlParameter parm3 = new SqlParameter("@intUserID", SqlDbType.Int);
parm3.Value = userID;
parm3.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm3);
cn.Open();
using (SqlDataReader rd =
cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while (rd.Read())
{
nextQuestion = new Question(rd.GetInt32(0), rd.GetString(1));
}
}
Thread.Sleep(1000);
}
finally
{
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
The JavaScript code might be the hardest part, because we have to reference all our HTML objects and get/set their values. There must certainly be a simpler way to do the job, so don't hesitate to propose something in the comments section. The OnTimeout
and OnError
functions aren't included but are present in the Zip file.
function Callback()
{
var questionID = document.getElementById('QuestionID');
var answer1 = document.getElementById('YesAnswer');
var answer2 = document.getElementById('NoAnswer');
var answer3 = document.getElementById('DontKnowAnswer');
var answerID = 0;
if(answer1.checked) answerID = 1;
if(answer2.checked) answerID = 2;
if(answer3.checked) answerID = 3;
var object = new Question();
object.QuestionID = questionID.value;
object.AnswerID = answerID;
DisplayUpdateImage(true);
QuestionService.StoreAnswer(object, OnComplete, OnTimeout, OnError);
}
function OnComplete(response)
{
var StartForm = document.getElementById('StartForm');
var QuizForm = document.getElementById('QuizForm');
var EndForm = document.getElementById('EndForm');
var questionID = document.getElementById('QuestionID');
var questionText = document.getElementById('QuestionText');
if(response != null)
{
StartForm.style.display = 'none';
EndForm.style.display = 'none';
QuizForm.style.display = 'block';
questionID.value = response.QuestionID;
questionText.innerHTML = response.QuestionText;
}
else
{
EndForm.style.display = 'block';
QuizForm.style.display = 'none';
}
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 Stored Procedure 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 :)