ניהול מצב ביישום אנגולר
פוסט זה כולל טיפ קצר בנושא פיתוח Front End. אם אתם רוצים ללמוד יותר לעומק על פיתוח Front End מהבסיס ועד הנושאים המתקדמים תשמחו לשמוע שבניתי קורס וידאו מקיף בנושא זה הכולל מעל 50 שיעורי וידאו והמון תרגול מעשי.
למידע נוסף והצטרפות לקורס בקרו בדף קורס Front End באתר.
ספריות MVC בצד הלקוח הגיעו עם הבטחה גדולה: שיעזרו לנו לנהל יישומים גדולים. בפועל דוקא הפופולרית שבהן לא הצליחה לתת תשובה חד-משמעית לשאלה היכן לשמור את המידע, ובמקום רק יצרה בלבול עם ההפרדה בין Service ל Controller. בואו ננסה לעשות סדר דרך מספר דוגמאות.
1. בהתחלה היו Controllers
הרכיב הראשון שמתכנתי אנגולר מתחילים נתקלים בו הוא לרוב הבקר (Controller) כדי לייצר את קשירת המידע. כך חיפוש של angular hello world בגוגל מביא לתוכנית שנראית פחות או יותר כך:
והבעיה שרק השתיים וחצי שורות האלה כבר מובילות למסקנה הלא נכונה שבאנגולר אנחנו כותבים את הקוד בתוך בקר ואת התצוגה בתוך HTML. מהר מאוד הבקרים הופכים קשים לתחזוקה וגרוע יותר אנו מתחילים לשכפל קוד ביניהם. כך למשל בדוגמא הבאה תראו שתי תיבות שבכל אחת מהן תוכלו להזין מספר ולקבל את ייצוגו כטקסט (למשל עבור 2 תקבלו את המילה Two). הפונקציה שהופכת מספר לערך משוכפלת בין שני הבקרים:
2. ואז מגיעים ה Services
הרכיב הבא שתמצאו יהיה כנראה השירות (Service). זהו Singleton שאפשר לשתף בין מספר בקרים ובו לכתוב חלק מהקוד המשותף. כך אפשר לשתף את קוד התרגום בין שתי התיבות.
3. אבל מה לגבי מידע משותף?
הבעיה עם שירותים שהם מובילים למסקנה המוטעית שכל מה שמשותף בין מספר בקרים צריך להיכתב בהם, ולא כך הדבר. בדוגמא שלנו עדיין קוד הפונקציה translate משוכפל בין שני הבקרים. המעט שנשאר בפונקציה זו הוא העדכון של שדות המידע של הבקרים, ואפשר היה לחשוב שבאמצעות Services נוכל להיפטר גם מקוד משותף זה. הנה למשל כיוון לא מומלץ לפתרון:
הקוד עובד אבל מסורבל. כל בקר צריך לזכור איזה חלק במידע המשותף שייך לו. ביישומים גדולים יותר קל לייצר זליגות זכרון כי כל בקר צריך באופן יזום למחוק את המידע הרלוונטי עבורו מה Service. גם ציון האינדקס באופן מפורש בתבנית הכרחי מאחר ומדובר ב Mutable Data (במקרים דומים בהם משתמשים באוביקט במקום במערך אתם עשויים לראות את כתיב הנקודה).
4. שיתוף מידע ופעולות בדרך הנכונה
דרך טובה יותר לשתף מידע בין בקרים מזכירה רעיון של שמירת מידע בתוכנית: משתנה נשמר בבלוק הפנימי ביותר בו צריכים אותו, ובאנלוגיה מידע יישמר בבקר הפנימי ביותר בו צריכים אותו. אם צריכים לשתף מידע או פעולה הנוגעת במידע בין שני בקרים יש להשתמש בבקר מכיל או בירושה. נתחיל בשיתוף עליו דיברנו בסעיף הקודם, שיתוף הפונקציה translate כך שלא נצטרך לשכפל קוד בין הבקרים. בשביל זה אשתמש בירושה:
ומה אם נרצה לשמור מידע המשותף לשני הבקרים? למשל אם נרצה שהמספר אותו אנו מתרגמים ישותף בין שני האזורים? כאן נשתמש בבקר נוסף העוטף את שניהם באופן הבא:
המידע מחולק ל-2: הטקסט נשמר עבור כל אחד מהבקרים בנפרד ואילו המספר נשמר במשותף. זו הסיבה ששינוי המספר לא משנה מיידית את הטקסט בבקר השני. אם נרצה לשתף את שני פרטי המידע נשמור את שני השדות בבקר המשותף ונוותר על הירושה.
5. לסיכום: שמירת מידע ופעולות על המידע באנגולר
אנו נשתמש ב Services כדי לשמור פעולות טהורות שאינן שומרות מצב ואפשר להפעיל אותן מכל בקר ביישום ולקבל תמיד את אותה התוצאה. כך פעולת התרגום שלא משנה מאיזה מקום נשתמש בה במערכת, תמיד תתנהג באותו האופן. גם את התקשורת עם צד השרת כדאי לכתוב כ Service.
בקרים (Controllers) שומרים את המידע הרלוונטי לאזור מסוים בתצוגה. בגלל זה בקוד ה HTML אתם רושמים ng-app על אלמנט. הבקר מתאים לאלמנט ואזור תצוגה מסוים ותפקידו לשמור את המידע עבור אזור זה. כשצריך לשתף מידע בין מספר בקרים, זה בסך הכל אומר שצריך לשמור את המידע למעלה יותר בעץ.
שיתוף פעולות בין בקרים מתבצע במנגנון הרגיל של ירושת Prototypes ב JavaScript. בצורה כזו ניהול הפעולה תמיד נשאר בצד של הבקר. הבקר משתמש בפונקציות מה Service ואלו מחזירות מידע (או Promise עבור פונקציות אסינכרוניות). הבקר הוא זה שאחראי לקחת את התוצאה ולשמור אותה.
אם נסתכל על יישום אנגולר אפשר לדמיין את שכבת המידע כשכבה השוכנת ״מאחורי״ ה DOM. כל אזור ב DOM מקבל בקר שמתאים לו ושומר את המידע המוצג באזור זה.