כתיבת אפליקציית אנגולר מתבצעת ע"י כתיבת HTML שבתוכו משולבים תוספות של
אנגולר, כתיבת components כדי לשלוט על ה-templates, כתיבה של services כדי לשלוט על הלוגיקה,
ושילוב של components ו- services ליצירת modules.
הארכיטקטורה של אנגולר (מתוך האתר הרשמי, גם
הדוגמאות שלהלן יהיו מאותו האתר):
באיור לעיל ניתן לראות את שמונה אבני הבניין הראשיים של אנגולר 2:
- Modules
- Components
- Templates
- Metadata
- Data binding
- Directives
- Services
- Dependency injection
1. Modules
באנגולר מסמנים module ע"י @NgModule. בכל אפליקציית אנגולר יש לפחות module אחד. באפליקציות קטנות יכול להיות רק module אחד (שהוא ה-root
module), אבל ברוב האפליקציות יהיו מספר modules. כל module מיועד לפיצ'ר מסוים (feature module), או לסט של פיצ'רים באותו תחום. כל module הוא class עם decorator של @NgModule.
במאמר מוסגר נציין ש- decorator זה בעצם פונקציה שמתאימה
את ה-class של JS לטובת אנגולר. באנגולר יש
המון סוגים של decorator שמוסיפים metadata ל-class כך שאנגולר יידע מה לעשות
עם כל class.
ה- NgModule decorator, זו פונקציה שלוקחת
אובייקט metadata שמתאר את ה-module. השדות העיקריים באובייקט ה-metadata הם:
·
declarations – מכיל את
ה-view classes שמשתייכים ל-module הזה. באנגולר יש שלוש סוגים של view classes, שהם: components, directives ו-pipes.
·
exports – מכיל
רשימה מתוך ה-declarations שצריכה להיות זמינה
לשימוש ב-templates של components של modules אחרים.
·
imports – מכיל
רשימה מתוך ה-declarations שיוצאו (נכללו ברשימת exports) מ-modules אחרים ושנצרכים לטובת ה-templates ב-components שב-module הנוכחי.
·
providers – מכיל
רשימה של services שנמצאים ב-module הנוכחי. ה-services יהיו זמינים בכל
האפליקציה (ולא רק ב-module הנוכחי).
·
bootstrap – מכיל את
ה-view הראשי של האפליקציה (נקרא גם root component). השדה bootstrap צריך להיות רק ב-module הראשי (root module).
דוגמא ל-root module בסיסי:
app/app.module.ts:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
imports: [ BrowserModule ],
providers: [ Logger ],
declarations: [ AppComponent ],
exports: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
בד"כ הצורה שבה משתמשים ב-root module כ-Module הראשוני היא:
app/main.ts:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
ספריות אנגולר
אנגולר זה אוסף של JS modules (יש הבדל בין JS modules ל-angular modules. כל קובץ JS הוא בעצם JS module). כל ספריה של אנגולר
מתחילה עם @angular. כדי להשתמש בספריה של
אנגולר צריך לייבא אותה ע"י import לדוגמא:
import { Component } from '@angular/core';
גם angular modules מיבאים בצורה דומה.
לדוגמא:
import { BrowserModule } from '@angular/platform-browser';
2.
Components
ה-component שולט על חלק מהתצוגה, גם
מבחינת המראה וגם מבחינת הלוגיקה. מגדירים את הלוגיקה של ה- component בתוך class, וה-class מתקשר עם התצוגה ע"י
API של properties ו-methods.
3.
Templates
את התצוגה של ה- component מגדירים בשדה של ה-template שלו. ה-template זה בעצם קטע של HTML שמגדיר לאנגולר איך לרנדר
את התצוגה. ב-template משלבים בטקסט ה-HTML גם שימוש בקוד של אנגולר. לדוגמא:
<h2>Hero List</h2>
<p><i>Pick a hero
from the list</i></p>
<ul>
<li *ngFor="let
hero of heroes" (click)="selectHero(hero)">
{{hero.name}}
</li>
</ul>
<hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>
בדוגמא זו ניתן לראות שימוש ב-*ngFor, ובכל מיני סוגריים שהם חלק מה-syntax של אנגולר כמו {{hero.name}}, (click), [hero]. כמו כן ניתן לראות שימוש בתג חדש שיצרנו <hero-detailed> שמייצג את ה-component שיצרנו HeroDetailComponent.
4.
Metadata
ה-metadata מכיל מידע שמסביר לאנגולר
מה לעשות עם ה-class. ב-TypeScript מצמידים metadata ל-class ע"י decorator. לדוגמא:
@Component({
moduleId: module.id,
selector: 'hero-list',
templateUrl: 'hero-list.component.html',
providers: [ HeroService ]
})
export class HeroListComponent implements OnInit {
/* . . . */
}
בדוגמא זו ניתן לראות component decorator שמזהה את ה-class שכתוב לאחריו כ-component
class. ה-decorator מקבל configuration object שמכיל את המידע הנצרך לאנגולר כדי ליצור את
ה-component ואת ה-view שלו:
·
moduleId – קובע את
ה-base address ל-URLs היחסיים, כמו templateUrl.
·
selector – זהו CSS selector שמייצג את ה-component הנוכחי. בכל מקום ב-HTML שיופיע ה-selector הזה - <hero-list>, אנגולר יכניס לשם את ה-view של ה-component הזה.
·
templateUrl – מיקום
יחסי של HTML template של ה-component.
·
providers – מערך של services שנדרשים ל-component הנוכחי מה-dependency injection.
אפשר לסכם שה-metadata מספק לאנגולר רשימה של
אבני הבניין שנדרשים ל-component.
ה-template יחד עם ה-metadata וה-component מתארים את ה-view.
5.
Data binding
אנגולר מספק מספר אפשרויות של data binding בין ה-component ל-DOM:
1. מה-component ל-DOM
2. מה-DOM ל-component
3. לשני הכיוונים (two way data binding)
לדוגמא:
<li>{{hero.name}}</li>
<hero-detail [hero]="selectedHero"></hero-detail>
<li (click)="selectHero(hero)"></li>
·
שימוש
בסוגריים מסולסלים כפולים, {{hero.name}}, נקרא interpolation. צורה זו היא דוגמא של data binding מספר 1. הערך של ה-hero.name
property יוצג באלמנט <li>.
·
שימוש
בסוגריים מרובעים, [hero], נקרא property binding. גם צורה זו היא דוגמא של data binding מספר 1. במקרה הזה הערך של selectedHero יועבר ל-hero property, ולאחר מכן יוצג ב-DOM ע"י HeroDetailedComponent.
·
שימוש
בסוגריים עגולות, (click), נקרא event binding. צורה זו היא דוגמא של data binding מספר 2. כאן מועבר מידע (במקרה הזה event) מה-DOM אל ה-component.
ה-data binding מספר 3, שנקרא two way data binding, משלב property
binding יחד עם event binding. צורת הישמוש היא ע"י סוגריים עגולים בתוך סוגריים מרובעים,
לדוגמא:
<input [(ngModel)]="hero.name">
ב- two way
data binding אם ה-property משתנה ב-component אז הוא ישתנה גם ב-DOM (לדוגמא ב-input box), כמו ב-property
binding. ואם המשתמש שינה את הערך ב-DOM אז השינוי יעבור גם ל-component, כמו ב-event binding.
באנגולר עושים הרבה שימוש ב-binding בתקשורת בין ה-template ל-component. כמו כן, משתמשים בזה גם
בתקשורת בין component אבא ל-component בן.
6.
Directives
ה-directive הוא class עם decorator של @directive. ה-component הוא בעצם directive עם template, אך כיון שהוא כל כך חשוב
באריכטקטורה של אנגולר, ישנה הפרדה בהסבר בים component ל-directive.
ישנם שני סוגים נוספים של directive:
·
Structural
directive
·
Attribute
directive
ה-structural directive משנה את התצוגה ע"י
הוספה, הסרה או החלפה של אלמנטים ב-DOM. לדוגמא:
<li *ngFor="let
hero of heroes"></li>
<hero-detail *ngIf="selectedHero"></hero-detail>
ה-*ngFor אומר לאנגולר לתת <li> אחד לכל hero מרשימת heros.
ה-*ngIf מציג את ה-heroDetail component רק אם ה-hero הנבחר קיים.
ה-attribute directive משנה את התצוגה או את
ההתנהגות של אלמנט קיים. ב-template ה-directive הזה נראה כמו HTML attributes רגילים, ולכן זה נקרא attribute
directive.
ה-ngModel directive, שנותן לנו two way data binding, הוא דוגמא ל-attribute
directive. ה-ngModel משנה את ההתנהגות של
אלמנטים קיימים ע"י השמה של ערכים לתצוגה של האלמנט (ל-<input> למשל), וע"י תגובה
ל-event של שינוי.
<input [(ngModel)]="hero.name">
באנגולר יש עוד כמה directives, לדוגמא: ngSwitch, ngStyle, ngClass. כמו כן יש אפשרות לכתוב directive בעצמנו.
7.
Services
ה-service זה class שמספק לנו שירות מסוים.
למשל: logging service, data service, tax calculator,
application configuration.
ה-services זה אחד מאבני היסוד של
אנגולר. ה-components הם הצרכנים הראשים של services. דוגמא ל-service פשוט:
export class Logger {
log(msg: any) { console.log(msg); }
error(msg: any) {
console.error(msg); }
warn(msg: any) { console.warn(msg); }
}
ה-components אמורים להיות יחסית
קטנים. ב-component לא מיישמים את התקשורת עם
השרת, לא עושים input validation וגם לא מיישמים כתיבה ל-console. כל זה ודברים דומים לכך מיישמים ב-services.
התפקיד של ה-component זה לדאוג לחויית ההמשתמש.
הוא מאגד את ה-view (ממומש ב-template) יחד עם הלוגיקה. אבל אנגולר לא כופה את
הארכיטקטורה הזו, וגם אם ה-component יטפל בהכל בעצמו מבחינת
אנגולר זה יהיה בסדר. אנגולר עוזר במימוש הארכיטקטורה הזו ע"י כך שהוא עושה
את ה-services זמינים לכל ה-components בעזרת ה-dependency injection.
8.
Dependency
injection
ה- dependency
injection זו הדרך לספק לכל class חדש את כל הדברים שהוא
תלוי בהם. רוב ה-dependencies הם services. אנגולר יודע מה ה-services הדרושים ל-component מתוך הפרמטרים שב-constructor. לדוגמא:
constructor(private service: HeroService) { }
ה-class הזה צריך את HeroService.
כאשר אנגולר יוצר component הוא בהתחלה מבקש מה-injector את ה-services הדרושים. ה-injector מבחינתו מתחזק container של services
instances שהוא יצר עד כה. אם ה-service המבוקש קיים כבר ב-container אז אנגולר קורא ל-constructor של ה-component עם כל ה-services הדרושים. אם לאחד ה-services עדיין אין instance ב-container אז ה-injector יוצר instance של ה-service ושומר אותו ב-container לפני שהוא חוזר לאנגולר.
רק לאחר שכל ה-services הדרושים שמורים ב-container רק אז אנגולר קורא ל-constructor של ה-component עם כל ה-services הדרושים.
כאשר ה-injector צריך ליצור instance של service מסוים, הוא משתמש במערך providers שהוא זה שיכול ליצור instance של service. ניתן לכתוב את ה-providers ב-modules או ב-components. באופן כללי, עדיף לכתוב את כל ה-providers ב-root module. כך שאותו instance של service יהיה קיים בכל האפליקציה.
ניתן גם לכתוב providers בתוך component. המשמעות היא שנוצר instance חדש של service לכל instance חדש של ה-component.
כמה נקודות חשובות לגבי ה-dependency injection:
·
ה-DI הוא חלק מאנגולר ונמצא בשימוש בכל האפליקציה
·
ה-DI מייצר ומתחזק container של services
instances
·
ה-DI יכול ליצור instance חדש של service ע"י שימוש ב-provider.
9.
סיכום
·
למדנו את
שמונה אבני הבנין של אנגולר, הכוללים את:
·
Modules
·
Components
·
Templates
·
Metadata
·
Data binding
·
Directives
·
Services
·
Dependency
injection
וזה ידע בסיסי טוב כדי להתחיל ולכתוב אפליקציות אנגולר 2.
כמובן שיש עוד הרבה מה ללמוד. להלן מספר נושאים חשובים באנגולר:
Animations – ניתן
ליצור component לאנימציה גם בלי ידע נרחב
באנימציות, ע"י שימוש בספרית האנימציה של אנגולר.
Change detection – הצורה
שבה אנגולר מחליט שהערך של-property של component מסוים השתנה, ומתי לעדכן
את המסך.
Events – גם components וגם services יכולים לשלוח events. כמו כן, כל מנגנוני פרסום ה-event והרשמה ל-event גם כלולים כאן.
Forms - תומך בתרחישי הכנסת נתונים מורכבים עם
וולידצית HTML-based.
HTTP – נועד
לצורך יצירת קשר עם השרת כדי לקבל מידע, לשמור מידע, ולקלוט את ה-server side actions ע"י ה-HTTP
client.
Lifecycle hooks – מאפשר
להתחבר לנקודות מרכזיות במחזור החיים של components, מיצירת ה-component ועד לסגירתו ע"י שימוש ב-lifecycle hook interface.
Pipes – משמשים
לשיפור ה-UX בכך שהם משנים תצוגה של
ערכים מסוימים. לדוגמא, הצגת ערך כספי:
price | currency:'USD':true
יציג 42.33$.
Router – ניווט
מדף לדף בתוך ה-client בלי לעזוב את ה-browser.
Testing – שימוש ב-Angular Testing Platform לצורך הרצת unit tests על האפליקציה.בקיצור, יש עוד הרבה מה ללמוד, אז קדימה חבל על הזמן...