The Complete Guide Of ASP.NET AJAX

27 באוקטובר 2010

בס"ד

המדריך המלא ל – ASP.NET AJAX

 

הורדת המדריך בגרסת PDF.   
הורדת קבצי המקור של דוגמאות הקוד שבמדריך.

תוכן העניינים:

·        דרישות קדם.

·        מטרת המדריך.

·        מה זה בכלל AJAX.

·        Native AJAX – איך עבדו פעם ואיך הכול מתבצע מאחורי הקלעים.

o       עבודה עם XmlHttpRequest.

o       קריאה ל – HttpHandler.

§        GET.

§        .POST

o       הפעלת מתודות של WebService.

§        GET.

§        POST.

o       סיכום ביניים.

o       תרגיל – קבלת רשימת ערים לפי מדינות.

o       הכרת פורמט JSON.

o       קבלת אובייקטים ושליחת פרמטרים ב – JSON.

§        שימוש ב – JavaScriptSerializer.

§        שימוש במנגנון ScriptService כדי להחזיר JSON.

o       סיכום ביניים.

o       תרגיל – כתיבת AutoCompleteTextBox.

·        עבודה עם ICallbackEventHandler.

o       כיצד עובדים עם המנגנון.

o       תרגיל – מימוש מחשבון בעזרת ICallbackEventHandler.

·        היכרות עם ScriptManager.

o       תחילת העבודה.

o       הפעלה של WebService בעזרת ScriptManager.

o       הפעלה של מתודות בדף (PageMethods) בעזרת ScriptManager.

·        עבודה עם UpdatePanel.

·        היכרות עם Ajax Control Toolkit

 

 

דרישות קדם.

כדי להבין את המדריך הזה וכדי להשיג את המטרות שהמדריך נכתב עבורן יש להכיר:

·        Html

·        Xml

·        Java Script

·        ASP.NET

·        Web Service

 

מטרת המדריך:

הסיבה לכתיבת מדריך זה היא לרכז במקום אחד את כל (כמעט הכול) מה שקשור לעבודה ב – AJAX בעולם ה – ASP.NET, יש הרבה חומר באינטרנט (גם אצלי בבלוג) אבל רציתי לכתוב מדריך שמי שיקרא אותו ידע בסופו לכתוב AJAX ולהבין AJAX, חשוב לי לא רק יכולת הכתיבה ב – AJAX אלא  גם ההבנה מה קורה מאחורי הקלעים כמשתמשים במנגנונים המובנים של המערכת.

אני מקווה שתמצאו מדריך זה מועיל לכם ובסיומו תדעו ותבינו AJAX.

 

מה זה בכלל AJAX.

לפני שנבין מה זה AJAX ולמה המציאו אותו, נוודא שאנחנו זוכרים את התהליך שמתרחש בכל פעם שבו אנו לוחצים על לחצן בדף ASP.NET.

כשאנו גולשים לאתר אינטרנט כלשהו הדפדפן שולח בקשה (Request) לשרת ומבקש את הדף שאליו גלשנו (הפנייה יכולה להיות ב – GET או ב – POST – לא ניכנס כאן להבדלים, לקריאה נוספת על הנושא), במידה והשרת מצא את הדף שביקשנו הוא שולח תשובה (Response) הדפדפן מקבל את התשובה שבדרך כלל מכילה html ומציג את התשובה בדפדפן.

כשהמשתמש לוחץ על אחד מהלחצנים בעמוד מתרחש תהליך שנקרא Post Back, אוספים את כל המידע מהלקוח (כל האלמנטים בעמוד שהם מסוג input) ואת כל העוגיות ועוד כל מיני דברים מעניינים ושולחים Request חדש לשרת עם כל המידע שנאסף, השרת מקבל את המידע מנתח אותו ושולח תשובה (שהיא תהיה html חדש).

וכמובן בנוסף לכל זה כשמדובר בדף של ASP.NET יש את תהליך Page Life Cycle. שהוא מורכב וגורם להרבה עבודה בשרת.

התהליך הזה יקרה גם אם כל מה שרצינו לעשות זה לחשב שני מספרים ולהחזיר את התוצאה, מה שנשמע כמובן לא הגיוני, נניח שיש לנו דף שמכיל המון מידע ותמונות, מה ההיגיון לשלוח את כל תוכן הדף לשרת רק כדי לחבר שני מספרים ולהחזיר דף שנראה אותו דבר בשינוי קל של תוצאת החישוב ?

כדי לפתור את הבעיה הזאת החליט מי שהחליט להמציא את המושג Asynchronous JavaScript and XML – מה שמוכר בראשי התיבות שלו AJAX, המשמעות של הראשי תיבות היא שליחת Request מ – Java Script בצורה אסינכרונית (כלומר מבלי לתקוע את הדפדפן עד שיש תשובה) ולקבל את ה – Response בפורמט XML.

למשל – במידה ונרצה לחשב שני מספרים – במקום לשלוח את כל תוכן הדף לשרת נפעיל מתודה בשרת בעזרת יצירת Request מ – Java Script, השרת יחזיר את רק אץ סכום המספרים ולא את כל הדף מחדש וב – Java Script נציג היכן שצריך להציג את התוצאה.

החיסכון שלנו הוא יהיה כפול, ראשית חסכנו המון תעבורה והפחתנו עומסים מהשרת בנוסף חווית המשתמש תשתפר פלאים מכיוון שהוא מחכה הרבה פחות זמן לקבלת תשובה והוא לא חווה את התהליך המעצבן שהדף נהיה לבן ונבנה מחדש, והוא לא יהיה מודע לכך שמאחורי הקלעים הייתה פנייה מהירה וקצרה לשרת.

 

Native AJAX – איך עבדו פעם ואיך הכול מתבצע מאחורי הקלעים

כמו כל דבר בעולם התכנות, יש המון עטיפות ושכבות לכל קומפוננט, וגם ל – AJAX יש, אנחנו נכיר מאוחר יותר את הספרייה של ASP.NET, אבל חשוב להבין מה בעצם קורה מאחורי הקלעים ומה אותם עטיפות עושים בפועל, ולכן אנחנו נתחיל עם הבסיס ונכתוב קוד AJAX בסיסי ביותר, ולאחר מכן נראה כיצד העטיפות עוזרות לנו לכתוב קוד קל יותר.

עבודה עם XmlHttpRequest

כדי לעבוד ב – AJAX קיים ב – Java Script אובייקט מיוחד בשם XmlHttpRequest, בעזרתו ניתן לשלוח Request לשרת מ – Java Script.

אלו המאפיינים והמתודות של האובייקט שאנחנו צריכים להכיר.

open(method, url, async)               

המתודה מקבלת מספר פרמטרים ואלו מה שמעניין אותנו.

·        method – יכול לקבל GET או POST בהתאם לצורת שליחת הבקשה העדיפה עלינו.

·        url – הכתובת של הדף או המתודה שאנחנו רוצים לקבל.

·        async – במידה ונשלח true הבקשה תשלח בצורה אסינכרונית כלומר הדפדפן לא יתקע והגולש יכול להמשיך לשוטט להנאתו בדף, לעומת זאת אם נשלח false, בזמן שליחת הבקשה המשתמש לא יכול לעשות כלום בעמוד ומחכים לתשובה שהשרת יחזיר.

 

send(data)

כאשר קוראים לו מתבצעת שליחת ה – Request בפועל, המתודה יכולה לקבל data ונשתמש בזה כשנגדיר את method כ – POST, מכיוון שב -  GET אין משמעות לשלוח data מכיוון שאת כל המידע אנחנו אמורים לשלוח ב – Query String.

 

readyState

משתנה המכיל את המידע אודות המצב הנוכחי של הבקשה, הערך יכול להיות בין 0-4

·        0 – האובייקט נוצר אך עדיין לא אותחל, זה יהיה המצב לפני הקריאה למתודת open

·        1 – המתודה open נקראה אבל המתודה send עדיין לא נקראה.

·        2 – המתודה send נקראה אבל עדיין לא הגיע שום תשובה מהשרת.

·        3 – חלק מהמידע חזר אבל עדיין לא כולו.

·        4 – כל המידע חזר ואפשר כבר להשתמש בו.

 

status

מכיל את המידע על מצב ה – Response, כלומר האם הבקשה עברה כמו שצריך והתשובה שחזרה מכילה את מה שציפינו שתכיל, הערך יהיה 200 עבור בקשה תקנית, 404 עבור "דף לא נמצא"  (לרשימת הקודים המלאה).

 

onreadystatechange

משתנה המקבל פונקציה להפעלה בזמן שינוי הערך של המשתנה readyState, נוכל להירשם לאירוע כדי לדעת שקבלנו תשובה, בדרך כלל יהיה לנו קוד כזה:

xhr.onreadystatechange = function() {

    if (xhr.readyState == 4 && xhr.status == 200) {

        //.. some code

    }

};

 

 

נרשמנו לאירוע של שינוי המצב ונרצה לבצע קוד כלשהו רק בתנאי שהכול תקין (כלומר המצב עומד כרגע על הערך 4 – קבלנו תשובה מהשרת – והסטאטוס הוא 200 – הכול תקין) נרצה למשל להציג את התוצאה.

 

responseText

מכיל את התשובה שקבלנו מהשרת בצורה טקסטואלית.

 

responseXML

מכיל את התשובה שקבלנו מהשרת כאובייקט XML ויש לנו מתודות על האובייקט כדי לחלץ את המידע.

 

setRequestHeader

משמש אותנו לשלוח מידע נוסף לשרת – נראה את השימוש בו יותר מאוחר.

 

קריאה ל – Http Handler (הדוגמא המלאה נמצאת ב – NativeAJAX\GetHtmlOfOtherPage\Handler)

          GET

כעת נראה דוגמא מלאה – נרצה לפנות לשרת לשלוח שני מספרים ולקבל את התוצאה. 

נגדיר Handler חדש שמקבל ב – Query String את שני המספרים וכותב את התוצאה לתוך ה – Response

public void ProcessRequest(HttpContext context)

{

    context.Response.ContentType = "text/plain";

 

    int num1 = int.Parse(context.Request["n1"]);

    int num2 = int.Parse(context.Request["n2"]);

 

    context.Response.Write((num1 + num2).ToString());

}

 

 

 כעת נכתוב את צד הלקוח שיפנה לאותו handler ישלח את הערכים ויציג את התשובה ב – alert

<body>

    <input type="text" id="txt1" />

    <input type="text" id="txt2" />

    <input type="button" value="Use Ajax" onclick="Run()" />

</body>

 

function Run() {

    var num1 = document.getElementById('txt1').value;

    var num2 = document.getElementById('txt2').value;

 

    var xhr = new XMLHttpRequest();

 

    var qs = '?n1=' + num1 + '&n2=' + num2;

    var url = "Add2Numbers.ashx" + qs;

    xhr.open("GET", url, true);

 

    xhr.onreadystatechange = function(response) {

        if (xhr.readyState == 4 && xhr.status == 200) {

            alert(xhr.responseText);

        }

    };

 

    xhr.send();

}

 

בשלב הראשון מוציאים את הערכים מתוך תיבות הטקסט, לאחר מכן מייצרים מופע של XmlHttpRequest, לאחר מכן מייצרים את ה – url שמורכב מהכתובת של ה – handler ושליחת הפרמטרים ב – query string, מפעילים את מתודת open ומגדירים שה – request ישלח ב – GET, לאחר מכן נרשמים ל – onreadystatechange ובזמן שהתשובה תתקבל בהצלחה נציג את התוצאה ב – alert, במימוש הספציפי של ה – handler אפשר להשתמש רק במאפיין responseText מכיוון שלא שלחנו XML אלא רק מחרוזת, בסופו של דבר אנחנו קוראים למתודת send, וזאת התוצאה:

pic1.jpg

POST

אפשר כמובן גם לשלוח בקשת Request בתצורת POST, הקוד יהיה כמעט זהה:

function RunPOST() {

    var num1 = document.getElementById('txt1').value;

    var num2 = document.getElementById('txt2').value;

 

    var xhr = new XMLHttpRequest();

 

    var url = "Add2Numbers.ashx";

    xhr.open("POST", url, true);

 

    xhr.onreadystatechange = function() {

        if (xhr.readyState == 4) {

            alert(xhr.responseText);

        }

    };

 

    var contentType = "application/x-www-form-urlencoded";

    xhr.setRequestHeader("Content-Type", contentType);

    var qs = 'n1=' + num1 + '&n2=' + num2;

    xhr.send(qs);

}

 

השינויים בין הקריאות:

o       ה – URL מכיל רק את הכתובת בלי הפרמטרים.

o       ה – method הוגדר כ – POST

o       הוספנו הגדרה ל – header מסוג Content-Type שהערך שלו זה application/x-www-form-urlencoded

o       הערכים נשלחו בזמן קריאה ל – send.

 

הפעלת מתודות של Web Service (הקוד המלא – NativeAJAX\GetHtmlOfOtherPage\WebService)

          GET

הקוד שהרצנו מקודם עובד אבל יש לו חסרון אחד, זה לא נוח ולא הגיוני "לגלוש" ל – handler כדי לקבל תשובות, באמצעות Web Service נוכל להפעיל מתודות בצד השרת ולקבל תשובות.

נגדיר WebService חדש שמקבל שני מספרים ומחזיר מספר.

 

 

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

[System.ComponentModel.ToolboxItem(false)]

public class Add2NumberService : System.Web.Services.WebService

{

    [WebMethod]

    public int Add(int n1, int n2)

    {

        return n1 + n2;

    }

}

חשוב לשים לב שלמחלקה Add2NumberService יש attribute (מה זה Attribute) בשם WebService ולמתודה Add יש attribute בשם WebMethod.

כעת אנו רוצים להפעיל את המתודה Add מתוך קוד ה – Java Script.

כברירת מחדל Web Service חושף את עצמו רק ב – POST ו – SOAP, כדי לחשוף אותו גם ב – GET, צריך להוסיף את המקטע הבא לקובץ הקונפיג

<webServices>

  <protocols>

    <add name="HttpGet"/>

  </protocols>

</webServices>

 

כעת נכתוב את הקוד הבא. (בפונקצית Run)

var xhr = new XMLHttpRequest();

 

var qs = '?n1=' + num1 + '&n2=' + num2;

var url = "Add2NumberService.asmx/Add" + qs;

xhr.open("GET", url, true);

 

xhr.onreadystatechange = function() {

    if (xhr.readyState == 4 && xhr.status == 200) {

        // extract the value from result

    }

};

 

xhr.send()

 

אפשר לראות שה – URL מכיל את כתובת ה – service ואת שם המתודה ומקבל את הערכים ב – query string.

אבל, כדי להוציא את המידע כעת צריך לעשות קצת עבודה, ה – responseText יחזיר לנו את התוצאה הבאה:

pic2.jpg

ולכן כאן צריך להשתמש עם ה – responseXML כדי לחלץ את המידע מכיוון ש – Web Service  מחזירים כברירת מחדל XML, הסיבה שנעדיף לעבוד עם Web Service ולא עם handler היא פשוטה, זה נכון שחילוץ המידע פשוט יותר ב – handler, אבל handler יכול להחזיר רק מחרוזות ו – html ואילו Web Service יכול להחזיר אובייקטים וזה הגיוני להפעיל מתודה בשרת כדי לקבל את המידע, ולא לקבל html.

                   POST

כמו ב – handler קריאה ל – Web Service ב – POST דומה מאוד לקריאה ב – GET, והיא תראה כך:

var xhr = new XMLHttpRequest();

 

var url = "Add2NumberService.asmx/Add";

xhr.open("POST", url, true);

 

xhr.onreadystatechange = function() {

    if (xhr.readyState == 4 && xhr.status == 200) {

        alert(xhr.responseXML.text);

    }

};

 

var contentType = "application/x-www-form-urlencoded"

xhr.setRequestHeader("Content-Type", contentType);

var qs = 'n1=' + num1 + '&n2=' + num2;

xhr.send(qs);

 

 

סיכום ביניים

עבודה ב – AJAX משמעותה שליחת בקשות לשרת מתוך Java Script, קבלת הנתונים במהירות ועבודה על הנתונים בצד הלקוח.

ראינו כיצד להשתמש באובייקט XmlHttpRequest, ראינו עבודה עם Http Handler ועבודה עם Web Service ועשינו זאת ב – GET ו – POST.

 

תרגיל – קבלת רשימת ערים לפי מדינות

בשלב הזה אני מציע לעצור את הקריאה ולתרגל את מה שלמדנו עד כה.

·        צרו דף שבו יהיו שני אלמנטים מסוג select.

·        ל – select הראשון יהיה רשימת מדינות, ובשני לא יהיה כלום (כרגע)

·        צרו Web Service שיש בו מתודה שמקבלת string (שם מדינה) ומחזירה מערך של string (רשימת ערים)

·        הירשמו לאירוע onchange של ה – select הראשון הפעילו את ה – Web Service ושילחו למתודה את המדינה שהמשתמש בחר, קבלו בחזרה את רשימת הערים ומלאו אותם ב – select השני, (זכרו לרוקן את ה – select השני מכל הערכים שיש בו)

·        מומלץ לשים break point במקום שבו מקבלים את התוצאה ולהסתכל מה מכיל האובייקט responseXML כדי לדעת כיצד לפרסר את הנתונים שמתקבלים. (במידה וה – debugger של visual studio עושה בעיות ולא מוכן להציג את התוכן של המאפיין תשתמשו ב – debugger של IE. בקשה על F12 תפתח את החלון של IE Developer Tool Bar, שם בחרו את הטאב Script ולחצו על Start Debugging ותוכלו לשים break points כדי לדבג)

פיתרון התרגיל (הקוד המלא נמצא ב – NativeAJAX\CountriesAndCities)

לפני קריאת הפיתרון מומלץ בחום לנסות לפתור לבד.

 

צד  השרת:

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

[System.ComponentModel.ToolboxItem(false)]

public class Country : System.Web.Services.WebService

{

    [WebMethod]

    public string[] GetCities(string country)

    {

        if (country.ToLower() == "israel")

        {

            return new string[]

                {

                    "Tel-Aviv",

                    "Ramt Gan",

                    "Elad"

                };

        }

        else if (country.ToLower() == "usa")

        {

            return new string[]

                {

                    "Boston",

                    "Los Angeles",

                    "Holywood"

                };

        }

 

        return new string[0];

    }

}

 

 

 

צד הלקוח:

<select onchange="Fill(this.value)">

    <option>Select</option>

    <option value="israel">Israel</option>

    <option value="usa">USA</option>

</select>

<select id="city">

</select>

function Fill(value) {

 

    var cityCombo = document.getElementById('city');

    cityCombo.options.length = 0;

 

    if (value != '-1') {

        var xhr = new XMLHttpRequest();

 

        var qs = '?country=' + value;

        var url = "Country.asmx/GetCities" + qs;

        xhr.open("GET", url, true);

 

        xhr.onreadystatechange = function() {

            if (xhr.readyState == 4 && xhr.status == 200) {

 

                var arrayNode = xhr.responseXML.childNodes[1];

 

                for (var i = 0; i < arrayNode.childNodes.length; i++) {

                    var opt = new Option(arrayNode.childNodes[i].text);

                    cityCombo.options[i] = opt;

                }

            }

        };

 

        xhr.send();

    }

}

 

הכרת פורמט JSON

כמו שראיתם המידע שחוזר מה – Web Service הינו בפורמט XML, בפיתרון התרגיל קבלנו את ה – XML הבא:

puc3.jpg

 

וכמו שאפשר לשים לב XML הוא פורמט מבוזבז, כל הטקסט של התגים עצמם נמצא גם לפני התוכן וגם אחרי התוכן, בנוסף כמו שאפשר לראות הקוד שצריך לכתוב כדי לחלץ את המידע מתוך ה – XML הוא מאוד לא ברור ומאוד לא קריא, בתרגיל היה מדובר על מערך של מחרוזות, תחשבו איך יראה ה – XML של מערך של מחלקה שיש לה כמה מאפיינים שחלקם מערכים של מחלקות אחרות וכו', הקוד שנצטרך לכתוב כדי לחלץ את התוכן יהיה כמובן מאוד לא ברור וקשה לתחזוקה.

כדי לפתור את שני הבעיות הללו נוצר פורמט אחר שנקרא JavaScript Object Notation המוכר כ – JSON, לא נרחיב הרבה על הפורמט, בקצרה במקום לשלוח את ה – XML הבזבזני נוכל את אותו תוכן בפורמט JSON והוא יראה כך:

["Tel-Aviv","Ramt Gan","Elad"]

ב – JSON זה אומר מערך של מחרוזות, במקרה של אובייקט, כל מאפיין ייכתב כך:

key:value

בשיטה הזאת נוכל לחסוך בתעבורה של ה – XML שהוא בזבזני, אבל יותר חשוב מכך אפשר להפוך מאפיינים ב – Java Script מחרוזת בפורמט JSON לאובייקט, ואז נוכל להשתמש במאפיינים של האובייקט כדי לקבל גישה למידע במקום לרוץ על ה- XML.

 

קבלת אובייקט ושליחת פרמטרים ב – JSON

יש לנו שני דרכים ב – Web Service לקבל ולשלוח ב – JSON, הראשונה היא בעזרת מחלקה מיוחדת שנקראת JavaScriptSerializer והדרך השנייה היא Script Service, לכל אחד מהם יש חסרונות ויתרונות ונכיר כאן את שניהם.

עבודה עם JavaScriptSerializer (הקוד המלא – NativeAJAX\JSON\JSS)

נניח שיש לנו מחלקת Person שנראית כך:

public class Person

{

    public int Id { get; set; }

    public string Name { get; set; }

    public float Age { get; set; }

    public List<Address> Addresses { get; set; }

}

 

public class Address

{

    public string Country { get; set; }

    public string City { get; set; }

    public string Street { get; set; }

    public int Number { get; set; }

}

 

ויש לנו מתודה שאמורה להחזיר מופע שלו (בשלב הזה מתודה שלא מקבלת פרמטר)

אם נחזיר XML נקבל משהו בסגנון הזה:

puc4.jpg

 

וכמובן נצטרך לעבוד קשה כדי לחלץ את המידע, מה שאנחנו נעשה יהיה להגיד שבמקום שהמתודה שלנו תחזיר אובייקט מסוג Person היא תחזיר string שבה יהיה ייצוג של המופע שאנחנו רוצים להחזיר.

נכתוב את הקוד הבא:

[WebMethod]

public string GetPerson()

{

    var person = GetPersonInstance();

    return new JavaScriptSerializer().Serialize(person);

}

 

 

 

 

 

 

כעת כשנפנה ל – Web Service מצד הלקוח נקבל את המחרוזת הבאה:

pic5.jpg

 

כבר אפשר לראות שחסכנו די הרבה תעבורה, אבל מה שיותר חשוב היות שהתוכן של התשובה היא בפורמט JSON ולכן בצד הלקוח נוכל לכתוב את הקוד הבא:

xhr.onreadystatechange = function() {

    if (xhr.readyState == 4 && xhr.status == 200) {

        var obj = eval('(' + xhr.responseXML.text + ')');

    }

};

 

 

הפעלה של המתודה eval על מחרוזת בפורמט JSON יוצרת אובייקט של JS, ולמעשה נוכל להשתמש בכל המאפיינים שיש לו, כמו שאפשר לראות בתמונה הבאה:

pic6.jpg

 

כעת הקוד יהיה כמובן קריא וברור יותר וניתן לתחזוקה מכיוון שבמקום לראות קוד שרץ על XML נעבוד עם המאפיינים של האובייקט.

(יש דרכים נוספות להמרת JSON לאובייקטים, למשל JSON.parse)

                  

כעת נניח שיש לנו מתודה שנקראת Update והיא אמורה לקבל כפרמטר מופע של  Person ולשמור את השינויים שנעשו בו, השאלה שנשאלת כיצד נוכל לשלוח אובייקט לשרת, בעזרת JavaScriptSerializer נוכל לעשות זאת בפשטות, נגדיר את המתודה הבאה.

 

[WebMethod]

public void Update(string personInJsonFormat)

{

    var serializer = new JavaScriptSerializer();

    Person person =

        serializer.Deserialize<Person>(personInJsonFormat);

 

    // save changes….

}

 

 

כמו שאפשר לראות המתודה מקבלת מחרוזת וממירה אותו לאובייקט מסוג Person.

מה שיותר צריך להבין כיצד בצד הלקוח נמיר את האובייקט למחרוזת JSON, כדי שנוכל לשלוח אותו לשרת, (כעת מומלץ לעבוד ב – POST כדי שלא נעבור את מספר התוויים המקסימאלי שיכול להיות ב – Query String)

שלב ראשון נייבא את האובייקט מצד השרת בעזרת קריאה למתודה הקודמת (זאת שמשתמשת ב – eval כדי להמיר מ – JSON לאובייקט) לאחר מכן נשמור אותו במשתנה גלובלי, ואז נפעיל את המתודה הבאה:

function RunUpdate() {

 

    obj.Name = "Update Name";

 

    var xhr = new XMLHttpRequest();

    var url = "PersonService.asmx/Update";

    xhr.open("POST", url, true);

 

    var contentType = "application/x-www-form-urlencoded";

    xhr.setRequestHeader("Content-Type", contentType);

 

    var qsValue = JSON.stringify(obj);

    xhr.send("personInJsonFormat=" + qsValue);

}

 

נשנה מאפיינים של האובייקט, נגדיר את השליחה ב- POST (ולכן גם נגדיר את ה – Request Header), נשתמש בפונקציה stringify כדי להמיר את האובייקט ל – JSON, ונקבל את המחרוזת הבאה:

{"Id":1,"Name":"Update Name","Age":20.2,"Addresses":[{"Country":"Israel","City":"Elad","Street":"Amos","Number":19},{"Country":"Israel","City":"Beni-Brak","Street":"noam","Number":10}]}

נשלח אותה ב – body של ה – Request כערך לפרמטר personInJsonFormat.

 

אפשר לראות ששליחה וקבלה של אובייקטים ב – JSON הופכת להיות קלה מאוד, בצד השרת נשתמש ב – JavaScriptSerializer ובצד הלקוח נשתמש ב – eval וב – stringify.

 

שימוש ב – ScriptService כדי לשלוח ולקבל JSON (הקוד המלא – NativeAJAX\JSON\SS)

אם הכול כל כך נחמד ופשוט בעזרת JavaScriptSerializer אז למה לא להשתמש בזה תמיד ?

התשובה פשוטה – ראשית לא קבלנו באמת JSON, חדי העין יכלו לשים לב שקבלנו XML שהתוכן שלו היה JSON, אבל זה לא כל כך משנה, מה שכן מפריע הוא שהחתימה של המתודה יקבלו ויחזירו תמיד מחרוזות במקום אובייקטים, ובדרך כלל Web Services לא משמשים רק לקריאות Java Script אלא גם לעבודה בצד השרת ואם המתודה תחזיר מחרוזת, מי שיפעיל אותה מאפליקציה אחרת לא ידע (או לא אמור לדעת) מה לעשות עם המחרוזת שהוא מקבל, וכמובן הוא לא ידע מה לשלוח לה כפרמטר.

כדי לפתור את בעיית "חתימת המתודות" נוכל להשתמש במנגנון של ScriptService, כל מה שנצטרך לעשות זה להוסיף ל – Web Service שלנו Attribute בשם ScriptService ולמתודות שלנו Attribute בשם ScriptMethod, ברגע שנעשה זאת המתודות שלנו יוכלו לקבל ולשלוח אובייקטים ומאחורי הקלעים תתבצע ההמרה מ – JSON ול – JSON.

ב – Web Service יהיה לנו את אותם שני מתודות אבל הפעם הם יקבלו ויחזירו Person

 

 

 

 

 

 

 

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

[System.ComponentModel.ToolboxItem(false)]

[ScriptService]

public class PersonServiceWithSS : System.Web.Services.WebService

{

    [ScriptMethod(UseHttpGet = true)]

    [WebMethod]

    public Person GetPerson()

    {

        Person person = GetPersonInstacne();

 

        return person;

    }

 

 

    [ScriptMethod]

    [WebMethod]

    public void Update(Person person)

    {

        // save changes….

    }

}

 

לפני שנעבור לצד הלקוח חשוב לשים לב שבמתודה GetPerson הוגדר ב – Attribute ש – UseHttpGet הוא true, היות שברגע שהשתמשנו ב –ScriptService אפשר להפעיל את המתודות רק ב – POST אלא אם כן נגדיר אחרת.

כעת נעבור לצד הלקוח.

כדי לקבל את האובייקט נריץ את הקוד הבא

var xhr = new XMLHttpRequest();

 

var url = "PersonServiceWithSS.asmx/GetPerson";

xhr.open("GET", url, true);

 

xhr.onreadystatechange = function() {

    if (xhr.readyState == 4 && xhr.status == 200) {

        obj = eval('(' + xhr.responseText + ')');

    }

};

 

xhr.setRequestHeader("Content-Type", "application/json");

xhr.send();

 

אפשר לראות שבשונה מהקריאה הקודמת (שימוש ב – JavaScriptSerializer) אנחנו מפעילים eval על responseText ולא gל responseXML.text מכיוון שחזר JSON אמיתי ולא XML שמכיל JSON, בנוסף היינו צריכים להגדיר את ה – requestHeader ל – JSON (אע"פ שמדובר ב – GET), היתרון הגדול הוא כמובן שהמתודה מחזירה אובייקט ולא מחרוזת כך שאפליקציות אחרות יכולות להשתמש בה כרגיל.

אם נסתכל מה מכיל ה – obj שקבלנו לאחר הפעלת eval נראה את הדבר הבא:

pic7.jpg

כל התוכן של האובייקט מסתתר תחת מאפיין שנקרא d (כנראה קיצור של data) ואפילו יש לנו משתנה (__type) שמכיל את הטיפוס של האובייקט בצד השרת.

כדי לעדכן את האובייקט נכתוב את הקוד הבא:

obj.d.Name = "Update Name";

 

var xhr = new XMLHttpRequest();

var url = "PersonServiceWithSS.asmx/Update";

xhr.open("POST", url, true);

 

xhr.setRequestHeader("Content-Type", "application/json");

 

var qsValue = JSON.stringify(obj.d);

xhr.send('{person:' + qsValue + '}');

 

בהתחלה נעדכן את המאפיינים שנרצה (עם שימוש ב – d כדי לגשת למאפיינים), לאחר מכן נפעיל את stringify על obj.d ונשלח אותו כפרמטר לפונקציה, כמובן שצריך לשלוח אותו בפורמט JSON כלומר:

{key:value}

ולכן נכתוב:

{person: personvalue}

כש – personValue יתחלף עם מה שקבלנו מה – stringify.

סיכום ביניים

שימוש בפורמט JSON חוסך בתעבורה ועוזר לכתוב קוד נקי וקל לתחזוקה, כדי לעבוד עם JSON אפשר להשתמש ב – JavaScriptSerializer בצד השרת ובמקרה הזה המתודות יכולים לקבל ולהחזיר רק מחרוזות, אופציה שנייה היא להשתמש ב – ScriptService ובמקרה הזה צריך להגדיר את ה – content-type ל – application/json, בנוסף כדי לעבוד ב – GET צריך להגדיר את זה במפורש בעזרת UseHttpGet.

 

תרגיל – Auto Complete Text Box (הפיתרון המלא – NativeAJAX\AutoComplete)

בשלב הזה כדאי לעצור את המשך הקריאה ולתרגל, אנו רוצים שיהיה לנו תיבת טקסט שבזמן הקשת אותיות נקבל הצעות משלימות (כמו בגוגל) נשתמש ב – JSON בשיטת ScriptService.

         

·        צרו דף והוסיפו תיבת טקסט.

·        הירשמו לאירוע onkeyup ושלחו כפרמטר לפונקציה את הערך שיש בתיבת הטקסט.

·        הוסיפו select והגדירו את ה – size שלו ל – 7.

·        הירשמו לאירוע onchange של ה – select.

·        צרו Web Service עם מתודה המקבלת מחרוזת ומחזירה מערך. (לא לשכוח להוסיף את ה – attributes המתאימים), המתודה תחזיר מערך של מחרוזות אקראיים שמתחילים עם הטקסט שקבלתם.

·        מתוך המתודה שנרשמתם בזמן onkeyup הפעילו את ה – Web Service ומלאו את ה – select עם הערכים שקבלתם.

·        מתוך המתודה שנרשמתם אליה ב – onchange קחו את הערך שנבחר והשימו אותו בתיבת הטקסט.

 

מומלץ לנסות לפתור לבד את התרגיל, לפני עיון בקוד הפיתרון.

 

 

 

 

 

 

צד השרת:

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

[System.ComponentModel.ToolboxItem(false)]

[ScriptService]

public class AutoComleteService : System.Web.Services.WebService

{

    private static Random random = new Random();

 

    [ScriptMethod(UseHttpGet = true)]

    [WebMethod]

    public List<string> GetSuggestion(string prefix)

    {

        List<string> list = new List<string>();

 

        var count = random.Next(5, 20);

 

        for (int i = 0; i < count; i++)

        {

            list.Add(prefix + RandomString(random.Next(0, 10)));

        }

 

        return list;

    }

 

    private string RandomString(int size)

    {

        // return random string

    }

}

 

 

 

 

 

 

 

 

 

 

 

צד הלקוח:

<input type="text"

onkeyup="GetData(this.value)"

id="txt" />

 

<select id='sp'

        onchange="Change(this)"

        size="7"

        name="sp">

</select>

 

 

 

 

function GetData(value) {

 

    var xhr = new XMLHttpRequest();

 

    var webServiceName = 'AutoComleteService.asmx/GetSuggestion';

    var url = webServiceName + "?prefix='" + value + "'";

 

    xhr.open("GET", url, true);

 

    xhr.onreadystatechange = function() {

        if (xhr.readyState == 4 && xhr.status == 200) {

 

            var arr = eval('(' + xhr.responseText + ')');

 

            var select = document.getElementById('sp');

            select.options.length = 0;

            for (var i = 0; i < arr.d.length; i++) {

                select.options[i] = new Option(arr.d[i]);

            }

        }

    };

 

    xhr.setRequestHeader("Content-Type", "application/json");

    xhr.send();

}

 

function Change(select) {

    var data = document.getElementById('txt');

 

    data.value = select.options[select.selectedIndex].text;

}

 

 

 

 

עבודה עם ICallbackEventHandler

כעת אחרי שראינו והבנו את העבודה עם AJAX הבסיסי, נכיר כמה מן העטיפות לעבודה עם XmlHttpRequest, העטיפה הראשונה שנכיר היא מימוש ICallbackEventHandler

התפקיד של ה – Interface הזה הוא לאפשר הפעלה של מתודה בצד השרת מצד הלקוח בלי צורך לכתוב לבד את כל הקוד של XmlHttpRequest, ובסיום עבודת השרת להפעיל מתודה בצד הלקוח עם התשובה מהשרת.

נניח שיש לנו תיבת טקסט של רישום משתמש חדש, מי שרוצה להירשם מתחיל לכתוב את השם שלו בתיבת הטקסט במידה והשם קיים כבר במערכת תיבת הטקסט נצבעת באדום, וכל זה מבלי לעשות Post Back או לכתוב קוד עם XmlHttpRequest.

ה – Interface מכיל שני מתודת שצריך לממש.

void RaiseCallbackEvent(string eventArgument);

string GetCallbackResult();

 

המתודה הראשונה נקראת מצד הלקוח ואפשר לשלוח לה כפרמטר מחרוזת (שכמובן יכולה להכיל הרבה מידע ובצד השרת צריך לחלץ את המידע בעזרת מניפולציות על מחרוזת) המתודה השנייה תקרא מיד לאחר מכן ואמורה להחזיר מחרוזת עם התשובה לצד הלקוח.

התהליך הוא כזה.

·        בצד ה – Java Script צריך לקרוא למתודה מיוחדת שנקראת WebForm_DoCallBack שמקבלת את הפרמטרים הבאים.

o       ה – Control שמממש את ICallbackEventHandler.

o       מתודה ב – Java Script להפעלה כדי לקבל את הפרמטר למתודת RaiseCallbackEvent בצד השרת.

o       שם של מתודה להפעלה ב – Java Script במידה והכול עבר בהצלחה.

o       שם של מתודה להפעלה ב – Java Script במידה והיה שגיאה.

o       משתנה מסוג boolהאם הקריאה היא סינכרונית או לא.

·        לאחר שקראנו ל – WebForm_DoCallBack

·        המתודה שאמורה להחזיר את הפרמטר עבור RaiseCallbackEvent נקראת.

·        Page_Load (וכל ה – Page Life Cycle) מופעל. (בצד השרת)

·        RaiseCallbackEvent נקראת.

·        GetCallbackResult נקראת.

·        המתודה בצד הלקוח מופעלת (או מתודת ההצלחה או מתודת הכישלון)

 

 

 

נראה את הקוד. (הקוד המלא – Callback\ WebPageWithCallback.aspx)

צד הלקוח:

<input type="text" id="txt" runat="server" />

 

function GetArgs() {

    return document.getElementById('txt').value;

}

 

function onSuccess(res) {

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

 

    if (res == "true") {

        txt.style.backgroundColor = 'red';

    }

    else {

        txt.style.backgroundColor = ";

 

    }

}

 

function onFailed(res) {

    alert(res);

}

 

צד השרת:

הדף צריך לממש את ICallbackEventHandler

private string userName;

 

#region ICallbackEventHandler Members

 

public void RaiseCallbackEvent(string eventArgument)

{

    userName = eventArgument;

}

 

public string GetCallbackResult()

{

    if (userName == "shlomo")

    {

        return "true";

    }

 

    return "false";

}

 

#endregion

 

מה שאנו עושים הוא די פשוט, המתודה RaiseCallBackEvent תקבל את הטקסט שהמשתמש יקליד ותשמור אותו במשתנה.

לאחר מכן המתודה GetCallBackResult תבדוק האם הוא קיים (בדוגמה אין לנו בסיס נתונים – ולכן נבדוק מול השם shlomo) ותחזיר תשובה מתאימה.

כדי לקשר בין תיבת הטקסט לבין קריאה ל – WebForm_DoCallBack נצטרך לכתוב את הקוד הבא: (ב – Page_Load)

 

protected void Page_Load(object sender, EventArgs e)

{

    string callBackStr =

        ClientScript.GetCallbackEventReference(this,

                                                "GetArgs()",

                                                "onSuccess",

                                                null,

                                                "onFailed",

                                                true);

    txt.Attributes["onkeyup"] = callBackStr;

}

 

השורה הראשונה עושה שני דברים:

·        דואגת שקובץ הסקריפט שמכיל את הפונקציה WebForm_DoCallBack תרד ללקוח.

·        מחזירה את המחרוזת המייצגת קריאה לפונקציה עם כל הפרמטרים ששלחנו.

o       this – הפקד שמממש את ICallbackEventHandler

o       GetArgs() – פונקציה בצד הלקוח שתחזיר את הערך מתוך תיבת הטקסט.

o       onSuccess – שם הפונקציה להפעלה במידה והכול הצליח.

o       onFailed – שם הפונקציה להפעלה במידה ומשהו נכשל.

o       true . – שהקריאה תהיה אסינכרונית.

 

לאחר מכן נגדיר שבזמן onkeyup הפונקציה תקרא.

כעת במידה והטקסט יהיה shlomo תיבת הטקסט תצבע באדום, אחרת היא תהיה לבנה.

pic8.jpg

 

תרגיל – מימוש מחשבון בעזרת ICallbackEventHandler
(את הפיתרון המלא ניתן למצוא ב –
Callback\ WebForm1.aspx)

 

עליכם לממש מחשבון שיתמוך בארבעת הפעולות הבסיסיות (חיבור חילוק כפל וחילוק).

המחשבון יהיה UserControl שיכיל את ה – GUI ואת המתודות לחישוב.

בדף יהיה לכם מופע של ה – User Control.

פתרון: (כרגיל מומלץ לנסות לפתור לבד אץ התרגיל ואז להסתכל בפיתרון המוצע)

 

צד הלקוח: (User Control)

 

<input type="text" id="txt1" />

<input type="text" id="txt2" />

 

<input type="button" value="Add" id="btnAdd" runat="server" />

<input type="button" value="Sub" id="btnSub" runat="server" />

<input type="button" value="Multi" id="btnMulti" runat="server" />

<input type="button" value="Div" id="btnDiv" runat="server" />

 

<span id="sp"></span>

 

 

function GetArgs() {

    var num1 = document.getElementById('txt1').value;

    var num2 = document.getElementById('txt2').value;

    var method = event.srcElement.value;

 

    // 2:4:Add

    return num1 + ':' + num2 + ':' + method;

}

 

function onSuccess(res) {

    document.getElementById('sp').innerHTML = res;

}

 

function onFailed(res) {

    alert(res);

}

 

 

צד השרת (User Control)

 

protected void Page_Load(object sender, EventArgs e)

{

    string callBackStr =

        ClientScript.GetCallbackEventReference(this,

                                                "GetArgs()",

                                                "onSuccess",

                                                null,

                                                "onFailed",

                                                true);

 

    btnAdd.Attributes["onclick"] = callBackStr;

    btnSub.Attributes["onclick"] = callBackStr;

    btnMulti.Attributes["onclick"] = callBackStr;

    btnDiv.Attributes["onclick"] = callBackStr;

}

 

int num1, num2;

string method;

 

#region ICallbackEventHandler Members

 

public void RaiseCallbackEvent(string eventArgument)

{

    // 2:4:Add

    string[] args = eventArgument.Split(':');

 

    num1 = int.Parse(args[0]);

    num2 = int.Parse(args[1]);

    method = args[2];

}

 

public string GetCallbackResult()

{

    if (method == "Add")

        return (num1 + num2).ToString();

 

    if (method == "Sub")

        return (num1 – num2).ToString();

 

    if (method == "Multi")

        return (num1 * num2).ToString();

 

    if (method == "Div")

        return (num1 / num2).ToString();

 

    return string.Empty;

}

 

#endregion

היכרות עם ScriptManager

ראינו עד עכשיו עבודה עם AJAX בצורה עצמאית ועבודה עם ICallbackEventHandler, עבודה עם AJAX בצורה עצמאית היא לא נוחה ועבודה עם ICallbackEventHandler היא ישנה וכמובן שזה לא הגיוני לשלוח ולקבל רק מחרוזות ובנוסף בכל ריצה לשרת יש הפעלה של Page_Load (אע"פ שהמשתמש לא שם לב לזה).

ScriptManager הוא התשובה העדכנית והנכונה לעבודה עם AJAX בסביבתASP.NET בעזרתו נוכל גם להפעיל מתודות בצורה פשוטה וקלה ובנוסף נוכל להחליט על חלקים מהדף שיעבדו עצמאית מול השרת ולא יגרמו ל – Post Back מלא אלא ל – Partial Post Back כפי שנראה בהמשך, כמו כן הוא מכיל הרבה ספריות Java Script שניתן להשתמש בהם וזה יכול לחסוך הרבה עבודה, במדריך זה נתייחס רק לאספקטים לעבודה עם  AJAX ולא לספריות שהוא מביא איתו.

תחילת העבודה

כדי להתחיל לעבוד עם ScriptManager עליכם להוסיף (בתחילת העמוד) את הקוד הבא

<asp:ScriptManager ID="sm1" runat="server"></asp:ScriptManager>

 

בדף יכול להיות רק מופע אחד של ScriptManager וכמו כן זה אמור להופיע בתחילת העמוד (הפקד הראשון לאחר התגית form)

 

הפעלה של WebService בעזרת ScriptManager
(הקוד המלא נמצא ב –
ScriptManager\InvokeWebService)

כדי להפעיל מתודות של WebService בעזרת ScriptManager צריך לרשום את ה – WebService ל – ScriptManager ואז אפשר להשתמש בפונקציות של ה – WebService בצורה רגילה (כאילו הם מתודות שמוגדרות ב – Java Script)

נניח שיש לנו WebService בשם Calc שיש לו את המתודה הבאה:

[ScriptMethod]

[WebMethod]

public int Add(int num1, int num2)

{

    return num1 + num2;

}

 

חשוב כמובן לזכור של – WebService חייב להיות את ה – Attribute של ScriptService (אחרת זה לא יעבוד)

כעת בצד הלקוח נרשום את הקוד הבא:

<asp:ScriptManager ID="sm" runat="server">

    <Services>

        <asp:ServiceReference Path="~/Calc.asmx" />

    </Services>

</asp:ScriptManager>

 

הרישום של ה – Service יוצר קובץ Java Script שיורד ללקוח ועוטף את הפונקציות הקימות ב – Service

כעת ניתן בצד הלקוח להפעיל את מתודות ה – Service בקריאה למתודה בשם המלא שלה – כלומר – שם ה – namespace שם ה – class ושם המתודה.

 

דוגמא:

<input type="text" id="txt1" />

<input type="text" id="txt2" />

 

<input type="button" value="Add" onclick="Run()" />

 

 

function Run() {

    var num1 = document.getElementById('txt1').value;

    var num2 = document.getElementById('txt2').value;

 

    InvokeWebService.Calc.Add(num1, num2, onSuccess, onFailed)

}

 

function onSuccess(res) {

    alert(res);

}

 

function onFailed(res) {

    alert(res.get_message());

}

 

את המתודה מפעילים בעזרת ה – namespace המלא שם המחלקה ושם המתודה, שולחים למתודה את:

·        הפרמטרים שהיא אמורה לקבל.

·        שם של מתודה להפעלה במידה והכול יצליח.

·        שם של מתודה להפעלה במידה ויהיה exception.

במתודת onSuccess הפרמטר res יהיה ה – return value של הפונקציה, בדוגמא שלנו החזרנו int אבל כמובן שאפשר להחזיר כל אובייקט ולעבוד איתו בצד הלקוח.

במידה ונגיע למתודה onFailed נקבל אובייקט שיכיל מידע אודות השגיאה, בדוגמא השתמשנו ב – res.get_message, שזה מקביל למאפיין Message בצד השרת לאובייקט מסוג exception, האובייקט שנקבל במידה ויש שגיאה בצד השרת מכיל מידע מועיל, הנה חלק מהדברים החשובים:

·        get_message – מחזיר את הודעת השגיאה.

·        get_exceptionType – מחזיר אס סוג השגיאה.

·        get_stackTrace – מחזיר את ה – Stack Trace כמו בצד השרת

·        get_statusCode – מחזיר את מספר השגיאה (HTTP)

 

אפשר לכתוב את המתודה בצורה יותר קריאה (לדעתי) בעזרתanonymous function

InvokeWebService.Calc.Add(num1, num2,

    function (res) {

        alert(res);

    },

    function (res) {

        alert(res.get_message());

    });

 

בנוסף אפשר להחליף את השימוש ב –

document.getElementById('txt1').value;

 

ל –

$get('txt1').value;

 

(הקיצור של $get מגיע מהספריות של ScriptManager.)

 

הפעלה של מתודות בדף (PageMethods) בעזרת ScriptManager
(את הקוד המלא ניתן למצוא
ScriptManager\ InvokePageMethod)

לפעמים זה מרגיש לא נכון לכתוב מתודה ב – WebService רק כדי לפנות אליה ב – AJAX, בדרך כלל במקרים בהם נרצה לפנות לשרת כדי לבצע לוגיקה ששייכת ספציפית לדף שבה נמצאים כרגע, ניקח לדוגמא את הבדיקה האם שם משתמש תפוס שממשנו בעזרת ICallbackEventHandler נניח שנרצה להפעיל אותה בעזרת ScriptManager אבל לא נרצה למקם את הפונקציה בתוך WebService אלא בתוך הקוד של הדף.

נוכל לבצע את זה בעזרת מנגנון מיוחד של ScriptManager שנקרא PageMethods, מה שצריך לעשות זה את השלבים הבאים:

·        להגדיר ל – ScriptManager את המאפיין EnablePageMethod ל – true

·        להגדיר מתודות סטטיות בדף ולתת להם את ה – WebService Attribute

דוגמת קוד

בצד השרת נכתוב את המתודה הבאה:

[WebMethod]

public static bool IsUserExsit(string name)

{

    return name == "shlomo";

}

 

בצד הלקוח נכתוב את הקוד הבא:

<asp:ScriptManager ID="sm1"

                    runat="server"

                    EnablePageMethods="true">

</asp:ScriptManager>

 

<input type="text" id="txt" onkeyup="checkName(this)" />

 

function checkName(txt) {

    PageMethods.IsUserExsit(txt.value,

        function (res) {

            if (res) {

                txt.style.backgroundColor = 'red';

            }

            else {

                txt.style.backgroundColor = ";

            }

        },

        function (res) {

            alert(res.get_message());

        });

}

 

בתוך הפונקציה checkName אנחנו מקבלים את תיבת הטקסט כפרמטר, נפעיל את המתודה IsUserExsit בעזרת PageMethods נשלח כפרמטר את:

·        הערך שיש בתיבת הטקסט.

·        פונקציה להפעלה בזמן הצלחה – הפונקציה מקבלת את res שזה הערך המוחזר.

·        ופונקציה להפעלה בזמן כישלון.

במידה והקוד עם ה – anonymous function מבלבל אתכם, כך זה יראה בצורה האחרת:

function checkName2(txt) {

    PageMethods.IsUserExsit(txt.value, onSuccess, onFailed);

}

 

function onSuccess(res) {

    if (res) {

        $get('txt').style.backgroundColor = 'red';

    }

    else {

        $get('txt').style.backgroundColor = ";

    }

}

 

function onFailed(res) {

    alert(res.get_message());

}

 

העבודה עם הפעלה של מתודות בעזרת ScriptManager תהיה תמיד אסינכרונית מה שלפעמים יכול לגרום לבעיה, למשל עם נרצה להפעיל את ה – PageMethods מתוך CustomValidator לא נוכל מכיוון שעד שהערך יחזור מהשרת הפונקציה תסתיים וזה יחשב כאילו שהולידצייה הצליחה, אפשר לפתור את זה אבל זה נושא עמוק יותר, ולכן לא נכנס לזה במדריך הזה אבל אפשר למצוא לזה פיתרון בפוסט הזה.

 

עבודה עם UpdatePanel

לאחר שראינו כמה שיטות עבודה כדי לפנות לשרת ולהפעיל מתודה אנחנו מגלים שחסר לנו משהו בסיסי ביותר, הכול היה נחמד וטוב אם כל מה שאנחנו רוצים זה לפנות לשרת לשלוח מספר פרמטרים ולקבל תשובה, אולי אפילו לקבל אובייקט ואז ולהתאים את תוכן הדף בעזרת האובייקט.

אבל מה יקרה אם אנחנו רוצים לעשות דברים מורכבים כמו למשל דפדוף בגריד, אם ננסה לכתוב מתודה בשרת שתקבל את מספר הדף ותחזיר מערך של אובייקטים עם המידע שצריך להיות כרגע בגריד נצטרך לעבוד די קשה ולכתוב הרבה קוד Java Script כדי לשנות את הגריד למידע העדכני.

כדי לפתור את זה הומצא הרעיון של Partial Post Back כלומר לעשות Post Back לשרת אבל לא Post Back רגיל אלא חלקי – מה הכוונה – בדוגמא עם הגריד אנחנו יכולים לעטוף את הגריד בפקד מיוחד שנקרא UpdatePanel וכל Post Back שיהיה מתוך ה – UpdatePanel (בדוגמא שלנו – דפדוף בגריד) תפנה לשרת ב – AJAX, השרת יפעיל את Page_Load וכל התהליך הרגיל (עם כמה שינויים) אבל הלקוח יקבל רק את ה – html הרלוונטי לאותו Update Panel, אמנם לא חסכנו ביצועים מול השרת אבל נתנו חווית משתמש נעימה יותר.

מה שצריך לעשות זה בסך הכול לעטוף את מה שנרצה ב – Update Panel בצורה הבאה. (את הדוגמא המלאה עם הדפדוף ניתן למצוא ב – UpdatePanelDemo/ Pageing.aspx

כמובן שצריך להוסיף ScriptManager בעמוד.

<asp:UpdatePanel ID="udp1" runat="server">

    <ContentTemplate>

        <!– Controls that do PostBack –>

    </ContentTemplate>

</asp:UpdatePanel>

 

כשתריצו את הדוגמא של ה – Paging תגלו חוויית משתמש שלא הכרתם בזמן הדפדוף.

יש המון מה לדבר על Update Panel אני לא ארחיב כאן במדריך, אני רק אציין כמה נקודות שצריך ללמוד אותם ולשים לב אליהם.

·        אי אפשר לשנות בעמוד נתונים מחוץ ל – Update Panel במידה וה – Post Back התרחש מתוך ה – Update Panel, נניח שיש לחצן בתוך Update Panel ותיבת טקסט מחוץ ל – Update Panel, במידה ובקוד של הלחצן (בצד השרת) ננסה לשנות את התוכן של תיבת הטקסט, כשילחצו על הלחצן התוכן של תיבת הטקסט לא ישתנה.

·        במידה ויש כמה  Update Panel בעמוד כל Post Back של אחד מה – Update Panel מעדכן את כולם אלא אם כן המאפיין UpdateMode (האחרים – לא זה שמתוכו נהיה Post Back) הוגדר כ – Conditional ואז כדי לעדכן אותו כשה – Post Back הוא מ – Update Panel  אחר צריך לקרוא למתודת Update שלו. יש בכל הנושא הזה (מתי Update Panel מתעדכן) הרבה כללים, לקריאה נוספת.

·        אי אפשר להשתמש ב – FileUpload בתוך UpdatePanel (רק בעזרת הפקד של AjaxControlToolKit – נראה בהמשך)

·        יש נושא שנקרא Trigger שמאפשר ל – Update Panel לעשות Post Back מלא אם הוא צריך או לאפשר ל – Update Panel להתעדכן כש – Update Panel  אחר עושה Post Back גם אם הוא הוגדר כ – Conditional.

 

 

 

 

 

 

היכרות עם AjaxControlToolKit

ספריית הפקדים שמגיעה עם ASP.NET לעבודה עם AJAX היא די קטנה, יש את ה – Script Manager ואת Update Panel ועוד שני פקדים שכמעט ולא משתמשים בהם
(
Update Progress ו – Timer), כדי לפצות על כך יש למיקרוסופט ב – CodePlex פרויקט שנקרא   AjaxControlToolKitהפרויקט הוא אוסף נרחב של פקדים שעובדים ב – AJAX, אחד הדוגמאות הטובות ביותר היא הפקד Calendar של ASP, הוא אחד הפקדים ה"לא טובים" ביותר של מיקרוסופט מכיוון שכל בחירה בתאריך או תזוזה של חודש גורמת ל – Post Back מלא, התחליף שלו ב – AjaxControlToolKit הוא מדהים אין Post Back בכלל והכול קורא ב – AJAX. אפשר להסתכל על כל הפקדים שלו ולהבין איך עובדים איתם באתר הדוגמאות שלהם.

 

כדי להתחיל לעבוד איתם צריך לעשות את השלבים הבאים:

·        הורדת ה  – dll מהאתר שלהם (בלינק למעלה)

·        הוספת הקוד הבא בקונפיג:

<add namespace="AjaxControlToolkit"

    assembly="AjaxControlToolkit"

    tagPrefix="ajax" />

 

·        במידה והורדתם את הגרסה החדשה ביותר אי אפשר להשתמש ב – ScriptManger הרגיל אלא צריך להשתמש בתחליף שלהם ולכתוב בכל מקום את הקוד הבא:

<ajax:ToolkitScriptManager

 

 

 

זהו קשה להאמין אבל המדריך מגיע לסיומו מקווה שעזר לכם להכיר את העולם המופלא של AJAX וזה יעשה לכם את החיים קלים יותר, במידה ויש לכם שאלות/הערות/הארות תוכלו ליצור איתי קשר דרך הבלוג שלי בדף צור קשר, או דרך מכללת סלע שם אני מלמד.

 

שלמה גולדברג

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *

16 תגובות

  1. Danny Michaeli27 באוקטובר 2010 ב 20:02

    מדריך מושקע מאוד!!
    שאלה: מה לגבי הביצועים של כל אחת מהשיטות המתוארות?

    הגב
  2. happyZZR140028 באוקטובר 2010 ב 16:55

    שיחקת אותה בגדול
    חזק ואמץ

    הגב
  3. הפנר5 בנובמבר 2010 ב 8:46

    עוד לא התעמקתי בתוכן המאמר , אולם אני בטוח שמאמר זה ישמש אותי כבסיס לפיתוח בעתיד
    זהו המאמר המעמיק הראשון בעברית שאני רואה בכלל בנושא
    תודה וחזק ואמץ

    הגב
  4. Rotem Bloom13 בנובמבר 2010 ב 8:20

    שלמה,
    השאלה של דני לגבי ביצועים מאוד רלוונטית ונכונה וזו בדיוק הבעיה שמשתמשים ב-AJAX של .NET.
    הדרכים לקריאות AJAX שמיקרוסופט מספקת אכן קלות למימוש ועושות את העבודה אבל הדגש על ביצועים שם הוא לצערי מאוד נמוך ולא יעיל. אומנם זה משתפר מגירסה לגירסה או לפחות מיקרוסופט מאפשרת לעשות קריאות בדרכים שונות ויעילות יותר אבל זה עדיין לא זה למי שרוצה לעבוד ב-AJAX עם דגש על ביצועים גבוהים.

    לא סתם בחרה מיקרוסופט לשלב JQUERY כתשתית JAVASCRIPT לגירסה חדשה של VS 2010.

    ההמלצות שלי לגבי ביצועים הם:
    1. AJAX לעבוד עם JQUERY ולא עם התשתית של מיקרוסופט כלומר לוותר על: SCRIPTMANAGER, UPDATE PANEL וכול ספריות ה-JS של מיקרוסופט.
    2. ספריות AJAX UI – לוותר על AJAX TOOLKIT היא ממש על הפנים ולעבוד עם JQUERY UI או כול ספריית UI איכותית אחרת כגון: MOO TOOLS, YAHOO UI ויש עוד רבים.
    3. תפיסת קריאות AJAX – עדיף לעשות קריאה ל-WEB METHOD אבל באמצעות JQUERY או לממש HTTP HANDLER שיתפוס את הקריאות. לתפוס קריאות עם PAGE שמאחוריו כול ה-LIFE CYCELE וה-EVENTS פחות מומלץ.
    4. להשתדל לעבוד עם JSON כפרוטוקול להעברת מידע הוא הכי יעיל ונוח לעבודה עם JAVASCRIPT.

    כמובן שנושא ביצועים הוא מורכב ויש בו עוד המון פרמטרים אבל זה ככה על קצה המזלג לגבי AJAX.

    רותם

    הגב
  5. איציק30 בינואר 2011 ב 22:50

    מה עם שליחת הפרמטרים בעברית, איך מקודדים נכון?
    כל האפשרויות שניסיתי לא צלחו..

    הגב
  6. אלי שטיינמן1 במאי 2011 ב 9:51

    מאמר מצויין! גם טוב וגם כתוב טוב. תודה! עכשיו יש לי קצת סדר בבלאגן.

    הגב
  7. שלומי26 ביוני 2011 ב 0:39

    מאמר מעולה.עשה המון סדר בבלאגן בכל הקשור ל ajax.תודה רבה

    הגב
  8. אבישי7 באוגוסט 2011 ב 9:52

    מדריך מקיף, מושקע ומעולה!
    תודה רבה 🙂

    הגב
  9. אורי2 בספטמבר 2011 ב 1:20

    כל הכבוד רמה גבוהה מודה לך

    הגב
  10. Oracle8 בפברואר 2012 ב 13:19

    באקספלורר זה עובד רגיל

    הגב
  11. תמר10 במאי 2012 ב 13:20

    יישר כח! תזכה למצוות! ההסברים שלך ממש מפשטים דברים מורכבים.

    הגב
  12. רחל9 ביולי 2012 ב 9:56

    יש לי שאלה קטנה, ה WebService וכל המימוש שלו יושב באותו פרויקט של כל הדפי ASPX, HTM ? או רק בדוגמה שניתן להוריד זה כך.
    כי אני הפרדתי את זה לשתי פרויקטים אחד WebSites ואחד WebService וניסתי לכתוב את הקוד שאתה מסביר פה וה xhr.readyState כל הזמן =1, יש לך פתרון לזה?
    תודה רבה.

    הגב
    1. נחמה15 במאי 2016 ב 23:30

      נכון שכבר עבר המון זמן מאז ששאלת, אבל גם אני "תקועה" שם,
      האם יש לך פתרון?
      תודה רבה

      הגב
  13. zag7819 בינואר 2014 ב 0:07

    ועכשיו לשאלתי:
    בדוגמה הראשונה אתה יוצר handler בשם ProcessRequest,
    אך אתה לא מתייחס לפונקציה זו או קורא בשמה בהמשך.
    איך פונקציית הלקוח תדע לקרוא לפונקציה זו?

    הגב
  14. tcdsvu8 במאי 2014 ב 10:26

    איך קוראים לפונקציה של wcf application servies מ ajax?
    ע"י post & json

    הגב