אחרי שראינו lazy loading component בעזרת הראוט, ניצור עוד דף ועוד רכיבים, נחבר אותם העזרת שירות ונעבור על עוד מספר נקודות קטנות בדרך.
תחילה ניצור רכיב חדש, נפתח את ה-CMD בספריה PAGES ונריץ את הפקודה הבאה
C:\my-standalone-project\src\app\pages>ng g c about
נוסיף לראוט שלנו את הדף החדש שיצרנו
import { Routes } from '@angular/router'; export const routes: Routes = [ { path: '', loadComponent: () => import('./pages/home/home.component').then(p => p.HomeComponent) }, { path: 'about', loadComponent: () => import('./pages/about/about.component').then(p => p.AboutComponent) } ];
כשהפרויקט רץ, נגלוש לכתובת: http://localhost:4200/about
ונראה שאכן אנחנו מצליחים להגיע לדף שיצרנו.
כדאי שיהיה לנו יותר קל להבחין בדף שאנחנו נמצאים נעשה שינויים בדף הבית ובדף אודות כך:
<h1>home works!</h1> <a routerLink="about">About</a>
import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; @Component({ selector: 'app-home', standalone: true, imports: [RouterModule], // added templateUrl: './home.component.html', styleUrl: './home.component.scss' }) export class HomeComponent { }
<h1>about works!</h1> <a routerLink="/">Home</a>
import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; @Component({ selector: 'app-about', standalone: true, imports: [RouterModule], // added templateUrl: './about.component.html', styleUrl: './about.component.scss' }) export class AboutComponent { }
עכשיו שיש לנו קצת תוכן, נמחק מהקובץ הראשי את התוכן המיותר שיש בו
<router-outlet></router-outlet>
ניקינו את הפרויקט, יש לנו 2 דפים שבכל אחד מהם יש כותרת של אותו דף ולינק שמוביל לדף השני.
בשלב הזה, נוסיף 2 רכיבים, אחד יתארח בדף הבית והשני בדף אודות. אחרי זה נחבר אותם בשירות אחד לשני.
נפתח CMD בספרית components ונריץ את הפקודות הבאות:
C:\my-standalone-project\src\app\components>ng g c name-input C:\my-standalone-project\src\app\components>ng g c show-name
ברכיב name-input נבצע את השינויים הבאים
<div class="name-input"> <h2>Who am I?</h2> <form [formGroup]="form"> <label> <span>Full name</span> <input type="text" formControlName="fullName"> </label> <button type="button" (click)="onSaveName()">Save</button> </form> </div>
import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; @Component({ selector: 'app-name-input', standalone: true, imports: [ReactiveFormsModule], templateUrl: './name-input.component.html', styleUrl: './name-input.component.scss' }) export class NameInputComponent implements OnInit { form: FormGroup = new FormGroup({}); constructor() { } ngOnInit(): void { this.form = new FormGroup({ fullName: new FormControl('') }) } onSaveName() { console.log(this.form.value); } }
כפי שאפשר לראות, הוספנו טופס, שדה טקסט מחובר לטופס וכפתור שמירה שמדפיס לקונסול שלנו את הערך של הטופס
נוסיף את הרכיב לדף הבית
<h1>home works!</h1> <a routerLink="about">About</a> <div> <app-name-input></app-name-input> </div>
import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; import { NameInputComponent } from '../../components/name-input/name-input.component'; @Component({ selector: 'app-home', standalone: true, imports: [RouterModule, NameInputComponent], // added templateUrl: './home.component.html', styleUrl: './home.component.scss' }) export class HomeComponent { }
אם תשמרו ותלכו לדף הבית, נראה את הרכיב שלנו, בכתיבה של טקסט בתוך השדה ואז לחיצה על שמירה, נראה בקונסול אוביקט עם מאפיין בשם fullName שמכיל את הטקסט שכתבנו בשדה.
הרכיב הבא שנשנה יהיה show-name
<div class="show-name"> <h2>My name is.... {{fullName}}</h2> </div>
import { Component } from '@angular/core'; @Component({ selector: 'app-show-name', standalone: true, imports: [], templateUrl: './show-name.component.html', styleUrl: './show-name.component.scss' }) export class ShowNameComponent { fullName: string = ''; }
וכמובן שנחבר אותו לדף אודות
<h1>about works!</h1> <a routerLink="/">Home</a> <div> <app-show-name></app-show-name> </div>
import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; import { ShowNameComponent } from '../../components/show-name/show-name.component'; @Component({ selector: 'app-about', standalone: true, imports: [RouterModule,ShowNameComponent], templateUrl: './about.component.html', styleUrl: './about.component.scss' }) export class AboutComponent { }
אם נשמור הכל ונריץ, נראה את הרכיבים שלנו בדפים אבל אם נכתוב משהו אז השני לא יתעדכן. הסיבה היא שהם עדיין לא מחוברים.
זה הזמן לחבר אותם, ונעשה את זה בעזרת שירות (service).
ניצור בספרית APP ספריה בשם services ובתוכה נריץ את הפקודה הבאה
C:\my-standalone-project\src\app\services>ng g s information
נכנס לקובץ החדש שנוצר ונערוך אותו כך
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class InformationService { private fullNameInfo = new BehaviorSubject(''); public fullNameInfo$ = this.fullNameInfo.asObservable(); constructor() { } setFullNameInfo(data: string) { this.fullNameInfo.next(data); } }
יצרנו משתנה מסוג behaviorSubject, לא חייבים להשתמש בו ואפשר לממש אחרת, אבל זה נוח.
הרעיון שלו זה להחזיר מצב 0, איזה ברירת מחדל שאנחנו קובעים גם אם המידע לא הגיע עדיין מהשרת וכך אנחנו יודעים בודאות מה המידע שיגיע.
אנחנו שומרים עליו בסוד כדאי שלא ישכתבו אותו מבחוץ. מחצינים משתנה נוסף שמשקף את הערך שיש במשתנה הסודי שלנו.
לבסוף, מקצים כמו API, פונקציות שיכולות לגשת למשתנה הפרטי שלנו ולשנות אותו. נהוג לעשות בפונקציות בדיקות שהמידע שמגיע תקין לפני שמעדכנים את המשתנה הפרטי שלנו.
עכשיו לחיבורים 😁.
תחילה הרכיב שכותב
import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { InformationService } from '../../services/information.service'; @Component({ selector: 'app-name-input', standalone: true, imports: [ReactiveFormsModule], templateUrl: './name-input.component.html', styleUrl: './name-input.component.scss' }) export class NameInputComponent implements OnInit { form: FormGroup = new FormGroup({}); constructor(private informationService: InformationService) { } ngOnInit(): void { this.form = new FormGroup({ fullName: new FormControl('') }) } onSaveName() { this.informationService.setFullNameInfo(this.form.value.fullName); } }
נעדכן גם את הרכיב שקורא
import { Component, OnInit } from '@angular/core'; import { InformationService } from '../../services/information.service'; @Component({ selector: 'app-show-name', standalone: true, imports: [], templateUrl: './show-name.component.html', styleUrl: './show-name.component.scss' }) export class ShowNameComponent implements OnInit { fullName: string = ''; constructor(private informationService: InformationService) { } ngOnInit(): void { this.informationService.fullNameInfo$.subscribe(info => { this.fullName = info }) } }
לאחר ששמרנו, נלך לדף הבית, נכתוב משהו ונלחץ על שמירה.
נעבור לדף אודות ושם נראה את מה שכתבנו נוסף להמשך המשפט.
בעקרון בשלב הזה סיימנו את כל התהליך מצד לצד. יש עוד מספר נקודות שארצה להתעכב עליהם.
import { Component, OnDestroy, OnInit } from '@angular/core'; import { InformationService } from '../../services/information.service'; import { Subscription } from 'rxjs'; @Component({ selector: 'app-show-name', standalone: true, imports: [], templateUrl: './show-name.component.html', styleUrl: './show-name.component.scss' }) export class ShowNameComponent implements OnInit, OnDestroy { fullName: string = ''; info$ = new Subscription; constructor(private informationService: InformationService) { } ngOnInit(): void { this.info$ = this.informationService.fullNameInfo$.subscribe(info => { this.fullName = info }) } ngOnDestroy(): void { this.info$.unsubscribe(); } }
כאשר מבצעים רישום, צריך לדאוג להתנתק ממנו, אנגולר לא יודע לעשות את זה לבד ולכן כאשר נעבור לדף אחר ונחזור הוא יצור רישום נוסף ולא במקום הקודם עד שהזיכרון יתמלא.
שינוי נוסף הוא כאשר אנחנו חוזרים מדף אודות לדף הבית, אנחנו רוצים שהוא יזכור את מה שכתבנו ולכן צריך לחבר את שדה הטקסט לשירות שלנו.
import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { InformationService } from '../../services/information.service'; import { Subscription } from 'rxjs'; @Component({ selector: 'app-name-input', standalone: true, imports: [ReactiveFormsModule], templateUrl: './name-input.component.html', styleUrl: './name-input.component.scss' }) export class NameInputComponent implements OnInit, OnDestroy { form: FormGroup = new FormGroup({}); info$ = new Subscription; fullname: string = ''; constructor(private informationService: InformationService) { } ngOnInit(): void { this.info$ = this.informationService.fullNameInfo$.subscribe(info => { this.fullname = info; }) this.form = new FormGroup({ fullName: new FormControl(this.fullname) }) } onSaveName() { this.informationService.setFullNameInfo(this.form.value.fullName); } ngOnDestroy(): void { this.info$.unsubscribe(); } }
כאשר נרשום משהו, נשמור אותו ונעבור לדף אחר וחזרה הכל יעבוד כמו שאנחנו מצפים.
דבר אחרון לפני סיום, כאשר נרצה לחבר לשירות שלנו קריאה לשרת (HTTP) , למרות שהכל נראה תקין ולא נראה שיש שגיאות
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class InformationService { private fullNameInfo = new BehaviorSubject(''); public fullNameInfo$ = this.fullNameInfo.asObservable(); constructor(private http: HttpClient) { } setFullNameInfo(data: string) { this.fullNameInfo.next(data); } updateDB() { this.http.post('/', '').subscribe(data => { }) } }
כאשר נשמור ונעלה את הפרויקט, נקבל בקונסול את השגיאה הבאה:
ERROR NullInjectorError: R3InjectorError(Standalone[_HomeComponent])[_InformationService -> _InformationService -> _InformationService -> _HttpClient -> _HttpClient]: NullInjectorError: No provider for _HttpClient!
איך לתקן את זה? זוכרים שבחלק הראשון אמרתי שנחזור לקובץ config…
import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; import { provideHttpClient } from '@angular/common/http'; export const appConfig: ApplicationConfig = { providers: [provideRouter(routes), provideHttpClient()] };
אנחנו מספקים את HTTPCLIENT באופן גלובלי באפליקציה ולכן גם השגיאה יורדת והאתר חוזר לפעול כרגיל
🤯 נראה לי שלצעדים הראשונים ב-standalone זה מספיק, בהצלחה ✌️