שימוש ב-pipe קיימים ומשלנו ב-Angular

מחבר:
בתאריך:

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

רוצים דוגמאות ל-pipes? בבקשה.

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

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

 

Pipe ללא פרמטרים

את ה-pipe מוסיפים למשתנה ב-html בתוך הסוגריים המסולסלים. לדוגמה, נוסיף למשתנה title את ה-pipe ששמו lowercase ותפקידו להציג את ערך המשתנה כשכל האותיות קטנות.

/src/app/app.component.ts
-----------------------------------

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  title = 'List Of Cars';
}

/src/app/app.component.html
-----------------------------------

<h2>{{ title | lowercase }}</h2>

כך זה נראה כשנריץ על הדפדפן:

lowercase pipe בפעולה

  • המשתנה title יוצג על גבי המסך כשכל האותיות קטנות (lowercase).
  • את ה-pipe מוסיפים לערך שמודפס ב-html.
  • בין ה-pipe והמשתנה שעליו הוא עובד מפריד קו מפריד אנכי (pipe באנגלית).

 

 

Pipe .2 שמקבלים פרמטרים

דוגמה ל-pipe שמקבל פרמטר הוא currency, שמשמש כדי להוסיף פסיקים של אלפים וגם את סימן המטבע.

נוסיף לקומפננטה האנגולרית מערך שמכיל מודלים ומחירים של מכוניות. ואח"כ נדפיס את המחירים בתוך לולאה ונפעיל את ה- pipe ששמו currency.

/src/app/app.component.ts
-----------------------------------

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  title = 'List Of Cars';

  cars = [
    {model: 'Tesla', price: 30000},
    {model: 'BMW', price: 47000},
    {model: 'Jaguar', price: 200000},
    {model: 'Ferrari', price: 400000},
    {model: 'Sussita', price: 4000}
  ];
}

/src/app/app.component.html
----------------------------------

<h2>{{ title | lowercase }}</h2>

<ul>
  <li *ngFor="let car of cars">
  {{car.price | currency}}
  </li>
</ul>

כך זה נראה:

currency pipe בפעולה angular אנגולר

הערך ברירת המחדל שמציג ה-pipe הוא בדולרים.

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

/src/app/app.component.html
----------------------------------

<h2>{{ title | lowercase }}</h2>

<ul>
  <li *ngFor="let car of cars">
  {{car.price | currency:'NIS'}}
  </li>
</ul>

כך זה נראה כשנריץ על הדפדפן:

currency pipe בפעולה angular אנגולר פרמטר NIS

* העברת פרמטר דוגמת סימן ה-₪ תעבוד ב-Angular5 אבל לא בגרסאות מוקדמות יותר שעבורם נסתפק בסימון הבינ"ל של מטבעות באמצעות 3 אותיות.

כדי לקבוע את מספר הספרות אחרי הנקודה ניתן להעביר פרמטר שלישי:

/src/app/app.component.html
----------------------------------

<ul>
  <li *ngFor="let car of cars">
  {{car.price | currency:'NIS':'1.2-2'}}
  </li>
</ul>

והתוצאה לפניכם:

currency pipe הכולל מספר ספרות אחרי הנקודה

המשמעות של 1.2-2 היא:

  • לפחות ספרה אחת לפני הנקודה
  • לא פחות משתי ספרות אחרי הנקודה
  • לא יותר משתי ספרות אחרי הנקודה

 

3. שרשור של pipe

ניתן לצרף מספר pipes ביחד לשרשרת של הוראות. לדוגמה:

{{ car.price | currency:'NIS' | lowercase }}

זו התוצאה:

הדגמה של שרשור או צירוף מספר pipes

בין ה-pipes מפריד קו מאונך.

סדר כתיבת ה-pipes חשוב. בדוגמה זו, אם קודם נפעיל את ה-lowercase ורק אח"כ את ה-currency נקבל תוצאה שונה מהרצוי.

{{ car.price | lowercase | currency:'NIS' }}

מפני שסדר יישום ה-pipes על ידי אנגולר הוא משמאל לימין.

 

4. ה-Pipe שמציג ערכים ממקור א-סינכרוני

כשננסה להציג משתנה שמקורו א-סינכרוני, דוגמת ערך שמגיע מ-promise או מ-observable , ניתקל בבעיה.

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

/src/app/app.component.ts
-----------------------------------

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  welcomeMsg = new Promise((resolve, reject)=>{
    setTimeout(()=>{
      resolve("Hello, car owner!");
    }, 3000)
  })
}

/src/app/app.component.html
----------------------------------

<p>{{ welcomeMsg }}</p>

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

מה קורה כשלא משתמשים ב-pipe async על משתנה אסינכרוני

זו בעיה שנוצרה בגלל שהאפליקציה לא ידעה שעליה לחכות לשינוי בערך המשתנה בזמן כלשהו בעתיד כי המשתנה הוא א-סינכרוני. כדי לפתור את הבעיה נשתמש ב-pipe ששמו async.

/src/app/app.component.html
----------------------------------

<p>{{ welcomeMsg | async }}</p>

כך זה נראה אחרי שפתרנו את הבעיה, וה-promise סיפק את ערך המשתנה:

שימוש ב-pipe async של אנגולר פותר את הבעיה של שימוש במשתנים א-סינכרוניים

 

5. הדוקומנטציה של pipes

אתם מוזמנים ללמוד אודות ה-pipes השונים בתיעוד הרשמי: https://angular.io/api?type=pipe

 

6. כתיבת Pipe משלנו

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

כדי להוסיף pipe נשתמש בממשק שורת הפקודות cli בפקודה שזה התחביר שלה:

> ng generate pipe [pipeName]

וכדי להוסיף pipe ששמו shortString, נשתמש בפקודה:

> ng generate pipe shortString

הרצת הפקודה תגרום להוספת הקובץ:

short-string.pipe.ts
-------------------------

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'shortString'
})
export class ShortStringPipe implements PipeTransform {
  transform(value: any, args?: any): any {
    return null;
  }
}

  • בדקורטור מגדירים את שם ה-pipe.
  • ה-pipe מיישם את ה-interface ששמו PipeTransform, ולכן הוא חייב שתהיה לו מתודה transform, שמקבלת ערך של משתנה ופרמטרים נוספים שאינם הכרחיים. ה-pipe חייב להחזיר ערך.

ניגש לכתיבת הקוד שיהפוך כל מחרוזת שנכניס ל-pipe לערך הרצוי.

short-string.pipe.ts
-------------------------

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'shortString'
})
export class ShortStringPipe implements PipeTransform {
  transform(str: string, len: number): any {
    let newStr = str.substr(0, len);

    if(str.length > len){
      newStr += '...';
    }

    return newStr;
  }
}
  • ה-pipe מקבל שני פרמטרים. מחרוזת (str), ואורך רצוי של ה-output.
  • במידה ואורך המחרוזת ארוך יותר מהאורך הרצוי, ה-pipe יוסיף 3 נקודות.

 

כיצד נשתמש ב-pipe שיצרנו בקוד?

כדי ליידע את האפליקציה האנגולרית אודות ה-pipe שהוספנו צריך לייבא אותו לקובץ המודל-

src/app/app.module.ts
-----------------------------

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { ShortStringPipe } from './short-string.pipe';

@NgModule({
  declarations: [
    AppComponent,
    ShortStringPipe
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
  • צריך לייבא את ה-pipe וגם להוסיף לרשימת ה-declerations.
  • במידה והשתמשנו ב-cli כדי ליצור את ה-pipe, הוא יבצע את הייבוא בשבילנו.

נשתמש ב-pipe ב-html:

/src/app/app.component.html
----------------------------------

<h2>{{ title | lowercase  | shortString:5 }}</h2>

זו התוצאה:

שימוש ב-pipe שכתבנו במדריך כדי לקצר את אורכם של מחרוזות

 

7. Pipe שמבצע סינון

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

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

נתחיל מהוספת מערך המכוניות לקומפננטה:

/src/app/app.component.ts
-----------------------------------

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  title = 'List Of Cars';

  welcomeMsg = new Promise((resolve, reject)=>{
    setTimeout(()=>{
      resolve("Hello, car owner!");
    }, 3000)
  })

  cars = [
    {model: 'Tesla', price: 30000},
    {model: 'BMW', price: 47000},
    {model: 'Jaguar', price: 200000},
    {model: 'Ferrari', price: 400000},
    {model: 'Sussita', price: 4000}
  ];
}

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

נשתמש ב-cli כדי ליצור את ה-pipe ששמו less-than שבו נשתמש כדי לסנן את המכוניות שמחירם נמוך מהמספר שיזין המשתמש.

> ng generate pipe lessThan

וזה קוד ה-pipe:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'lessThan'
})
export class LessThanPipe implements PipeTransform {
  transform(arr: any, number: number, param: string): any {
    let filteredArray = [];

    if(arr.length === 0 || number < 1 || typeof param !== 'string') {
      return arr;
    }

    for(var i in arr){
      if(arr[i][param] <= number){
        filteredArray.push(arr[i]);
      }
    }

    return filteredArray;
  }
}
  • ה-pipe מקבל מערך ושני פרמטרים. number שהוא המחיר המקסימלי, ו-param שהוא שם השדה לפיו אנו מעוניינים לסנן.
  • לולאה, שעוברת על כל אחד מפריטי המערך, מסננת את כל הפריטים שבהם ערך השדה נמוך מהערך הנקוב, ודוחפת אותם למערך, שהפונקציה מחזירה.

נוסיף לקובץ ה-html את רשימת המכוניות ואת השדה שלתוכו יזין המשתמש את המחיר הגבוה ביותר שהוא מוכן לשלם תמורת מכונית.

/src/app/app.component.html
----------------------------------

<h2>{{ title | lowercase  | shortString:5 }}</h2>

<p>{{ welcomeMsg | async }}</p>

<p><label>Prices less than</label> <input type="text" [(ngModel)]="filterByPrice"></p>
<ul>
  <li *ngFor="let car of cars | lessThan:filterByPrice:'price'">
    <p>{{car.model}} <i>{{car.price}}</i></p>
  </li>
</ul>
  • הערך שאותו מזין המשתמש מועבר לעיבוד של אנגולר באמצעות קשירה דו-כיוונית למשתנה filterByPrice.
  • הפילטר מסנן את הרשימה, והוא מקבל שני פרמטרים. את filterByPrice ואת שם השדה price, שעליו מתבצע הסינון.

צפו בקוד בפעולה:

 

למדריכים נוספים בסדרת ה-Angular

הוסף תגובה חדשה

 

= 5 + 5

תמונת המגיב

שיר בתאריך: 10.11.2017

ניסיתי את הדוגמה האחרונה והיא לא עובדת לי.

תמונת המגיב

יוסי בן הרוש בתאריך: 10.11.2017

כדי ליישם קשירה דו-כיוונית צריכים לייבא את
FormsModule בקובץ app.module.ts
כתבתי על זה במדריך קשירה דו-כיוונית.

תמונת המגיב

משה בתאריך: 17.11.2017

עזר לי מאוד. תודה רבה.

תמונת המגיב

יוסי בן הרוש בתאריך: 17.11.2017

בכיף, משה.

תמונת המגיב

דניס בתאריך: 20.03.2018

מדריכים נהדרים! הדוגמא האחרונה לא עובדת לי בגלל שגיאה ב- filterByPrice: ng:identifier filterByPrice is not defined...