באנגולר אנחנו משתמשים בספריית rxjs בעיקר לטיפול במידע. rxjs משמשת לטיפול בקוד אסינכרוני, למשל בקשות http. הספרייה מחליפה את הצורך בשימוש ב-promises ומנגנון async-await.
נראה איך קוד rxjs מחליף קוד js.
תוכנית JS
נכתוב תוכנית js פשוטה בה יש תיבת טקסט שבה אנחנו עוקבים אחרי מה שהמשתמש מכניס לתוכה. אם משתמש מכניס מספר, נדפיס אותו לקונסול. אם משתמש מכניס משהו שהוא לא מספר, נציג שגיאה.
קובץ html
<input />
קובץ js
const input = document.querySelector('input');
input.addEventListener('input', (event) => {
let text = event.target.value;
text = parseInt(text);
if(isNaN(text)){
throw new Error('Enter a number');
}
logValue(text);
})
const logValue = (value) => {
console.log(`Your value is ${value}`);
}
יש לנו אלמנט input שהוא המקור לאירועים. אנחנו מקבלים דרך listener כשמתבצעת פעולה ומתחילים סדרה של פעולות. בחלק מהצעדים תהיה שגיאה ואז הביצוע יעצור. אם הכל בסדר הביצוע ימשיך והערך שמתקבל עובר לפונקציה לטיפול.
כך נראה תרשים של הפעולה בתוכנית js.
שימוש ב-Rxjs
נתרגם את הדרך שעוברים בתוכנית js למושגים של אנגולר.
Observable – מקור האירועים לאורך הזמן. אנחנו רוצים להאזין לאירועים האלה ולבצע פעולות בהתאם להם ולהשתמש בערכים שאנחנו מקבלים לביצוע פעולות שונות באפליקציה.
Operators – מקבלים ערך, ועושים עליו פעולות ומעבירים אותו לשלב העיבוד הבא. יכולים להיות כמה operators. כל ה-operators נכנסים לתהליך של pipe.
Observer – התוצאה של הפעולות תהיה או שגיאה או הכל בסדר. אם יש שגיאה, לא נמשיך את הפעילות ונטפל בשגיאה. אם הגענו לסוף נעביר את הערך למי שצריך להשתמש בו.
יש כלי להתנסות ויזואלית ב-rxjs בכתובת: https://out.stegrider.now.sh.
Observable
תוכנית פשוטה שעוקבת אחרי שינויים בתיבת טקסט:
const { fromEvent } = Rx;
const input = document.createElement('input');
const container = document.querySelector('.container');
container.appendChild(input);
// We follow after the input element and watch the input event
// We get an observable
// The observable emit events and we can do processing on them.
const observable = fromEvent(input, 'input');
// This is specific to the online tool only
observable;
בכל פעם שיש פעולה בשדה ה-input יש התרעה על אירוע ב-observable.
יש לנו אירועים, השלב הבא הוא ליצור רצף פעולות עיבוד, processing pipeline.
Operators
Operators הם פונקציות שמבצעות עיבוד לערכים שעוברים ב-pipe. ה-observable מפעיל אירוע והערך עובר לביצוע הפעולות שעליו. יש פעולה שמקבלת את הערך, פעולה שמעבדת את הטקסט וכו'
Map Operator
פעולת map מקבלת את הערך שיוצא מתוך ה-observable שלנו, מבצעת פעולה, והתוצאה ממשיכה לפעולות הבאות. אנחנו קוראים ל-pipe ומשרשרים בתוכו את כל הפעולות שאנחנו רוצים לבצע לפי הסדר.
const observable = fromEvent(input, 'input')
.pipe(
map(event => console.log(event))
)
נדפיס את ה-event ונראה שמקבלים אירוע html של הכנסת input.
נוכל להחזיר את הערך שהוקלד כמו שנגשנו אליו קודם דרך קוד ה-js. בקוד הבא אנחנו מחזירים מפעולת ה-map את הערך שהוקלד. הפעולה הבאה ב-pipeline שלנו לא תראה את ה-event, אלא רק את הערך שמוחזר מפעולת ה-map.
const observable = fromEvent(input, 'input')
.pipe(
map(event => event.target.value)
)
נבצע עוד פעולת map ונראה מה הערך שמגיע אליה.
const observable = fromEvent(input, 'input')
.pipe(
map(event => event.target.value),
map(value => console.log(value))
)
עכשיו אנחנו מקבלים ב-console את הערכים ולא את האירוע.
נבצע את התהליך שביצענו למעלה בתוכנית ה-js.
const observable = fromEvent(input, 'input')
.pipe(
map(event => event.target.value),
map(value => parseInt(value)),
map(value => {
if(isNaN(value)){
throw new Error('Enter a number');
}
return value;
})
)
עכשיו אם נכניס ערך מספרי, נקבל את הערך הזה.
ואם נכניס ערך שאינו מספרי נראה שפעולת ה-observale הופסקה.
Observer
השלב האחרון בתהליך הוא ליצור observer שיקבל את הערכים שיוצאים מה-pipe או מטפל בשגיאות.
בקריאה ל-subscripbe אפשר להעביר פונקציה או אובייקט. האובייקט יקבל את הפונקציה next ואת הפונקציה error. ישנה פונקציה נוספת שאפשר להוסיף שהיא complete.
הפונקציה next נקראת בכל פעם שיש לנו הפעלה של אירוע ויוצא ערך מה-pipeline שלנו. הפונקציה error נקראת אם יש שגיאה בכל אחד מהשלבים של ה-pipeline שלנו.
const observable = fromEvent(input, 'input')
.pipe(
map(event => event.target.value),
map(value => parseInt(value)),
map(value => {
if(isNaN(value)){
throw new Error('Enter a number');
}
return value;
})
)
observable.subscribe({
next(value){
console.log(`Your value is ${value}`);
},
error(err){
console.error('Error happened!', err.message);
}
});
נראה את התוצאה של הכנסת ערכים תקינים:
וזאת התוצאה בהכנסת ערך לא תקין:
תיעוד Operators
תיעוד על rxjs אפשר למצוא בכתובת: https://rxjs.dev. בכניסה ל-reference אפשר למצוא את כל הפונקציות שיש בספרייה.
מיון על פי פונקציות יתן במורד הדף את החלק של האופרטורים של הספרייה.
יש שלוש קבוצות מרכזיות של אופרטורים.
שינוי – לוקחים ערך, מבצעים תהליך ומחזירים ערך חדש שממשיך ב-pipleine. אופרטור ה-map הוא אחד המרכזיים בקבוצה הזאת.
פילטר – שליטה ברצף האירועים שעוברים ב-pipeline. למשל לאסוף אירועים למערך, לעצור אירועים וכו'
יצירה – פונקציות שיוצרות observables חדשים.
אופרטור map למשל, שהשתמשנו בו קודם, שייך לקבוצת פונקציות השינוי.