پس از آشنایی با تکنیک Client Callbacks در ASP.NET 2.0، در این مقاله قصد دارم تا به نحوه ی ثبت رکورد در بانک اطلاعاتی بدون Postback صفحه به شکلی خاص بپردازم.
توجه مهم:
تعدادی از دوستان در مورد تفاوت یا مزیت استفاده از این روش با ابزارها و کتابخانه هایی که بدین منظور ایجاد شده است پرسیده بودند.
باید گفت که نمیشه تفاوت خاصی رو بین این روش و ابزارهایی که به منظور تسهیل در استفاده از AJAX عرضه شدند قائل شد.
اگر تفاوتی وجود داشته باشد در جزئیات هست نه کلیات!
مثالی که در این مقاله بدان پرداخته میشه، تا حدودی این جزئیات رو مشخص می کنه.
جاوا اسکریپت، عنصر اصلی در استفاده از AJAX هست. هر چقدر که "ابزارها" و "توسعه های فراوان" نیز بدین منظور ارائه شوند، باز هم نیاز مبرم به جاوا اسکریپت برای سفارشی کردن برنامه وجود دارد.
باز هم تاکید می کنم که مثالی که در این مقاله ارائه و تحلیل میشه، استفاده از جاوا اسکریپت رو به عنوان یک عنصر اصلی در کنار خود نشان می دهد.
تا حد امکان از "اضافه نویسی" و "پیچیدگی های متداول!" پرهیز شده و مقاله و برنامه به صورت خوانا، روان و ساده ارائه شده.
صورت مسئله:
در صفحه ای به کاربر اجازه ی وارد کردن اطلاعاتی شامل: "نام"، "نام خانوادگی"، "شماره تماس" و "محل سکونت" داده می شود.
قصد بر آن است که این اطلاعات
بدون PostBack صفحه در دیتابیس ذخیره شوند و داده های وارد شده به شکل جدول، در صفحه نمایش داده شوند.
روش خاص اضافه کردن اطلاعات که در ابتدا بدان اشاره شد آن است که تنها اطلاعاتی در جدول نمایش داده می شوند که کاربر جاری آنها را وارد کرده است. به عنوان مثال، اگر 2 کاربر در 2 مکان مختلف اقدام به وارد کردن اطلاعات کنند، هر کاربر تنها رکوردهایی را خواهد دید که خود وارد کرده است.
روشی که کاربر بتواند تمامی اطلاعات را ببیند در مقاله ای جدا بدان خواهم پرداخت.
همان طور که در مقاله ی قبل گفته شد، پیش از هر چیز نیاز داریم تا: "تابع جاوا اسکریپتی ایجاد شود که وظیفه ی ارسال درخواست به سرور و دریافت نتیجه را انجام دهد."
کد این تابع به شکل زیر ایجاد میشد:
کد:
protected void Page_Load(object sender, EventArgs e)
{
string CR_REF = ClientScript.GetCallbackEventReference(this, "myValue", "ReceiveDataFromServer", "validateF");
if (!ClientScript.IsClientScriptBlockRegistered("DoServerAction"))
{
string SERVER_SCRIPT = @" function DoServerAction(myValue,validateF) { " + CR_REF + "}";
ClientScript.RegisterClientScriptBlock(this.GetType(), "DoServerAction", SERVER_SCRIPT, true);
}
}
باز هم از مطالب مقاله ی قبل به یاد دارید که: "متد RaiseCallbackEvent در زمان ایجاد Callback (فراخوانی تابع DoServerAction) فراخوانی می شود و مقداری که از سمت کلاینت به سرور پاس داده می شود، در پارامتر eventArgument این متد قرار می گیرد."
پس متد RaiseCallbackEvent بهترین مکان برای اضافه کردن رکورد در دیتابیس است!
پس ابتدا نیاز به روالی داریم که وظیفه ی ثبت رکورد را برای ما انجام دهد.
در این مقاله به خاطر سهولت کار از دیتابیس Access استفاده کردم. روال مورد نیاز را به شکل زیر ایجاد می کنیم:
کد:
private string InsertRecord(string DataToInsert)
{
OleDbConnection Cnn = new OleDbConnection("Provider=Microsoft.JET.OLEDB.4.0;Data Source=" + Server.MapPath(".") + "\\Data\\DB_Callback.mdb;");
OleDbCommand Cmd = new OleDbCommand("INSERT INTO tbl_Callback(strName,strFamily,strPhone,strLocation) VALUES(?,?,?,?)", Cnn);
string[] arrClientValues = DataToInsert.Split("|".ToCharArray());
Cmd.Parameters.AddWithValue("@paramName", arrClientValues[0]);
Cmd.Parameters.AddWithValue("@paramFamily", arrClientValues[1]);
Cmd.Parameters.AddWithValue("@paramPhone", arrClientValues[2]);
Cmd.Parameters.AddWithValue("@paramLocation", arrClientValues[3]);
try
{
Cnn.Open();
Cmd.ExecuteNonQuery();
}
catch (OleDbException ex)
{
return "0";
}
catch (Exception ex)
{
return "0";
}
finally
{
Cnn.Close();
if (Cmd != null) Cmd.Dispose();
}
return "1";
}
تشریح عملکرد تابع فوق:
همان طور که ملاحظه می کنید، تابع فوق، مقداری از نوع رشته ای را که حاوی داده های مورد نظر برای درج در دیتابیس است به عنوان پارامتر ورودی دریافت می کند.
همچنین برای آگاهی از صحت عملکرد تابع در درج رکورد، این تابع مقدار "یک" یا "صفر" را که به ترتیب بیانگر "موفقیت" یا "عدم موفقیت" تابع در ثبت رکورد است برگشت می دهد. به این مقدار برگشتی در سمت کلاینت نیاز مبرم داریم...
نکته ای که احتمالا متوجه آن شده اید این است که ما چهار فیلد داریم اما تابع "InsertRecord" تنها یک مقدار را به عنوان پارامتر ورودی میگیرد!
دلیل این امر آن است که تکنیک Client Callbacks در ASP.NET 2.0 به ما تنها اجازه ی ارسال یک مقدار را در یک لحظه به سرور می دهد و این مقدار در پارامتر اول متد DoServerAction قرار می گیرد.
پس این مقادیر را در سمت کلاینت به هم متصل، و به منظور تشخیص هر مقدار، مقادیر را با کاراکتر "|" از یکدیگر جدا می کنیم. این مقادیر در سمت سرور (تابع InsertRecord) به وسیله ی متد "Split" از هم جدا می شوند.
توجه داشته باشید که کاراکتر "|" یک کاراکتر دلخواه است. معمولا باید کاراکتری را در نظر بگیرید که مطمئن هستید کاربر آن کاراکتر را به عنوان داده های ورودی وارد نمی کند. در غیر اینصورت برنامه در تشخیص مقادیر دچار مشکل خواهد شد.
پس از آشنایی با نحوه ی عملکرد تابع InsertRecord و پیاده سازی آن، باید آن را در مکانی مناسب فراخوانی کرد.
همان طور که پیشتر ذکر شد، متد "RaiseCallbackEvent" بهترین مکان برای این فراخوانی است!
کد:
protected string strReturn;
protected string insertResult;
public void RaiseCallbackEvent(string eventArgument)
{
if (!String.IsNullOrEmpty(eventArgument))
{
strReturn = eventArgument;
insertResult = InsertRecord(strReturn);
}
}
مقادیر وارد شده توسط کاربر، پس از آنکه در سمت کلاینت به هم متصل و به سرور ارسال شدند، در پارامتر "eventArgument" قرار می گیرند. متغیر "strReturn" این مقدار را گرفته و به تابع "InsertRecord" پاس می دهد.
متغیر "insertResult" نیز نتیجه ی برگشتی حاصل از عملکرد تابع را در خود نگاه می دارد.
تنها کار باقیمانده، برگشت نتیجه ی کار (متغیر insertResult) به کلاینت است و همان طور که می دانید، متد "GetCallbackResult" این کار را انجام می دهد.
کد:
public string GetCallbackResult()
{
string retResult = insertResult;
return retResult;
}
خسته نباشید! کار ما در سمت سرور تمام شد!
حال نوبت به قسمت اصلی کار(!) در سمت کلاینت رسیده است.
ابتدا توضیحی مختصر در مورد عناصر تشکیل دهنده ی صفحه:
سه TextBox، یک DropDownList (به منظور انتخاب محل سکونت) و یک Button.
یک المنت "DIV" با نام "Message" برای نمایش پیغام.
یک المنت "TABLE" با نام "tblResults" برای نمایش داده های وارد شده توسط کاربر. این TABLE در یک DIV قرار دارد که در ابتدا خاصیت "visibility" آن برابر با "hidden" است. پس TABLE در ابتدا نمایش داده نمی شود.
برای درک بهتر کدهای جاوا اسکریپت سمت کلاینت، به ترتیب روند اجرای کدها پیش می رویم...
کاربر پس از وارد کردن مقادیر در TextBox و انتخاب محل سکونت، بر روی دکمه کلیک می کند.
تابع جاوا اسکریپت "doAction" اجرا می شود.
بهتره نگاهی به تابع "doAction" بیندازیم.
کد:
کد:
function doAction()
{
document.getElementById("btnAddRecord").style.cursor = "wait";
document.body.style.cursor = "wait";
document.getElementById("Message").innerHTML = "...در حال ثبت اطلاعات. لطفا چند لحظه صبر کنید";
var ref_drpLocation = document.getElementById("drpLocation");
var selItem = ref_drpLocation.options[ref_drpLocation.selectedIndex].value;
var sentData = "";
sentData += document.getElementById("txtName").value;
sentData += "|" + document.getElementById("txtFamily").value;
sentData += "|" + document.getElementById("txtPhone").value;
sentData += "|" + selItem;
DoServerAction(sentData,'');
}
مطمئنا در سایت
www.barnamenevis.org در هنگام ارسال پست با عبارت "نوشته شما هم اکنون در حال فرستاده شدن است ... اندکی صبر کنید" مواجه شده اید.
همچنین اشاره گر ماوس نیز به صورت "ساعت شنی" ظاهر می شود.
در دو خط ابتدایی تابع فوق، اشاره گر ماوس در زمان قرارگیری بر روی دکمه و صفحه تغییر حالت خواهد داد. در خط سوم نیز عبارت "در حال ثبت اطلاعات. لطفا چند لحظه صبر کنید..." در المنت "DIV" قرار می گیرد.
پس از ایجاد یک ارجاء به DropDownList و به دست آوردن مقدار انتخاب شده در آن، متغیر "sentData" را با مقادیر وارد شده توسط کاربر پُر می کنیم. پیشتر ذکر شد که این مقادیر با کاراکتر "|" از یکدیگر متمایز می شوند.
در پایان، تابع "DoServerAction" را با مقادیر جمع آوری شده که در متغیر "sentData" قرار گرفتند فراخوانی می کنیم.
پس از اجرای عملیات در سمت سرور، در صورتی که روند ثبت رکورد با موفقیت انجام پذیرد، مقدار "یک" برگشت داده می شود و در پارامتر "strData" تابع جاوا اسکریپت "ReceiveDataFromServer" قرار می گیرد.
تابع "ReceiveDataFromServer" اجرا می شود.
ابن تابع، نتیجه ی پردازش را دریافت می کند و می توان بر مبنای آن عمل دلخواه را انجام داد.
نگاهی به تابع "ReceiveDataFromServer" می اندازیم...
کد:
function ReceiveDataFromServer(strData)
{
if (strData == "1") //Record Successfully Inserted
{
document.getElementById("tblContainer").style.visibility = "visible";
var ref_drpLocation = document.getElementById("drpLocation");
var selItem = ref_drpLocation.options[ref_drpLocation.selectedIndex].value;
var result = "";
result += document.getElementById("txtName").value;
result += "|" + document.getElementById("txtFamily").value;
result += "|" + document.getElementById("txtPhone").value;
result += "|" + selItem;
createElem(result);
document.getElementById("Message").innerHTML = "";
document.getElementById("btnAddRecord").style.cursor = "default";
document.body.style.cursor = "default";
}
else
{
document.getElementById("Message").innerHTML = "<font color='red'>!خطا در ثبت اطلاعات</font>";
document.getElementById("btnAddRecord").style.cursor = "default";
document.body.style.cursor = "default";
}
}
عملی که قصد انجام آن را در تابع "ReceiveDataFromServer" داریم، ایجاد و نمایش ردیف برای TABLE به ازای هر رکورد وارد شده توسط کاربر و نمایش آن بر روی صفحه است
گفته شد که: "در صورتی که روند ثبت رکورد با موفقیت انجام پذیرد، مقدار "یک" برگشت داده می شود".
گفته ی فوق را در ابتدای تابع بررسی می کنیم.
در صورت صحت عملیات (برگشت مقدار "یک")، "DIV" را نمایان می کنیم و مقادیر وارد شده توسط کاربر را همانند آنچه که در روال "doAction" انجام دادیم به دست می آوریم.
مشخص شد که هدف ما ایجاد عناصر TABLE به صورت پویا است. این کار با استفاده از جاوا اسکریپت امکان پذیر هست.
تابعی با نام "createElem" ایجاد کردم که وظیفه ی ایجاد ساختار مورد نیاز برای TABLE (ایجاد TD ها و TR ها) و انتساب آنها به TABLE را انجام می دهد...
کد:
function createElem(elemText)
{
var row = document.createElement("tr");
var tmpArray = elemText.split("|");
for (var i=0;i<tmpArray.length;i++)
{
var cell = createCell(tmpArray[i]);
row.appendChild(cell);
}
row.className = "trResultsItem";
document.getElementById("tbResults").appendChild(row);
return false;
}
تابع فوق با قبول یک مقدار ورودی، این مقادیر را که همان مقادیر وارد شده توسط کاربر هستند و به وسیله ی کاراکتر "|" از یکدیگر مجزا شده اند در یک آرایه قرار می دهد. با استفاده از اندیس این آرایه می توان به مقادیر ورودی دست پیدا کرد.
در ابتدا یک المنت "TR" ایجاد می شود. سپس تابع "createCell" به تعداد مقادیر ورودی به منظور ایجاد هر سلول (TD) فراخوانی می شود.
تابع "createCell" را در ذیل مشاهده می کنید:
کد:
کد:
function createCell(text)
{
var cell = document.createElement("td");
var tdText = document.createTextNode(text);
cell.appendChild(tdText);
return cell;
}
تابع فوق، یک المنت TD را ایجاد کرده و عبارت مورد نظر را که در پارامتر "text" قرار می گیرد به آن نسبت می دهد.
به تابع "createElem" باز می گردیم.
پس از فراخوانی تابع "createCell"، المنت TD ایجاد شده به المنت TR نسبت داده می شود.
سپس برای فرمت دهی به ردیف ایجاد شده، خاصیت "className" ردیف برابر با "trResultsItem" قرار داده شده است. خصوصیات تعریف شده برای کلاس "trResultsItem" در فایل "StyleSheet.css" برنامه قرار دارد.
توجه داشته باشید که می توان از متد "setAttribute" نیز به جای خاصیت "className" استفاده کرد اما متد "setAttribute" در Internet Explorer برای مقدار دهی به خاصیت "class" المنت کاربردی نخواهد داشت! اما خاصیت "className" در اکثر مرورگرها (همانند IE، Firefox، Opera) ساپورت می شود.
در نهایت، ردیف ایجاد شده به المنت "TBODY" ی "TABLE" نسبت داده می شود تا به عنوان یکی از ردیف های TABLE ثبت و نمایش داده شود.
توجه مهم:
همان طور که تا کنون متوجه شده اید، فرآیند ایجاد و نمایش پویای ردیف ها به طور کامل در سمت کلاینت انجام می شود! و این بدان معناست که نیازی به بازیابی کل اطلاعات از سرور نداریم! پُر واضح است که سرعت عملیات در این حالت به طرز چشمگیری افزایش پیدا خواهد کرد. البته توجه داشته باشید که این فرآیند مربوط به حالت خاصی از عمل ثبت است که نحوه ی انجام آن در ابتدای مقاله توضیح داده شد. مسلما در حالتی که نیاز به نمایش کلیه ی اطلاعات وارد شده توسط کاربران باشد، نیاز به بازیابی اطلاعات بیشتری از سمت سرور وجود دارد. در حالت کنونی، تنها مقدار "صفر" یا "یک" بازیابی می شود اما در حالتی که بدان اشاره شد، نیاز به بازیابی کلیه ی اطلاعات است.
حال ادامه ی تابع "ReceiveDataFromServer" را بررسی می کنیم.
پس از آنکه تابع "createElem" کار خود را با موفقیت انجام داد، پیغام نمایش داده شده در زمان ثبت رکورد در المنت "DIV" از بین می رود و اشاره گر ماوس به حالت اولیه ی خود باز می گردد.
در صورتی که روال ثبت رکورد با مشکلی مواجه شود، مقدار "صفر" برگشت داده خواهد شد. در این حالت پیغام " خطا در ثبت اطلاعات!" در المنت "DIV" نمایان می شود و اشاره گر ماوس نیز به حالت اولیه ی خود باز می گردد.
خسته نباشید! کار ما به پایان رسید.
تکنیک Client Callbacks انعطاف پذیری فوق العاده ای دارد. دست برنامه نویس را در ایجاد موقعیت های دلخواه و سفارشی باز می گذارد. البته خلق این موقعیت ها مستلزم آشنایی در حد متوسط با جاوا اسکریپت است. مسلما استفاده از این امکان جدید برای افرادی که رابطه ی خوبی با جاوا اسکریپت ندارند چندان خوشایند نیست...
در قسمت بعد، مطالب جالب تری در مورد این تکنیک خواهید خواند.