RSS

קטגוריה: Angular 2.0

Angular2 in memory web api

Angular2 in memory web api

 

באנגולר 2 אפשר להשתמש בספריה ‘angular2-in-memory-web-api’ כדי לבטל את התקשורת הפיזית לשרת ולעבוד מול מידע שנמצא בזיכרון, טוב לבדיקות.

שלב ראשון מחלקה שתחזיק את המידע בזיכרון. ראו קוד:

export class InMemoryDataService {
createDb() {
return { users:[
// users resource
{…},
// user object
{…},
// user object
…   
// users objects
]};
}
}

 

הדגשים:

1.     אנגולר יקרה למתודה createdDb בפעם הראשונה שתנסו לפעיל את המחלקה Http.

2.     ה- users מחליפים את ה- resource ב- web-API שעליו ניתן לעשות פעולות CRUD ע"פ החוקים של REST. למשל ‘app/users/12’ תביא את המשתמש עם id שווה ל- 12.

 

שלב שני בניית מערך לשימוש ה- Injector הראשי.

 

export const HTTP_IN_MEMORY = [
    HTTP_PROVIDERS,
    { provide: XHRBackend, useClass: InMemoryBackendService },
    { provide: SEED_DATA , useClass: InMemoryDataService },
    { provide: InMemoryBackendConfig, useValue: { delay: 600 } }
];
 
דגשים:

1.     השרות XHRBackend הוא זה שמבצע בפועל את התקשורת לשרת, לכן 
צריך להחליף אותו עם השרות InMemoryBackendService שבמקום לבצע
תקשורת יקרא ל- SEED_DATA, שזה בעצם המחלקה שאנחנו כתבנו, 
שיוצרת את מבנה הנתונים בזיכרון.

2.     השרות InMemoryBackendConfig מאפשר לנו לתת הגדרות של התנהגות,
כמו למשל המתנה של 600 MS בין הקריאה לחזרת התשובה. 
ניתן לראות את כל האפשרויות בממשק הבא:

export interface InMemoryBackendConfigArgs {
    // default response options
    defaultResponseOptions?: ResponseOptions;
    // delay (in ms) to simulate latency
    delay?: number;
    // false (default) if ok when object-to-delete not found; else 404     
    delete404?: boolean;
    // host for this service
    host?: string;
    // root path before any API call
    rootPath?: string;
}
 
 
שלב אחרון: חיבור ל- bootstrap.
 
bootstrap(App,[ HTTP_IN_MEMORY ]);
 
סיכום:

 

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

 

מאמר זה הוא חלק התוכן שאני מעביר בקורסים שלי.
הקורס הבא יהיה ב- 18 לספטמבר. לפרטים נוספים הירשמו כאן: ng-course.org
.

 

מודעות פרסומת
 
השארת תגובה

פורסם ע"י ב- יולי 4, 2016 ב- Angular 2.0, Uncategorized

 

Angular 2 The Clock Component

באנגולר 2 אחרי כל אירוע אסינכרוני יש tick שנקרה ע"י ה-zone. אם יש הרבה אירועים אסינכרוניים בשנייה אנחנו יכולים לגרום לבעיית ביצועים.

בפוסט זה אני רוצה להסביר איך לכתוב רכיב שעון בדיוק של 50 ms. בחרתי את הקוד הבסיסי  לשעון שלי:

setTime(){
let t = new
Date();
this.time =
`
${t.getHours()}

        :
${this.formatNum(t.getMinutes())}
        :
${this.formatNum(t.getSeconds())}
        :
${t.getMilliseconds()}`;
setTimeout(
this.setTime.bind(this),50
);
}
formatNum(i){
return i < 10 ? `0${i}`
: i; }

 

שימו לב:

אני משתמש ב- setTimeout ולא ב- setInterval כדי לא ליצור עומס על ה- queue במידה וה- UI Thread עמוס ואז הוא לא מצליח למשוך מהתור את הפונקציות בקצב שה- setInterval מיצר אותם.

 

שלב א: לעבוד מחוץ לאנגולר

אני לא רוצה שכל 50 ms יבוצע tick במערכת. (המשמעות של tick  היא שאנגולר עובר על עץ הרכיבים ובודק מה השתנה ואז מעדכן את ה- UI. ראו פוסטים בנושא. ) לכן אני אשתמש ברכיב ה- NgZone.

constructor(zone:NgZone) {}
ngOnInit(){
    this.zone.runOutsideAngular(()=> {
        this.setTime();
    });
}
 
הקוד הנ"ל מוציא את ה- setTimeout ממנגנון ה- zone. כלומר עכשיו ה- 50 ms לא גורמים ל- tick.
 

שלב ב: לעדכן את המסך

אני אשתמש ביכולות של ה- ChangeDetectorRef והפונקציה detectChanges, מחשבת את כל החלקים הדינאמים בתבנית (למשל {{}} או [] ). ראו קוד:

@Component({
    selector: 'clock',
    template: `<span>{{time}}</span>`
})
export class Clock extends BaseDemo{
    time:string = '00:00:00:000';
    isDestroy:boolean = false;
    constructor(zone:NgZone,
                cd:ChangeDetectorRef) {}
    ngOnInit(){
        this.cd.detach();
        this.zone.runOutsideAngular(()=> {
            this.setTime();
        });
    }
    setTime(){
        if(this.isDestroy) return;
        let t = new Date();
        this.time = `
            ${t.getHours()}
            :${this.formatNum(t.getMinutes())}
            :${this.formatNum(t.getSeconds())}
            :${t.getMilliseconds()}`;
        this.cd.detectChanges();
        setTimeout(this.setTime.bind(this),50);
    }
    formatNum(i){ return i < 10 ? `0${i}` : i; }
    ngOnDestroy(){
        this.isDestroy = true;
    }
}

 

סיכום:

יש הרבה רכיבים שיש להם תדירות גבוהה של עדכון מסך. שימוש ברכים אלו יגרמו ל- tick שיגרום לעדכן את כל האפלקציה. בפוסט זה אני מראה לכם איך לשלוט בתדר העידכון ע"י ניתוק מה- zone וקריאה ל- detectChanges כל פעם שרוצים לעדכן את הרכיב על המסך. עדכון זה לא גורם ל- tick.

כמו תמיד אשמח לפידבקים.

למידע נוסף בנושא, אשמח לראות אותכם בקורס אנגולר  2 שלי ng-course.

 
תגובה אחת

פורסם ע"י ב- יוני 25, 2016 ב- Angular 2.0, Uncategorized

 

Pure Pipe vs. Impure Pipe

 

בפוסט זה אני רוצה להסביר את ההבדלים בין שני סוגי ה- Pipe. בשביל להסביר את ההבדלים בניתי את שני ה-pipes הבאים, ראו קוד:

import {Pipe} from '@angular/core';
let counter:number = 0
;
export class MyPipe implements
PipeTransform {
    counter2:
number = 0
;
   
constructor
(){ counter++; }
    transform(value:
string,name:string
) {
       
return `n: ${name} v: ${value} c2: ${this.counter2++} c1:${counter}`
;
    }
}
@Pipe({ name:
'myPurePipe', pure: true
})
export class MyPurePipe extends
MyPipe{
   
constructor(){ super
(); }
}
@Pipe({ name:
'myImpurePipe', pure: false
})
export class MyImpurePipe extends
MyPipe{
   
constructor(){ super
(); }
}

 

נשתמש ב-Pipes באופן הבאה:

myPurePipe  : {{ 1  | myPurePipe  : 'a' }}

myPurePipe  : {{ 1  | myPurePipe  : 'a' }}
myImpurePipe: {{ 2  | myImpurePipe: 'b' }}

myImpurePipe: {{ 2  | myImpurePipe: 'b' }}

 

תוצאות:

 

Pipe

Type

Num of Instances

Num of Executes

myPurePipe

Pure

1

2

myImpurePipe

Impure

2

Many == Ticks

 

הסברים:

א.    Pipe מסוג pure לא נוצרים מספר מופעים, כלומר זה singleton. המתודה transform מופעלת כאשר יש tick רק עם ה- inputs שונים מה-tick הקודם. במקרה שלנו 1 ו- ‘a’ לא משתנים ולכן ה- myPurePipe  פועל רק פעם אחת  כפול מספר הפעמים שאני משתמש בו בתבנית, כלומר 2.

ב.     Pipe מסוג impure נוצר instance כמספר הפעמים שהוא מופיעה בתבנית. בנוסף הוא מופעל כל tick.

שאלה: באיזה סוג של pipe אני צריך להשתמש?

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

Pipe שהוא impure הוא לא דטרמיניסטית ולכן צריך להפעיל אותו בכל tick. יש סבירות גבוהה שאנחנו נצטרך לשמור נתונים שונים לכל pipe שמופיע בתבנית ולכן אנחנו צריכים מספר מופעים כדי לשמור את הנתונים השונים. דוגמא:

@Pipe({ name: 'sumRandomPipe', pure : false })
export class SumRandomPipe implements
PipeTransform {
    sum:
number = 0
;
    transform(min:
number, max:number
) {
       
this.sum +=  Math.floor(Math.random() * (max – min + 1
)) + min;
       
return this
.sum;
    }
}

 

ביצועים:

כל אירוע אסיכרוני, timers ,UI ותקשורת גורמים ל-ticks מכאן נובע שב- impure צריך לבצע כל פעם מחדש את המתודה transform בלי קשר אם הקלט השתנה או לא, זה יכול לגרום לנו לבעיית ביצועים. בנוסף לא נשכח שלכל שימוש ב-pipe נוצר instance ושימוש מופרז יכול ליצור בעיות של זיכרון.

כמו תמיד כל תגובה מבורכת.

אשמח לראות אותכם בקורס שלי על AngularJS 2.

 

 

 
השארת תגובה

פורסם ע"י ב- יוני 15, 2016 ב- Angular 2.0

 

DoCheck vs. DoChanges

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

1. זיהוי הצורך בעידכון המסך (NgZone):

מנגנון זה אחראי על הפעלת מנגנון העידכון (tick). בלי הפעלת ה-tick לא יתעדכן המסך.  איך NgZone עושה את זה ניתן לקרוא כאן.

2. זיהוי השינוים ב- State של ה- Component וה- Directive ועידכון התבנית בהתאם:

מנגנון זה נקרה ChangeDetector. לכל Component נוצר ChangeDetector יחודי ע"פ התבנית של ה- Component. כל ביטוי ( השימוש ב- {{expression}} או [property]='expression' ) בתבנית יוצר ב- ChangeDetector סוג של watcher. ניתן לקורא על זה כאן וכאן.

3. מעבר על עץ ה- Components:

כאשר מופעל ה- tick אנגולר בודק את השינויים ע"פ סעיף 2 ב- Root Component. בנוסף הוא הוא עובר לילדים של Root Component. האבא מעביר לבן את השינויים דרך ה- inputs שמוגדרים בבן. במילים פשוטות האבא מבצע השמה לתכונות של הבן אם הוא מזהה שינוי ב- inputs. ראה דוגמא:

clip_image002

זו התבנית של האבא. כאשר ה- ChangeDetector של האבא מזהה שה- expression קיבל ערך חדש, מתבצע השמה של הערך החדש ב- property1 של רכיב הבן.

לפני שמתבצעת ההשמה ב-property1 מופעלים אצל הבן ה-hooks  שהוא מימש. עכשיו הגענו לרגע האמת שאפשר להסביר את ההבדל בין שני ה- hooks, הראשון ngOnChanges והשני ngDoCheck.

בדוגמא שלמעלה אם ה- expression שווה לערך מסוג primitive , האנגולר מסוגל לזהות שינויים בערך של ה- expression. במקרים אלו אם נשתמש ב- ngOnChanges אנגולר יספק לנו ערך ישן וערך חדש.

שאלה: למה צריך את ה- ngOnChanges אם בסופו של דבר נגיע ל- setter של ה- property ששם אנחנו יודעים גם לזהות ערך קודם וערך חדש?

תשובה: אכן השימוש בזה לא שכיח, רוב ה- Directives של אנגולר לא משתמשים בזה.  NgModel משתמש ב- ngOnChanges, ע"פ דעתי אפשר לכתוב את הקוד הנ"ל גם בלי ngOnChanges.

שאלה: האם אנגולר מסוגל לזהות שינויים בתוך אובייקט או מערך ב- Input?

תשובה: לא.

בדוגמא למעלה אם ה- expression מחזיר אובייקט, אנגולר לא מסגול לזהות שינויים באובייקט ( זה כולל גם מערכים ) וכתוצאה מכך גם ה- setter של ה- property לא יתבצע, כאשר יש שינוי באובייקט.

שאלה: מה עושים?

תשובה: ngDoCheck.

כאשר אנחנו צריכים Input מסוג אובייקט או מערך אנחנו צריכים להשתמש ב- ngDoCheck. הפעלת המתודה ngDoCheck אומרת לכותב ה- Component זה הזמן שלך לבדוק מה השתנה ולעדכן את ה- properties בהתאם.

שאלה: איך בודקים מה השתנה באובייקט או במערך?

תשובה: באנגולר יש את שני המחלקות SimpleChange ו- IterableDiffers שבעזרתם אנחנו יכולים לזהות שינויים. פוסט בנושא ניתן לקורא כאן.

ראו טבלה של Directives שמשתמשים במחלקות אלו:

Differ Class

Hook

Input Type

Directives

SimpleChange

DoCheck

Object

NgClass

SimpleChange

DoCheck

Object

NgStyle

IterableDiffers

DoCheck

Array

NgFor

 

שאלה: אני שאלה קטנה, למה אנחנו צריכים לדעת את כל זה?

תשובה: ביצועים…

יש מספר פרמטרים שמשפיעים על הביצועים:

1. מספר ה- expressions ב- Template.

2. תדירות ה- tick.

3. גודל העץ שצריך לסרוק ב- tick.

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

 

איך עושים את זה בפוסט הבא.

אם אהבתם את הפוסט אתם מוזמנים לבוא לקורס שלי ב-Angular 2.

לפרטים לחצו כאן.

 
תגובה אחת

פורסם ע"י ב- יוני 2, 2016 ב- Angular 2.0

 

Angular 2.0 ViewResolver Class

אחת השאלות הנפוצות היא, האם אפשר לכתוב פונקציה שמחזירה URL במקום URL סטאטי ב- @Component. באנגולר 1 זה היה אפשרי, ראו פוסט בנושא.

באנגולר 2.0 זה גם אפשרי, אך זה טיפה יותר מסובך.

ראו קוד:

class myViewResolver extends ViewResolver{
    resolve(component: Type): ViewMetadata {       
       
var view =  super
.resolve(component);
       
//
TODO: Write logic here 🙂
       
view.templateUrl = 'app/app.html'
;
       
return
view;
    }
}
bootstrap(App,[
    provide(ViewResolver , {useClass:myViewResolver})
]);

 

הסברים:

אני דורס את ה- ViewResolver הקיים וכותב את שלי שעוטף את הקיים. עטיפה זאת מאפשרת לי לשנות את ה-URL של התבנית ע"פ לוגיקה מסוימת. דוגמאות:

1. להוסיף ל-URL פרמטר של שפה. App.{leng}.html  ה- {leng}  יוחלף בזמן ריצה לשפה המבוקשת.

2. להוסיף ל-URL פרמטר של הרשאה. App.{role}.{leng}.html.

מזכיר לכם שאני יכול להזריק ל- MyViewResolver שרותים אחרים, למשל שרותים שאחראים על שפה והרשאות.

 

מקווה לפגוש אותכם ב-meetup  הקורב שאנ מעביר.

 
תגובה אחת

פורסם ע"י ב- אפריל 6, 2016 ב- Angular 2.0

 

Angular 2.0 Global Decorator

 

בפוסט זה אני רוצה להראות איך אפשר לכתוב @Global Decorator שירשום לנו את ה- Component ל-Injector של האפלקציה.

import {Component, Input, ViewEncapsulation} from 'angular2/core';
import userTpl from './user.tpl.html!text'
;
import userCss from './user.css!text'
;
import {Global} from "../utils/GlobalUtil"
;
@Global()
@Component({
    selector:
'user'
,
    template : userTpl,
    styles:[userCss]
})

export class
UserCmp{
   
private _name:string
;
   
get name(){return this
._name;}
    @Input(
'bp-name'
)
   
set name(val){this
._name = val;}
}

 

ע"י ההוספה של @Global אנחנו יותר לא צריכים לעשות כלום כדי להשתמש ב- UserCmp. להלן הקוד של Global:

import {bootstrap as bp}    from 'angular2/platform/browser';
import
{
provide,
PLATFORM_DIRECTIVES,
PLATFORM_PIPES
}
from "angular2/core";

var providers:any[] = [];

function
addPipe(pipe){
providers.push(provide(PLATFORM_PIPES, {
useValue: [pipe],
multi:
true
}))
}

function
addDirective(directive){
providers.push(provide(PLATFORM_DIRECTIVES, {
useValue: [directive],
multi:
true
}))
}

function
addService(service){
providers.push(service);
}


export function
Global(){
return
(target)=>{

Reflect.getMetadata(
'annotations'
, target)
.forEach(a => {
if( a.constructor.name === 'InjectableMetadata'
){
addService(target);
}
else if (a.constructor.name === 'PipeMetadata'
){
addPipe(target);
}
else
{
addDirective(target);
}
});
return
target;
};
}


export function bootstrap(type,prvs:any
[]){
return
bp(type,providers.concat(prvs));
}

 

 

הסברים:

1. הפונקציה Global רושמת כל Component למערך שאותו אני מכניס ל-Injector של האפלקציה.

2. כדי להריץ את האפלקציה צריך להשתמש ב- bootstrap שאני כתבתי ולא של אנגולר.

 

סיכום:

עבודה בדרך זו מקלה מאוד את הדרך לארוז מספר Components לקובץ אחד, כדי להשתמש ברכיבים אלו כל מה שאתם צריכים זה לטעון את הקובץ. בפוסט הבא אני אראה איך אפשר לדרוס את @Component Decorator כדי לחסוך לאפשר config מרכזי לכל ה- Components.

 

Ng-Course:

הקורס הבא על אנגולר 2.0 מתחיל ביום רביעי ה-6 לחודש. למידע הירשמו כאן.

 
השארת תגובה

פורסם ע"י ב- מרץ 31, 2016 ב- Angular 2.0

 

Angular 2.0 – Relative template and styles URLs

This post will show how to work with systemjs and text plugin to achieve relative template and styles URLs.

Step 1:

Download the SystemJs Text plugin.

Step 2:

For SystemJS use, locate text.js in the application, and then locate it with map configuration:

System.config({

  map: {

    text: 'path/to/text.js'

  }

});

 

Step 3:

Imports the html and css file into the module and then use it in @Component decorator. See code.

import {Component} from 'angular2/core';
import html        from './SubComp.html!text'
;
import css         from './SubComp.css!text'
;

@Component({
selector: 'sub-app',
template: html,
styles  :[css]
})

export class
MyComponent {}

 

 

Summary:

With relative URLs it more easy to organize each Component and it materials (HTML and CSS) in one folder.

If we need to move the folder it will be very easy to do it, without to update the template and styles URLs in the @Component decorator.

 
2 תגובות

פורסם ע"י ב- מרץ 26, 2016 ב- Angular 2.0