שימוש ב-Data ו-HTTP Requests

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

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

ניצור פרוייקט חדש.

ng new wsearch

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

ng g c components/SearchBar
ng g c components/PageList

נצטרך גם ליצור service שיביא לנו את הנתונים מויקיפדיה. ה-service יקבל את מושג החיפוש ויחזיר את רשימת הדפים הרלוונטים מויקיפדיה.

ng g s services/wikipedia

כדי לא להתעסק בעיצוב נתקין את Bootstrap בפרוייקט.

שתי הקומפוננטות ישבו בתוך הקומפוננטה הראשית של האפליקציה.

קובץ app.component.html

<div class="container pt-5 pb-5">
  <app-search-bar></app-search-bar>
  <app-page-list></app-page-list>
</div>

טיפול בקלט

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

  1. המשתמש מכניס את הטקסט -> המשתמש מקיש על Enter -> נקבל את הערך שהוכנס -> נשלח לקומפוננטה הראשית לביצוע חיפוש.
  2. המשתמש מכניס את הטקסט -> עם כל תו שמוקלד אנחנו מריצים אירוע שמאחסן את התו הזה ומעדכן את הקלט -> המשתמש מקיש Enter -> לוקחים את הערך שיש לנו ושולחים לקומפוננטה הראשית לביצוע חיפוש.

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

בקומפוננטה החיפוש נכניס את שורת החיפוש.

קובץ search-bar.component.html

<input class="form-control" type="text" placeholder="Enter your search...">

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

קובץ search-bar.component.html

<input class="form-control"
  type="text"
  placeholder="Enter your search..."
  (input)="onInput(getValue($event))"
>

קובץ search-bar.component.ts

export class SearchBarComponent {
  term = "";

  constructor(){}

  ngOnInit(){}

  onInput(value: string){
    this.term = value;
  }

  getValue(event: Event): string {
    return (event.target as HTMLInputElement).value;
  }
}

כדי לראות את הקליטה של הנתונים אפשר לשים console.log בפונקציית onInput.

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

קובץ search-bar.component.html

<form (submit)="onFormSubmit($event)">
  <input
    class="form-control"
    type="text"
    placeholder="Enter your search..."
    (input)="onInput(getValue($event))"
  />
</form>

העברת מידע מילד להורה

העברת מידע מהורה לילד מתבצעת על ידי שימוש ב- Input@. בכיוון ההפוך (child to parent) אנחנו משתמשים ב- Output@ ומייצרים אירוע שמעביר את המידע.

קובץ search-bar.component.ts

@Output() submitted = new EventEmitter<string>();
term = "";

onFormSubmit(event){
  event.preventDefault();
  this.submitted.emit(this.term);
}

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

קובץ app.component.html

<div class="container pt-5 pb-5">
  <app-search-bar (submitted)="onTerm($event)"></app-search-bar>
  <app-page-list></app-page-list>
</div>

קובץ app.component.ts

export class AppComponent {
  onTerm(term: string){
  }
}

כדי להשתמש ב-API צריך להבין איך הוא עובד ומה הוא מציע כשירות. ,תיאור מפורט אפשר למצוא בקישור ה-API של ויקיפדיה. קישור ישיר לאופציית החיפוש ב-API.

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

קובץ wikipedia.service.ts

export class WikipediaService {
  constructor() { }

  search(term: string){
  }
}

כדי לייצר בקשת Http נייבא את המודול HtpClientModule למודול הראשי שלנו.

קובץ app.module.ts

import { HttpClientModule } from '@angular/common/http'

imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
],

כדי לפנות ל-API נגדיר httpClient ב-service. נעתיק את ה-url אליו אנחנו צריכים לפנות מתוך התיעוד של ויקיפדיה' נשלח את הפרמטרים המתאימים ונבצע קריאת get.

קובץ wikipedia.service.ts

export class WikipediaService {
  wikiUrl = "https://en.wikipedia.org/w/api.php";

  constructor(private httpClient: HttpClient) { }

  search(term: string){
    let params = {
      action: "query",
      list: "search",
      origin: "*",
      format: "json",
      srsearch: term
    };

    return this.httpClient.get(this.wikiUrl, {params});
  }
}

נקרא לפונקציה מהקומפוננטה ונקבל את התוצאה שהגיעה. האובייקט שמתקבל הוא מסוג observable ולכן צריך לבצע פעולת subscribe כדי לגשת למידע שלו.

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

קובץ app.component.ts

export class AppComponent {
  constructor(private wikipedia: WikipediaService){}

  onTerm(term: string){
    this.wikipedia.search(term).subscribe(result =>{
      console.log(result);
    });
  }
}

קבלנו אובייקט שבו יש מרכיב אחד שמעניין אותנו וזה query->search. שם יש רשימה של תוצאות החיפוש שהתקבלו.

עכשיו שיש לנו את המידע, צריך להעביר אותו לקומפוננטת page-list כדי שתציג אותו.

העברת מידע מהורה לילד

כדי להעביר את המידע לקומפוננטת page-list צריך לבצע העברת מהורה לילד (parent to child) על ידי שימוש ב-Input@.

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

קובץ app.component.ts

export class AppComponent {
  pages = [];

  constructor(private wikipedia: WikipediaService){}

  onTerm(term: string){
    this.wikipedia.search(term).subscribe((result: any) =>{
      this.pages = result.query.search;
    });
  }
}

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

קובץ app.component.html

<div class="container pt-5 pb-5">
  <app-search-bar (submitted)="onTerm($event)"></app-search-bar>
  <app-page-list [pages]="pages"></app-page-list>
</div>

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

קובץ page-list.component.ts

export class PageListComponent {
  @Input() pages = [];
}

ועכשיו אפשר להשתמש במשתנה בתבנית הקומפוננטה.

קובץ page-list.component.html

<table class="table table-hover mt-4">
  <thead>
    <tr>
      <th scope="col">Title</th>
      <th scope="col">Wordcount</th>
      <th scope="col">Snippet</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let page of pages">
      <td>{{ page.title }}</td>
      <td>{{ page.wordcount }}</td>
      <td>{{ page.snippet }}</td>
    </tr>
  </tbody>
</table>

וזה המסך שנקבל.

עוד דבר שאנחנו רוצים לעשות זה ליצור קישור ב-title של המאמר שמוביל למאמר עצמו.

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

https://en.wikipedia.org?curid=

נשנה את הקוד של ה-title בקובץ שמציג את הטבלה.

קובץ page-list.component.html

<tr *ngFor="let page of pages">
      <td><a [href]=" 'https://en.wikipedia.org?curid=' + page.pageid " 
             target="_blank">{{ page.title }}</a></td>
      <td>{{ page.wordcount }}</td>
      <td [innerHTML]="page.snippet"></td>
</tr>

ניווט במאמר

מאמרים אחרונים

Weekly Tutorial