RSS

קטגוריה: AngularJS Tips

NgModule Decorator Deep Dive

 

In this post I want to emphasize some facts that help you understand the “ANGULAR MODULES (NGMODULE)” article.

1.          The services classes have different behaviors from Components, Directives and Pipes (CDP) in Angular Injector.

a.     Service behaviors in injector:

                                        1.     Singleton Instance – When you register a service to the Injector, you get a singleton instance.

                                        2.     Bubble – When a component wants to inject a service, Angular first looks at the component injector (child) and if the service doesn’t exist, it searches in the parent component injector (“bubble”). The Injector continues to go up until it finds the service. The root parent Injector belongs to platform object.

 

b.      CDP behaviors in injector:

                                        1.     Not singleton – When you use a directive or component in a template, you get a new instance of the directive/component. For example, if you use same directive number of times in a template, you get many instances of the directive.
 

                                        2.     Not bubble – You can’t register a directive in a parent component and use it in a child component.

 

2.          If a module loads normally, it doesn’t create an injector. If a module loads in a lazy way, it creates an injector for the module scope.

3.          Split the properties of NgModel Decorator to two groups: one for services and one for CDP.

a.     Group I – Properties for services classes:

                                        1.     Providers

b.     Group II – Properties for CDP classes:

                                        1.     Declarations

                                        2.     Imports

                                        3.     Exports

                                        4.     Bootstrap

 

Now we can start to explain the NgModule properties:

 

Group I – Properties for services classes:

§  @NgModule.providers – all the classes listed here are registered to the application injector as singleton (not in each component or directive metadata @component.providers that declares in @NgModule.declarations property). Anybody can ask for this singleton instance.
We have one exception; the lazy module has it on Injector. Meaning, the services listed in module providers are registered in the module injector instead of the application injector. It makes this service only available
to this module.

 

Group II – Properties for CDP classes:

§  @NgModule.declarations – all the CDP listed here know each other. It is the same as to register each component (@Component.directives) to all the other’s components. This property affects only the classes (CDP) defined in this property.

Example: declarations = [Compoent1, Component2, Component3, Directive1, Directive2, Pipe1, Pipe2 ]


clip_image002

 

Note: Each component knows all the others. Directive and pipe don’t have a template so they don’t need to know anybody.

 

§  @NgModule.exports – List of components, directives and pipes (CDP) that will be visible to modules that import this module.

 

§  @NgModule.imports – Everything from the imported modules that declares as an export, in the imported modules, will be available to declarations of this module.

 

Example:

1.     ModuleA ( declarations=[ Component1], imports:[ModuleB] )

2.     ModuleB ( declarations=[ Component2, Component3], exports:[ Component3] )

clip_image004

                        Component1 can use Component3 in a template but can’t use Component2.

 

§  @NgModule.bootstrap – Array of components to bootstrap. Angular loads this array to the DOM during the bootstrap (application launch) process. Use it only in root module (main).

Summary
This post is not instead of reading the ANGULAR MODULES (NGMODULE)” article, it just to emphasize some points in the article to make it more readable. I hope this information is helpful for you; send feedback please :=).

The next course opens on 18 September. For more information click here.

 

 
השארת תגובה

פורסם ע"י ב- אוגוסט 30, 2016 ב- Angular 2.0, AngularJS Tips

 

Custom Group Validation in Angular 2

 

Angular 2 has improved the way we write custom validation. There are a lot of articles of how to do it. In this post I want to focus on how to build custom group validation in model driven development. I will build a custom group validation (CGV) that checks each item in the formArray that has a unique value.

The code for the model:

namesArray = new FormArray([], this.customGroupValidation );
myForm =
new
FormGroup({
    inputName:
new FormControl("
),
    names    :
this
.namesArray
});

 

I use the FormArray class instead of FormGroup in the names field because the number of names is unknown; it is dynamic. The user can add multiple names.

 

The template code:

 

<form [formGroup]="myForm">
    name : <
input type="text" formControlName="inputName"
>
    <
button (click)="add()">Add</button><br
>
    <
hr
>
    <
div class="left"
>
        names: <
br
>
        <
ul formArrayName="names"
>
            <
li *ngFor="let item of namesArray.controls; let i = index"
>
                <
input type="text" [formControlName]="i"
>
                <
button (click)="removeAt(i)">X</button><br
>
            </
li
>
        </
ul
>
        <
div *ngIf="namesArray.hasError('duplicate')"
>
            duplicate entries
        </
div
>
    </
div
>
</
form
>

 

clip_image002

The code for add and remove names:

add(){
   
this
.namesArray.push(
       
new FormControl(this.myForm.get('inputName'
).value)
    );
}
removeAt(i:
number
){
   
this
.namesArray.removeAt(i);
}

 

Now let’s see the custom group validation method. I used groupBy method of lodash library to detect if there are any groups of more than one item, which means that we have duplicate names.

customGroupValidation (formArray) {
   
let isError = false
;
   
var
result = _.groupBy( formArray.controls , c => c.value );
   
for (let prop in
result) {
       
if (result[prop].length > 1
) {

            isError = true;
            _.forEach(result[prop],
function
(item:FormControl) {
                item._status =
"INVALID";

           
});
        }
else
{
            result[prop][
0]._status = 'VALID';           

       
}
    }
   
if(isError){ return {'duplicate':'duplicate entries'
}}
}

 

The result of the custom group validation is that every time we add or change name to a value that already exists in the formArray, the two formControl with the same value will be invalid. In addition the parent formArray will also be invalid.

clip_image004

If I delete or change one of the duplicates names, the form will go back to be valid (in this case ‘Eyal’ name).

I hope this information was helpful for you. Try the live example.

This is one of many examples that I show in my AngularJS 2 course (.html"ng-course).

The next course opens on 18 September. For more information click .html"heHYPERLINK "http://ng-course.org/ng-course/courses/angular2.html"rHYPERLINK "http://ng-course.org/ng-course/courses/angular2.html"e.

 
השארת תגובה

פורסם ע"י ב- אוגוסט 28, 2016 ב- Angular 2.0, AngularJS Tips

 

Tips and Tricks 1 – Angular 2 Routing

When you use RouterModule.forRoot method to register the routes for the router object, the method has a second parameter of type ExtraOptions interface.

export interface ExtraOptions {
enableTracing?:
boolean
;
useHash?:
boolean
;
}

 

enableTracing = true :

Print to the console the navigation events: NavigationStart, RoutesRecognized and NavigationEnd. It can be very helpful information.

 

useHash = true:

Change the location strategy from path to hash. This is a short version of:

{ provide:LocationStrategy, useClass: HashLocationStrategy }. In the development environment I prefer to work with hash because it doesn’t break in F5 (reload).

The next Angular 2 course (ng-course.org) is going to be on 18, 21, 25, & 28 in September.

 
השארת תגובה

פורסם ע"י ב- אוגוסט 21, 2016 ב- Angular 2.0, AngularJS Tips

 

Filter Performance Issue

בפעם הראשונה שקראתי על Filter של אנגולר חשבתי שזה אחד הדברים היפים שיש באנגולר, בעיקר בגלל הפשטות שלו.

בקוד הבא אנחנו רואים דוגמא קלאסית לכוח והפשטות של Filter באנגולר.

clip_image002

בדוגמאות קוד זו אנחנו רואים איך אפשר לכתוב ביטוי שלוקח את רשימת השמות ומכניס אותם לפונקציה OrderBy וארגומנט שני 'name' התוצאה נכנסת לפונקציה filter וארגומנט השני הוא הערך של שדה ה- search. הערך שמוחזר מהפונקציה filter הוא רשימת השמות שתכנסת ל- ng-repeat.

בנוסף אפשר גם לכתוב custom filter ולהשתמש בהם באותו אופן.

בעיות ביצועים עם Filters:

אנגולר מעדכן את המסך כל פעם שהוא מזהה שינוי על העץ של ה- scopes ע"י בדיקה של ה- watchers שמאוחסנים על ה- scopes. בדיקה זו מתבצעת כאשר קוראים למתודה $scope.$apply(), למשל שמשתמשים ב- ng-click, ng-model, $http. למעשה כל directive שמאזין לאירוע ( למשל UI , תקשורת ) מבצע בסוף הפעלה של פונקצית $apply. כל פעם ש- directive מפעיל את $apply עושים בדיקה לכל ה- watchers שקיימים על ה- scopes כלומר מפעילים גם את הביטוי:

names| orderBy:'name'| filter:search

גם אם הערכים של ה- search וה- names לא השתנו. יותר גרוע מזה, אם אחד ה- watchers מזהה שהיה שינוי חוזרים ובודקים את כל ה- watchers שוב, כלומר גם את הביטוי של ה- filters.

סיכום הבעיה:

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

פתרון:

 להוציא את חישוב ה- filters מה- watchers, כלומר שהם לא יכתבו בתוך ה-HTML.

ראו קוד לדוגמא:

clip_image004

clip_image006

הסבר:

פונקציה calc רצה רק כאשר השדה ה- search או השדה names משתנה ולא כל פעם שמתבצעת פונקציה $apply.

 

סיכום:

השימוש ב- filters ב-HTML יצור watchers שרצים כל $apply וזה יכול ליצור לנו בעיית ביצועים, בעיקר אם התדירות של הפעלת הפונקציה $apply גבוהה וה- Filters השונים רצים על רשימות גדולות.

הפתרון לעביר את החישוב מ- HTML לקוד. הפעלת החישוב תתבצע רק כאשר שדה שיכול להשפיע על החישוב שונה.

 
תגובה אחת

פורסם ע"י ב- אוקטובר 22, 2015 ב- AngularJS, AngularJS Tips, ng-course.org

 

AngularJS Tip 5: AngularJS Arguments

כל מי שמכיר אנגולר יודע שצריך לעבוד על ה-this בתוך controller ולא על ה- $scope. כתבתי על זה הרבה פוסטים שבהם אני מסביר את החשיבות לשים את המתודות על ה- prototype. כיום אני רוצה להראות לכם פונקציה שתמתיק לכם את הגלולה.

כיום שאתם שמים את כל הפונקציות על ה- prototype אתם חיבים לשים את כל ה- arguments על ה-this כדי שיהיה לכם גישה אליהם בתוך הפונקציות שעל ה- prototype. בפוסט AngularJS Tip 4: Dynamic Prototype Patter אני מדגים פתרון אך הוא בעייתי ב- ES6. אז כתבתי את המתודה הבאה:

function argsHelper(annotateNames, annotateValues){
for(var i = 0; i < annotateNames.length; i
++){
this[annotateNames[i]] = annotateValues[i
]
}
}

 

function MainCtrl($http,$log, $scope) {
    argsHelper.call(this, MainCtrl.$inject, arguments);    
    // Now $http, $log and $scope on the this object.

}
MainCtrl.prototype.getFoo = function(){
    this.$http.get('api/foo')
        .then(function(res){
            this.result = res.data;
        }.bind(this));
}
 

 

כמו שאתם רואים הפונקציה  argsHelper יודעת לשים על ה-this את כל ה- arguments, ולכן הם נגישים עכשיו לפונקציות שמוגדרות על ה- prototype.

אשמח לשמוע את דעתכם J

לאנשים שרוצים ללמוד יותר על אנגולר ו-JavaScript ממליץ לכם בחום לבוא לקורס שלי באוגוסט.

 
7 תגובות

פורסם ע"י ב- יוני 29, 2015 ב- AngularJS Tips

 

Master Template in AngularJS

Master Template in AngularJS

 

אנגולר מאפשר לנו לכתוב תבניות מאוד חכמות, וע"י Directives אנחנו יכולים לארוז אותם לתוך תג. למשל בדוגמא של accordion ו- expander, התוצאה הסופית היא שזה מתורגם לHTML מאוד גדול ביחס לתבנית.

<accordion>

   <h1>Eyal – {{date}}</h1>

   <expander class='expander'

             ng-repeat='item in expanders'

             expander-title='{{item.title}}'>

        {{item.text}}

   </expander>

</accordion>

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

דוגמאות למצבים שאני חושב שגישה זו עושה הגיון.

דוגמא 1: Binding Once or Not

תבנית עם Binding Once שלפעמים אני רוצה לבטל את ה- Binding Once. למשל התבנית הבאה:

clip_image002כאשר אני מסתכל על רשימה של פריטים אני רוצה שהתבנית של המוצר תיהיה עם יכולות של Binding Once ואילו כשאני מסתכל על מוצר בודד אני רוצה שאותה תבנית תיהיה Two way Data-binding. כלומר ההבדל היחידי בין התבניות זה ה- "::" שצריך להוסיף לפני שם המשתנה כדי לקבל binding once {{expression::}}. זה מקרה קלאסי לבנות Master Template שיקבע עם יהיה לפני שם המשנה ה-"::" או לא ע"י פרמטר.

 

דוגמא 2: שפות

כאשר אנחנו רוצים תמיכה של שפות בתוך התבניות אפשר להשתמש ב- angular-translate. אבל אפשר גם להשתמש בפתרון של Master Template ואז אנגולר יעבוד פחות קשה וגם יהיה יותר קל להבין את התבניות.

מה משותף לשתי הדוגמאות האלו? שה-MT יכול להכין את התבניות ע"פ מידע שקיים בזמן "קימפול" ואנגולר עובד על התבניות שנוצרו בזמן ריצה. ראו שרטוט.

clip_image004

אם לא רוצים להכין מראש את התבניות ע"פ שפה, אפשר להעביר את ה-MT לצד השרת ואז להוסיף ל-URL ערך שמציין את השפה הרצויה. ( לדוגמא : http://evardi.com/product.html?lag=heb  ) פתרון לגישה זו פרסמתי בפוסט AngularJS Tips 1–Directive TemplateURL.

כאשר לא רוצים לערב את השרת, אפשר לעשות אותו דבר בצד של אנגולר.

השאלה שמתעוררת היא  באיזה טכנולוגיה נשתמש כדי ליצור את התבניות של אנגולר? אפשר handlebars למשל, אבל למה לא להשתמש בפתרון ש- EcmaScript 6.0 מביא לשולחן?

לדוגמא:

(function (angular) {
"use strict"
;
///////////// AngularJS Code ///////
var mi = angular.module('myApp'
, [])
.factory(
'lang'
,langFactory)
.directive(
'helloWorld'
,helloWorldDirective);

function langFactory(){
return
{
he:{ hello : "
שלום" },
en :{ hello : "hello"
}
};
}

function helloWorldDirective(lang,$log){
return
{
template :
function
($compileNode, templateAttrs){
var
template = `<div>
<b> ${lang.he.hello}<b> : {{
name}}

                  </div>`;
return template.toString();
}
};
}
})(
angular
);

 

הסברים:

כאשר אנגולר מקמפל קטע HTML והוא מזהה את הדיראקטיב hello-world הוא קורא ל-template. ואז נוצרת תבנית עם השפה הרצויה שאותה אנגולר מקמפל. הבעיה שאם הדיראקטיב נמצא מספר פעמים על הדף הפונקציה של template תקרא מספר פעמיים. לכן צריך להוסיף מנגנון של Chace. אם חושבים על זה פעם שניה אפשר בשלב ה-run לעבד את התבניות ולהכניס אותם ל- $templateChaceואז הדראקטייב לא צריך פונקציה מיוחדת ואפשר לחזור לעבוד עם templateUrl.

 

דוגמא:

(function (angular) {
    "use strict";
    ///////////// AngularJS Code ///////
    var mi = angular.module('myApp', [])
        .factory('lang',langFactory)
        .directive('helloWorld2',helloWorldDirective2)
        .run(function($templateCache,lang){
            var template = `<div>
            <b> ${lang.he.hello}<b> : {{name}}
            </div>`;
            $templateCache.put("helloworld.html",template.toString());
        });
    function langFactory(){
        return{
          he:{ hello : "שלום" },
          en :{ hello : "hello" }
        };
    }
    function helloWorldDirective2(lang,$log){
        return{
            templateUrl : "helloworld.html"
        };
    }
})(angular);
 
הסברים:
בשלב העלייה של אנגולר אנחנו בונים את התבניות ומכניסים להם את התרגומים. 
כמובן שאפשר להמשיך לשפר את המנגנון ולעבוד עם קבצים, לעבד אותם ואז להכניס אותם 
ל-$templateCache. אני אישית חושב שצריך לבצע את זה בצד השרת, הכי טוב מבחינת ביצועים.
 
דוגמא 3:
יש הרבה דיראקטייב שהם statics, כלומר הם מקבלים דרך ה- attributes ערכים קבועים כלומר 
אי אפשר לחבר את ה-attributes האלה ל-data binding ואז הם מוחלפים בתבנית. 
כלומר הערכים של ה- attributesנקבעים בזמן כתיבת ה-HTML.

למשל:
<image-button img="face.jpg" imgAlign="left">
    Hello World
</image-button>
 
הדיראקטייב הזה מיצר כפתור שבנוי מתמונה וטקסט ואפשר לקבוע עם התמונה תהייה באחד 
מהצדדים ימין, שמאל, למטה או למעלה. הדראקטייב הזה הואstatic  הוא נועד רק כדי לחסוך 
שורות קוד ב- HTML. 
 
דארקטייב כזה מאוד קל כאשר כותבים אותו באנגולר בצורה נאיבית. הבעיה שמהר מאוד י
וצרים scope ו- watchים, דברים אלו יוצרים בעיות ביצועים שלא לצורך.
 
פתרון:
 
function imageButtonDirective($log){
    return{
        template : function($compileNode, attrs){
            var template = `<div>
            <img src=${attrs.img} align=${attrs.imgalign}/>
            ${$compileNode.html()}
            </div>`;
            $log.debug(template.toString());
            return template.toString();
        }
    };
}
 
כאשר אנחנו בונים את התבנית ב- template אנחנו מקבלים מספר יתרונות:
1. שימוש בדראקטייב זה ב- ng-repeat הפונקציה ליצירת התבנית תופעל רק פעם אחת. שימו לב, 
אפשר לעשות את זה גם במתודה compile, אך יש בזה שתי חסרונות:
A.      אי אפשר להוסיף דראקטייב בשלב ה- compile בלי לקמפל שוב את התבנית עם $compile.
B.      קידוד תבניות הרבה יותר נוח בשיטה declarative מאשר בשיטה של imperative.
2. אפשר לעבוד עם Chace ע"פ הערכים שמקבלים ב- attributes ולכן מקבלים ביצועים טובים יותר.
3. גם במקרה הזה אפשר היה לעבד את המידע בצד השרת ואפילו להשתמש בתוכנה כמו grant כלומר 
לבצע את העיבוד בזמן ה- build.
 
סיכום:
החיים הם לא שחור או לבן, יש הרבה מאוד אפור...
במקרים מסוימים כאשר אפשר לעבד את ה-HTML בזמן ה-build עדיף לא להשתמש בטכנולוגית 
האנגולר כי נשלם על זה בביצועים שלא לצורך.
 
אשמח לקבל תגובות !!!
 
אם אהבתם את הפוסט ואתם רוצים להמשיך ולהעמיק בנושא, אשמח לראות אותם בקורס 
שלי על אנגולר למתקדמים.

 

 

 

 
2 תגובות

פורסם ע"י ב- פברואר 14, 2015 ב- AngularJS, AngularJS Tips

 

AngularJS Tip 4: Dynamic Prototype Pattern

אחד הדברים שמאוד מציק לי ב-JavaScript זה השימוש המסיבי ב- this בפונקציות שנמצאות ב- prototype.

function UserCtrl($http,$log,bl){
this._bl
= userBL;
this._$http
= $http;
this._$log
= $log;
// more code
}
UserCtrl.
prototype.update = function
(name,address){
this._bl.update.call(this,name,address.street,address.house
);

    // this._$log(‘this and that’);
//
this
._$http(‘http://this.com’);

};

 

 

אנגולר מזריק לתוך ה-constructor את השירותים, $http, $log, bl ואני חייב לשים אותם על ה- this אם אני רוצה לגשת עליהם בפונקציות שנמצאות על ה- prototype. הסיבה שבגללה אני שם את הפונקציות על ה- prototype כדי שהם לא ישוכפלו כל פעם שקוראים ל- constructor.
ב-
ECMAScript 6 הכתיבה של class תהפוך לקוד עם prototype וגם בשיטה הזאת אפשר לבצע Derived class. דוגמא ל- class ב- ECMAScript 6
.

class UserCtrl {
    constructor($http,$log,bl) { //class constructor
        this._bl = userBL;
        this._$http = $http;
        this._$log = $log;
    }

    update(name,address) { //class method
        this._bl.update.call(this,name,address.street,address.house);
    }
}

 

כמו שאנחנו רואים גם בגירסה 6 ה- this נשאר במתודה של ה-update.

פתרון: Dynamic Prototype Pattern

הסיבה שבה חייבים להשתמש ב-this בתוך הפונקציות כי הם לא מוגדרות בתוך ה- constructor ולכן הם לא יכולות לעשות closure לארגומנטים. אם נשים את המתודות בתוך ה- constructor אנחנו צריכים לוודא שההשמה שלהם לא תקרה יותר מפעם אחת. בגלל שבאנגולר כל ההזרקות שלו הם singleton אפשר לפתור את זה…

function UserCtrl(userBL){
    this.name = userBL.name;
    this.address = userBL.address; 

    // Dynamic Prototype Pattern
    if (typeof this.update != "function"){
        UserCtrl.prototype.update = function(name,address){
            userBL.update.call(this,name,address.street,address.house);
        };
    }

}

 

הסבר:

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

 

זהירות:

אפשר להשתמש בתבנית הזאת וע"י כך להוריד את ה- this בתוך המתודה רק לארגומנטים שב- constructor בגלל שכל הארגומנטים שמוזרקים ע"י ה- $injector באנגולר הם singleton.

זה לא יהיה חכם לגשת ל- var’s שמוגדרים ב-constructor כי הגישה היא רק ל- var’s של ה- constructor הראשון. אם ה- var’s משמשים כ- statics אז אפשר.

 

מה אתם חושבים על זה? בעד או נגד?

 
השארת תגובה

פורסם ע"י ב- נובמבר 21, 2014 ב- AngularJS Tips