DCSIMG
May 2012 - Posts - שלמה גולדברג (הרב דוטנט)

שלמה גולדברג (הרב דוטנט)

מרצה בסלע ויועץ בעולם ה - net.

May 2012 - Posts

override alert function

בעבר כתבתי כיצד לדרוס את ההתנהגות הדפולטבית של parseInt, הפעם אני רוצה להראות איך קוד פשוט ישדרג את ה – alert הסטדנרטי של הדפדפן לשימוש ב – jquery ui dialog

בדרך כלל הקוד הבא:

<input type="button" value="Show" onclick="alert('is jquery ui dialog ?')" />

לחיצה על הלחצן תציג את ההודעה הבאה:

image

במידה ונכתוב את הקוד הבא: (בתחילת העמוד)

<script src="js/jquery-1.7.1.min.js" type="text/javascript"></script>

<link href="js/jquery-ui-1.8.18.custom.css" rel="stylesheet" type="text/css" />

<script src="js/jquery-ui-1.8.18.custom.min.js" type="text/javascript"></script>

<script>

    alert = function (text) {

        $('<div>' + text + '</div>').dialog({

            modal: true,

            title: 'Message'

        });

    }

</script>

כעת לחיצה על הלחצן תציג:

image

Silverlight 404 - mimetype

בפרוייקט מסויים שאני עוזר שם, יש דף silverlight (במאמר מוסגר, לדעתי האישית - silverlight היא אחת מהטכנולוגיות הטובות ביותר לפיתוח - ו"חבל על דאבדין ולא משתכחין" שהחברים האויבר חכמים ממחלקת השיווק במייקרוסופט החליטו שלא צריך יותר silverlight)
 
בכל מקרה כשגלשו לדף ה - aspx שהיה אמור להחיל בתוכו את ה - silverlight היינו מקבלים דף ריק.
 
הדבר הראשון שעשיתי היה לפתוח fiddler - וראיתי שלמעשה הבקשה לקובץ ה - xap מחזירה 404, מה שטיפה היה בעייתי מכיוון שפיזית זה כן ישב שם.
 
לאחר קצת שיטוט בגוגל, הבנתי שצריך להגידר את ה - mime type המתאים, שאלתי את המפתחים והם טענו שהם הוסיפו אותו בקונפיג, הם שלחו לי את הקוד הבא:
 

<staticConten>

  <remove fileExtension=".xap"/>

  <mimeMap fileExtension=".xap" mimeType="application/x-silverlight-app"/>

</staticConten>

 
הסברתי להם שה - section של staticContent יושב תחת system.webServer שרלוונטי רק מ - IIS7 ומעלה, ולא ב - IIS6, מה שיש להם.
 
בקשתי מהם להוסיף את ההגדרה מתוך IIS, כפי שמופיע בצילומי מסך כאן.
 
לאחר שעשו זאת, אכן גלישה לדף הביאה את ה - silverlight.

the installed product does not match the installation source

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

the installed product does not match the installation source(s), until a matching source is provided or the installed product and the source are synchronized, this action cannot be performed
 
 
 
זה יכול להיות לפעמים אם עוצרים את תוכנית התקנה בעזרת ה – Task Manager כי היא נתקעת וכד'.
 
ראיתי פתרון נחמד כאן.
 
MsiExec.exe /I "Install.msi" REINSTALLMODE=voums REINSTALL=ALL
 
 
כשכמובן יש לכם את ה – MSI המקורי, אבל אתם לא מצליחים להסיר אותה מ – Add or remove programs, שורת הפקודה תדע להסיר אותה בכל זאת.

שליחת מקשי מקלדת לחלונות אחרים מתוך תהליכים

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

var p1 = Process.GetProcessById(2516);

var p1 = Process.GetProcessesByName("Calcaulator")[0];

 
 
במידה ואתם רוצים למצוא את החלון לפי הכותרת שלו, תוכלו לכתוב את הקוד הבא:
 

[DllImport("user32.dll")]

private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

 
 
וכעת בקוד:
 

var myWindowHandle = FindWindow(null, "[Window Title]");

 
 
ההבדל, שבמידה ונשתמש עם Process נצרך לאחר מכן לגשת למאפיין MainWindowHandle, והמתודה FindWindow מחזירה כבר את ה – Handle.
 
 
כעת נכתוב את הקוד הבא:
 

SetForegroundWindow(p1.MainWindowHandle);

SendKeys.SendWait("10");

SendKeys.Flush();

 
 
צריך להוסיף System.Windows.Forms.dll (במידה ואתם לא נמצאים באפליקציית Win Forms), צריך לדאוג שהחלון החלו יהיה החלון שכרגע בפוקוס – כדי שיהיה לנו את המתודה המתאימה נוסיף את הקוד הבא:
 
 

[DllImport("user32.dll")]

private static extern bool SetForegroundWindow(IntPtr hWnd);

 
 
וכעת ניתן לשלוח כל מקש שנרצה לחלון המתאים.
 
נקודה קטנה – במידה ואתם בסביבת Win Application, ניתן לקרוא למתודת Send במקום מתודת SendWait.

מדריך מקוצר ל - ClickOnce חלק 3 מתוך 3

בפוסט הראשון ראינו בקצרה כיצד ניתן לייצר Click Once Deployment, בפוסט השני ראינו כיצד להשתמש עם ה – API שלהם בכדי לייצר התקנה שתייבא את ה – Click Once Application (אמנם ראינו את הקוד ב – Console Application – אבל הרעיון היה ברור).
 
כפי שהבטחתי, הפעם נראה כיצד נגרום לעדכונים אוטומטיים עבור האפליקציה שלנו.
 
דוגמת הקוד נלקחה מה – MSDN.
 
זה יקרה למעשה מתוך האפליקציה עצמה. (כלומר אותה אפליקציה שהותקנה בעזרת Click Once)
 
נוכל כמובן להפעיל את הקוד בעזרת פעולה יזומה של המשתמש (Check for Update), או שנוכל לבדוק כל פעם כשהאפליקציה נפתחת או בכל זמן אחר.
 
ראשית נצטרך לקבל את המופע של המחלקה ApplicationDeployment בצורה הבאה:
 

ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;

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

ad.CheckForUpdateCompleted += ad_CheckForUpdateCompleted;

ad.CheckForUpdateProgressChanged += ad_CheckForUpdateProgressChanged;

 

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

ad.CheckForUpdateAsync();

 
כעת במתודת תהליך התקדמות בדיקת העדכון, נוכל להציג מידע למשתמש מה קורה.
 

void ad_CheckForUpdateProgressChanged(object sender, DeploymentProgressChangedEventArgs e)

{

 

}

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

void ad_CheckForUpdateCompleted(object sender, CheckForUpdateCompletedEventArgs e)

{

 

}

 
 
 
 
גם כאן המשתנה e יכיל מידע בעל ערך, כמו למשל e.Error שיגיד לנו האם היה שגיאה e.Cancelled, שיגיד לנו האם הבקשה התבטלה, המידע החשוב הוא e.UpdateAvailable שיודיע לנו האם יש עדכון, ו – e.IsUpdateRequired שיעדכן אותנו האם עדכון זה הוא חובה.
 
בסופו של תהליך נכתוב את הקוד הבא: (אם נעבוד בצורה אסינכרונית)
 

ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;

 

ad.UpdateCompleted += ad_UpdateCompleted;

ad.UpdateProgressChanged += ad_UpdateProgressChanged;

 

ad.UpdateAsync();  

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

מדריך מקוצר ל - ClickOnce חלק 2 מתוך 3

בהמשך לפוסט הקודם, שדיבר על יצירת התקנה מסוג Click Once, נראה הפעם כיצד להשתמש ב - API כדי לממש מנגנון כזה לבד - כלומר להשתמש במנגנון המובנה של net ולייצר UI משלנו למנגנון.
 
 
הקוד שאכתוב כאן והדוגמאות יהיו ב - Console Application, אבל כמובן שבדרך כלל כשנרצה מנגנון משלנו ל - Click Once יהיה (בעיקר) כדי לממש UI לבד.
 
 
למעשה ה - API מחולק לשניים, הפעם הראשונה בה אנחנו מתקינים את התוכנה, והפעמים האחרות בהם אנחנו בודקים עידכונים, בפוסט זה נדבר על הפעם הראשונה, ובפוסט הבא נדבר על העידכונים.
 
הדוגמאות קוד מגיעות מה - MSDN (שם יש דוגמת קוד מלאה - עם טיפול בשגיאות וכדו' - פוסט מומלץ ביותר)
 
 
לאחר שנעשה publish, לאפליקציה שלנו (כפי שראינו בפוסט הקודם), נשתמש ב - API כדי לגרום להתקנה שלו.
 
נוכל לייצר אפלייקציה בה יהיה הקוד הבא:
 

static void Main(string[] args)

{

    string deployFile = @"file://[FOLDER]\WindowsFormsApplication2.application";

    Uri deploymentUri = new Uri(deployFile);

    var iphm = new InPlaceHostingManager(deploymentUri, false);

    iphm.GetManifestCompleted += iphm_GetManifestCompleted;

    iphm.GetManifestAsync();

    Console.ReadLine();

}

 
ראשית, נגדיר את ה - URI לקובץ עם סיומת ה - application. (שנוצר כתובצאה מה - publish שעשינו)
 
לאחר מכן נייצר מופע של המחלקה שיודעת להתקין ClickOnce, ונשלח לה כפרמטר את ה - URI וערך שאומר שמדובר גם באפליקציה שמוגדרת לעבוד כ - offline.
 
היות שכל העבודה עם המחלקה הזאת היא אסינכרונית, ראשית נרשם לאירוע של סיום הורדת הקובץ, ורק לאחר מכן נבקש את הקובץ (כמובן שנקרא ל - ReadLine כך שהתוכנית לא תסגר לפני שהכול יסתיים)
 
 
לאחר שקבלנו את הקובץ, נכתוב את הקוד הבא:
 

static void iphm_GetManifestCompleted(object sender, GetManifestCompletedEventArgs e)

{

    // Check for an error.

    if (e.Error != null)

    {

        // do ...

    }

    var iphm = (InPlaceHostingManager)sender;

    iphm.AssertApplicationRequirements(true);

    iphm.DownloadProgressChanged += iphm_DownloadProgressChanged;

    iphm.DownloadApplicationCompleted += iphm_DownloadApplicationCompleted;

    iphm.DownloadApplicationAsync();

}

המשתנה e מכיל הרבה מידע אודות ההתקנה (בדוגמא ב - MSDN, משתמשים במידע הזה הרבה (וכנראה גם בחיים האמיתיים))
 
חייבים להפעיל את פונקציית AssertApplicationRequirements כדי לוודא את דרישות הקדם (הערך true מוודא גם בדיקות הרשאות)
 
נרשם לאירועים של התקדמות ההורדה, וסיום ההורדה - ונתקין את התוכנה.
 
למעשה זהו פחות או יותר, כל מה שנשאר לנו זה לממש UI יפה וחמוד (אחרת ניתן פשוט להשתמש בזה של מייקרוסופט)
 
 
בפוסט הבא נראה כיצד נשתמש ב - API שלהם כדי לקבל עידכוני תוכנה.

מדריך מקוצר ל - ClickOnce חלק 1 מתוך 2

 

סיימנו (או התחלנו לסיים) את פיתוח האפליקציה, ואנחנו רוצים לייצר תוכנית התקנה, בעולם ה - net קיימת בפנינו שני אפשרויות עיקריות, הראשונה היא לייצר msi, שזה נושא בפני עצמו, והשנייה היא להגדיר את האפליקציה שלנו כ - ClickOnce Appliation
 
יש הבדלים שונים בין התקנה בעזרת msi לבין התקנה בעזרת ClickOnce, ההבדל המרכזי ביניהם, היא שהתקנה בעזרת ClickOnce היא יותר פשוטה (עבור המשתמש ועבור המפתח) ויש לה מנגנון עידכונים אוטמטיים כשמחליטים להעלות גרסה חדשה.
 
 
 
 
בפוסט זה נראה כיצד מגדירים ClickOnce בכמה צעדים פשוטים.
 
 
לאחר שסיימנו לפתח את האפליקציה, נלך למאפייני הפרוייקט ונבחר בטאב Publish
 
 
Publish 1
 
 
נגדיר את התיקייה לאן הקבצים יפובלשו. (אין צורך להגדיר את ה - Installation Folder Url)
 
נבחר את Mode ההתקנה (בדרך כלל נעדיף לבחור באופצייה השנייה, בה ניתן להריץ את התוכנה גם במצב offline)
 
נגדיר את גרסת האפליקציה.
 
תחת Appliation Files נוכל לבחור אילו קבצים מתוך הפרוייקט יהיו מפובלשים ואילו לא.
 
תחת Prerequisite נוכל לבחור את דרישות הקדם של האפליקציה (כמו למשל גרסת net וכד')
 
תחת Updates נוכל להגדיר האם יהיו automatic update או לא (ברירת המחדל, לא) ואם כן כל כמה ימים או בכל הפעלה - ועוד הגדרות הקשורות לנושא.
 
תחת Options נוכל להגדיר הגדרות כמו שפה, שם החברה, לינק לאתר שלכם וכד', בנוסף נוכל להגדיר האם לייצר קיצורי דרך על שולחן העבודה ועוד הגדרות שונות.
 
 
למעשה זהו פחות או יותר, לחיצה על Publish Now תייצר תייקיה ובה תיקייה בשם Application Files עם כל הקבצים הנדרשים, קובץ setup וקובץ עם סיומת application המכיל את ההגדרות של Click Once.
 
כעת ניתן להעלות את הקבצים הללו לשרת web כלשהו, כל אחד יוכל להתקין את האפליקציה, במידה והגדרתם auto update, והעליתם גרסה חדשה, האפליקציה תעודכן אוטומטית.
 
 
בפוסט הבא נכיר את ה - api של CliakOnce ונראה כיצד לייצר UI משלנו תוך שימוש במנגנון של ClickOnce.

Text Resource in asp.net mvc

 

 פוסט זה נכתב בעזרתם של תותחי העל נטלי אהפוטה וניב לוי (יהלום של סלע)
 
 
כרגיל בהרבה מקומות בהם אנחנו מפתחים אתרי אינטרנט, רוצים המנהלים שליטה על המחרוזות בלי צורך להזדקק למפתחים, הדרך הרגילה לעבוד עם מחרוזות היא בעזרת קבצי resx, שאיתם כמובן יש בעייה מבחינת העבודה איתם (למי שלא מפתח) - בעבר כתבתי כלי שנותן מענה מסויים, אך בפעם הזאת החליט מי שהחליט שהמחרוזות ישמרו בבסיס נתונים, מה שמביא אותנו לנקודות הבאות:
 
  • המידע בבסיס הנתונים, ויש צורך להגדיר דף בו המנהלים יוכלו לערוך את המחרוזות.
  • שמירת המידע במקום כלשהו בזיכרון, כדי לא לגשת כל הזמן לבסיס הנתונים.
  • אפשרות לגשת למידע מקבצי ה - cshtml.
  • אפשרות לגשת למידע מקבצי java script.

 

המודל נראה כך:
 
Resource Model
 
  • Name - ה - key שדרכו מחפשים את הערך.
  • Comment - טקסט שמסביר למנהלים מה המשמעות של הערך.
  • Description - הערך עצמו.
  • IsUseInJs - האם הערך הזה צריך שתהיה לו גישה גם מצד הלקוח.
כעת הגדרנו משתנה סטטי המחזיר מתוך ה - Application את המידע, במידה והמידע לא קיים טוענים אותו.
 

public static List<Resource> Resources

{

    get

    {

        var list = HttpContext.Current.Application["Resources"];

        if (list == null)

        {

            UserModleEntities context = new UserModleEntities();

            //Detach the context to the object underline - read only with entity frame work;

            context.Resources.MergeOption = MergeOption.NoTracking;

            list = context.Resources.ToList();

            HttpContext.Current.Application["Resources"] = list;

        }

        return list as List<Resource>;

    }

}

 
 
כעת נרצה לאפשר בקלות את הגישה מקבצי ה - cshtml, ולכן כתבנו את הקוד הבא:
 

namespace System.Web.Mvc.Html

{

    public static class HtmlHelpersExtensions

    {

        public static string TextResource(this HtmlHelper helper, string name)

        {

            return ApplicationHelper.Resources.Where(x => x.Name == name).Select(x => x.Description).FirstOrDefault();

        }

    }

}

 
כעת בכל מקום בדף נוכל לכתוב:
 

@Html.TextResource("Welcome")

 
וזה יחזיר את הערך הנכון.
 
 
כפי שהגדרנו, אנחנ רוצים גם גישה מקבצי JS, בעבר פרסמתי משהו דומה עם קבצי resx.
 
נייצר קובץ ashx, ונכתוב את הקוד הבא:
 

public void ProcessRequest(HttpContext context)

{

 

    //Setting cache options

    //TimeSpan -Default cache evrey 60 seconds

    TimeSpan freshness = Properties.Settings.Default.ResourceCacheDif;           

    DateTime now = DateTime.Now;

    context.Response.Cache.SetExpires(now.Add(freshness));

    context.Response.Cache.SetMaxAge(freshness);

    context.Response.Cache.SetCacheability(HttpCacheability.Server);

    context.Response.Cache.SetValidUntilExpires(true);

 

 

    context.Response.ContentType = "application/js";

    //The list of all js resources

    List<Resource> jsListResources = ApplicationHelper.JsResources;

 

    //Constructing the array

    StringBuilder stringBuilder = new StringBuilder();

    stringBuilder.Append("var resourceArray = new Array(); ");

 

    foreach (Resource item in jsListResources)

    {

        stringBuilder.Append(string.Format("resourceArray['{0}'] = '{1}'; ", item.Name, item.Description));

 

    }

 

    context.Response.Write(stringBuilder.ToString());

 

}

 
כשהמאפיין JSResource מחזיר מערך של Resources שהוגדר עבורם UseInJs=true.
 
כעת ב - JS נוכל לכתוב בכל מקום
 

resourceArray.CaratsMandatory

Dynamically loading or removing an external JavaScript or CSS file

 

המקור לקוד בפוסט זה מגיע מכאן
 
לא מזמן כתבתי אתר קטן, שהיה צריך לתמוך במעבר בין עברית לאנגלית, כמובן שזה תמיד כאב ראש ויש כל מיני שיטות לעשות זאת (בעיקר הבעייה עם שינוי הכיוון)
 
 
בפעם הזאת בחרתי להתמודד עם הבעייה בצורה הבאה:
 
כתבתי קובץ css ובו העיצוב משמאל לימין כמו שצריך להיות באנגלית.
 
כתבתי קובץ css נוסף שבו מופיעים כל השנויים מבחינת העיצוב עבור השפה העברית (align, padding, marging, background וכד')
 
 
כעת במעבר לעברית הפעלתי את הפונקציה הבאה:
 

function loadjscssfile(filename, filetype) {

    if (filetype == "js") { //if filename is a external JavaScript file

        var fileref = document.createElement('script')

        fileref.setAttribute("type", "text/javascript")

        fileref.setAttribute("src", filename)

    }

    else if (filetype == "css") { //if filename is an external CSS file

        var fileref = document.createElement("link")

        fileref.setAttribute("rel", "stylesheet")

        fileref.setAttribute("type", "text/css")

        fileref.setAttribute("href", filename)

    }

    if (typeof fileref != "undefined")

        document.getElementsByTagName("head")[0].appendChild(fileref)

}

 
 
ושלחתי את שם קובץ ה - css של העברית, והיות שקובץ הזה ירד אחרון הוא דרס את כל ההגדרות של קובץ האנגלית.
 
במעבר לאנגלית הפעלתי את הקוד הבא:
 

function removejscssfile(filename, filetype) {

    //determine element type to create nodelist from

    var targetelement = (filetype == "js") ? "script" : (filetype == "css") ? "link" : "none"

 

    //determine corresponding attribute to test for

    var targetattr = (filetype == "js") ? "src" : (filetype == "css") ? "href" : "none"

 

    var allsuspects = document.getElementsByTagName(targetelement)

 

    //search backwards within nodelist for matching elements to remove

    for (var i = allsuspects.length; i >= 0; i--) {

        if (allsuspects[i] &&

            allsuspects[i].getAttribute(targetattr) != null &&

            allsuspects[i].getAttribute(targetattr).indexOf(filename) != -1) {

 

            //remove element by calling parentNode.removeChild()

            allsuspects[i].parentNode.removeChild(allsuspects[i])

        }

    }

}

 
כעת חזרו ההגדרות לפי מה שהוגדר באנגלית.
Posted: May 07 2012, 01:49 PM by Shlomo | with no comments
תגים:, , , ,

jquery ajax with async set to false and beforeSend registration

 

לאחרונה הגיעה אלי שאלה מעניינת,
 
אני משתמשת הרבה בקריאות סינכרוניות מקליינט לWCF.
לפני כל קריאה אני משנה את הcursor ל wait.
הבעיה היא שהוא כל כך מהיר שעוד לפני שהוא מספיק להפוך את העכבר – הוא כבר מגיע אל ה WCF ומקפיא את המסך, באופן שהעכבר נשאר כשהיה , עד שהקריאה חוזרת חזרה
(בקריאות א-סינכרוניות כמובן שזה לא קורה)
פתרתי את הבעייה הזו באמצעות
 window.setTimeout(function () { CallWCF(); }, 10);
ואז ה wait cursor מספיק להתבצע לפני שהקריאה נשלחת עקב ההשהייה.
 
האם יש פתרון מוצלח יותר במקום למלא את הקוד ב setTimeout (יש לי  קריאות רבות ל WCF)?
 
הקוד נראה כך (בערך): 
 

function click_click() {

 

 

    $.ajax({

        type: "POST",

        url: "/WebService1.asmx/HelloWorld",

        contentType: "application/json; charset=utf-8",

        dataType: "json",

        async: false,

        beforeSend: function () {

            setWaitCursor('wait');

        },

        success: function (res) {

            setWaitCursor('default');

        },

        error: function (e) {

 

        }

    });

}

 

function setWaitCursor(val) {

    $('*').css('cursor', val);

}

 

 
כעת הבעייה שנשאלת כיצד לגרום למסך לקבל את סימן ה - wait כשמפעילים את השאילתא ב - async:false.
 
בהתחלה, אמרתי שהדבר לא ייתכן - כפי שמופיע בדוקמנטצייה של jQuery (ובכל מקום אחר בגוגל) שבמידה ומדובר בקריאה סינכרונית שום דבר ב - UI לא יכול להשתנות לפני שחוזרים מהשרת - למעשה במידה וניתן היה לקרוא לקוד שהוגדר ב - beforeSend לפני הקריאה ל - $.ajax, היינו מסתפקים עם זה, הבעייה שגם הקוד שהופעל לפני הקריאה לשרת לא באמת התבצע אם היה מדובר בעבודה על ה - UI.
 
 
אבל לאחר חשיבה מאומצת הגעתי למסקנה, שאפשר לעבוד על הדפדפן. בשלבים הבאים:
 
 
ראשית נפעיל קוד מסויים בזמן כל קריאת ajax.
נבדוק האם המפתח מעוניין להריץ קוד בזמן beforeSend.
במידה וכן נבדוק האם הקריאה הוגדרה כסינכרונית.
במידה וכן, נבטל את הקריאה לשרת.
נריץ את ה - beforeSend.
ונריץ מחדש את הקריאה לשרת ללא ה - beforeSend.
 
 
כדי לגרום להריץ קוד מסויים עבור כל קריאת jquery ajax, נשתמש ב - ajaxPrefilter, נכתוב את בלוק הקוד הבא היכן שהוא בדף.
 

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {

 

    // user are register to beforeSend event and define the async property to false

    if (originalOptions.beforeSend && !originalOptions.async) {

 

        // abort the xml http request object

        jqXHR.abort();

 

        // invoke the beforeSend registerd method

        originalOptions.beforeSend();

 

        // reinvoke the ajax call after 10 milisecound (enough time for ui thread)

        setTimeout(function () {

            // remove the beforeSend event

            originalOptions.beforeSend = null;

 

            // call to server

            $.ajax(originalOptions)

        }, 10);

    }

});

 
 
כעת כל קריאה ל - ajax (בעזרת jquery) תעבור דרך קטע הקוד שצויין, במידה ונרשמו ל - beforeSend וגם הגדירו שהפעולה תהיה סינכרונית, ראשית יבוצע ה - beforeSend, ורק לאחר כ - 10 מילישניות תקרא הפונקצייה מחדש, כשכמובן הקריאה ל - beforeSend נמחקת (אחרת תהיה רקורסייה אינסופית)

חלוקת האתר לכמה חלקים וכיצד לעדכן את השרת מהיכן הגיעה הקריאה אליו

לא מזמן התבקשתי לעזור לתכנן אפליקציית אינטרנט בה המסך יכול להיות מחולק לכמה חלקים, והמשתמש יכול לגלוש באותה אפליקצייה מכל חלק - כך שהוא יוכל לראות כמה חלקים שונים של אותה אפליקצייה, באותו מסך.
 
במידה שהיינו מתחילים לכתוב את האפליקצייה מאפס, כנראה שהיינו בוחרים ב - Single Applcation Page וכל האתר היה עובד ב - ajax, וכך לא היה שום בעיה לחלק את המסך לשניים או יותר חלקים.
 
הבעייה שהאתר כבר היה כתוב :-)
 
במקרה הזה לאחר חשיבה הגענו למסקנה שהשיטה הכי יעילה, היא לחלק את העמוד הראשי לכמה iframes שהמשתמש יוכל לנווט בכל חלק מבלי לגרום ל - post back לכל החלקים האחרים.
 
כאן הגענו לבעייה חדשה, היות שמדובר בטאב אחד - כל ה - iframes חולקים את אותו session, ואין שום דרך בצד השרת לדעת מאיזה חלק הגיע ה - post back, והיות שהרבה מידע עבור המשתמש היה נשמר ב - session - כמו בחירות של תפריטים וכדו', נניח שהמשתמש היה בוחר בחירה מסויימת באחד החלקים, והוא היה הולך לחלק שני ועושה משם post back, השרת היה מתייחס לבחירה של המשתמש מהחלק האחר.
 
לאחר חצי יום נסיונות כתבתנו את הקוד הבא:
 

<table>

    <tr>

        <td><iframe src="WebForm2.aspx" data-side="LeftTop"></iframe></td>

        <td><iframe src="WebForm2.aspx" data-side="RightTop"></iframe></td>

    </tr>

    <tr>

        <td><iframe src="WebForm2.aspx" data-side="LeftButtom"></iframe></td>

        <td><iframe src="WebForm2.aspx" data-side="RightButtom"></iframe></td>

    </tr>

</table>

 
 
לכל iframe הוספנו attribute בשם data-side עם ערך שמכיל היכן פיזית הוא יושב בדף (data הינו תוספת של html5 כך שמאפיינים שאינם בתקן יוכלו להתווסף לאלמנטים)
 
כעת כל iframe מכיל את המידע היכן הוא יושב, כעת נשאר למצוא דרך לספר את זה לשרת, מבלי צורך לשנות את כל הקוד הקיים, גילינו שכל הדפים שה - iframe מצביע אליהם משתמשים באותו master page, הוספנו את הקוד הבא:
 

<script>

 

    $(window).bind('beforeunload', function () {

        $(parent.document).find('iframe').each(function (index, value) {

            if (value.contentWindow == window) {

                var side = $(value).attr('data-side');

                $.cookie('side', side);

            }

        });

    });

</script>

 
נרשמנו לאירוע beforeunload שיקרה כל פעם לפני כל post back ולפני עזיבת העמוד (על ידי לינק לדף אחר וכד'), נמצא את העמוד שמכיל את כל ה - iframes בעזרת הפיכת ה - document של ה -  parent לאובייקט jquery, נחפש את כל ה - ifrmae ונרוץ עליהם בלולאה.
 
נבדוק האם החלון שנמצא בתוך ה - ifrmae הוא אכן החלון שלנו (החלון שמתוכו מתבצע ה - post back)
 
במידה וכן, נוציא את הערך שנמצא במאפיין data-side (ניסינו בהתחלה להשתמש בפונקציית data של jquery בצורה הבאה:

var side = $(value).data('side');

הבעייה שלאחר מספר post back איכשהו לא קבלנו את המידע הנכון - ולכן חזרנו ל - attr הישן והטוב)
 
כעת שמרנו את הערך ב - cookie בשם side (היות שבכל request כל ה - cookies נשלחים לשרת).
 
 
בצד השרת הגדרנו enum שנראה כך:
 

public enum Side

{

    LeftTop,

    RightTop,

    LeftButtom,

    RightButtom

}

 
בקוד הוספנו ב - page_load (של מחלקת ה - PageBase  - המחלקה שכל הדפים בפרוייקט יורשים מהם) את הקוד הבא:
 

HttpCookie sideCookie = Request.Cookies["side"];

if (sideCookie != null)

{

    Side side = (Side)Enum.Parse(typeof(Side), sideCookie.Value);

}

 
כעת ניתן היה לשאול בכל מקום בקוד מהיכן נעשה ה - post back. (ספציפית שם בפרוייקט הוספנו מנגנון שכל שמירה ב - Session תעבור דרך פונקצייה שמשתמשת ב - side כדי לשמור את ה - session לאותו side (כלומר הוספנו את ה - ToString של המשתנה side לשם ה - session) כך שניתן תמיד להוציא את המידע הרלוונטי לאותו side)
 
 
אחרי קצת דיבגינג גילינו שחלק מה - post back לא מגיעים לקוד ה - beforeunload, בדיקה קטנה גילתה שמדובר ב - post back מתוך update panel, מה שגרם לנו להוסיף את הקוד הבא:
 

<script>

    var prm = Sys.WebForms.PageRequestManager.getInstance();

    prm.add_beginRequest(function (requestManager, args) {

        saveSideInCookie();

    });

 

</script>

 
כמובן שהוצאנו את הקוד מלמעלה לפונקצייה שנקראת saveSideInCookie, חשוב בנוסף לשים לב שהסקריפט הזה צריך לשבת אחרי ההגדרה של ה - Script Manager.
 
הקוד הזה יגרום לכך שכל async post back ישמור את המידע ב - cookie.

Introduction to ASP.NET MVC 3 - Part 3

 

בהמשך לפוסטים שיכניסו אתכם לעולם ה - Asp.net MVC, נמשיך לפתור תרגילים כדי ללמוד את הטכנולוגיה.

תרגיל מספר 4 - הודעות שגיאה על ערכים בלתי תקינים.
אמנם בפרק זה אין נגיעה ממשית ב - asp.net mvc, אך היות שסדרה זו מיועדת לנכנסים לעולם ה - web ללא רקע מוקדם, חלק מהתרגילים יגעו בעולמות שונים מעולם ה - web.
 
תיאור התרגיל:
הצגת הודעות שגיאה למשתמש בעזרת אנימצייה של הודעה (בסגנון ההודעות של Gmail).
 
מטרת התרגיל:
עבודה עם css, עבודה עם jQuery. עבודה עם timers.

שלבים:
  • הוסיפו לדף ה - Layout.cshtml אלמנט מסוג div שימורכז לאמצע המסך ויוצמד למעלה (בעזרת css – שימוש ב – position, עצבו אותו כך שיראה דומה למה שמוצג בגוגל).
  • הגדירו לו css שיסתיר את האלמנט.
  • כתבו פונקציית JS המקבל טקסט, ועושה את הפעולות הבאות:
    • בעזרת jQuery מוצא את ה – div.
    • כותב לתוכו את הטקסט המתקבל.
    • משתמש בפונקצית slideDown כדי להציג אותו באנימצייה.
    • לאחר חמש שניות (בעזרת setTimeout) מסתיר את האלמנט עזרת שימוש ב – slideUp)
  • שנו את מימוש דף הלוגין, שבזמן לחיצה על הלחצן – אם חסר ערכים שלא יציג alert אלא ישלח טקסט לפונקצייה שכתבתם זה עתה.
 
 
כפי שכבר כתבתי, פוסט זה ואחרים בסדרה זו, יכניסו אתכם לעולם פיתוח ה - web בהתבסס על asp.net mvc, והיות שבטכנולוגייה זו משתמשים הרבה ב - javascript ו - jquery חלק מהתרגילים יכללו תירגול בנושא זה.
 
אז לפיתרון. (את הקוד המלא ניתן להוריד כאן)
 
 
נוסיף את קטע קוד ה - html הבא בדף המסטר. (מיד לאחר תגית ה - body)
 
 

<div id="msg">

</div>

 
כעת נוסיף קטע css שישפיע על האלמנט msg
 

<style>

    #msg

    {

        position: absolute;

        width: 220px;

        background-color: Yellow;

        border: 1px solid black;

        left: 50%;

        margin-left: -110px;

        padding: 2px;

        font-family: Arial;

        border-radius: 0px 0px 5px 5px;

        text-align: center;

        top: 0px;

        border-top-style: none;

        display: none;

    }

</style>

 
ונכתוב את קוד ה - js שיציג את המידע בתוך האלמנט שיגיע עם אנימציה, ולאחר כמה שניות יעלם.
 

<script type="text/javascript">

 

    function showMessage(msg) {

        $('#msg').text(msg).slideDown();

        setTimeout(function () { $('#msg').slideUp(); }, 4000);

    }

</script>

 
 
כל מה שנשאר לעשות, זה במתודה validate במקום alert להשתמש ב - showMessage.