مطالب
بارگذاری پویای کامپوننت‌های Angular به همراه امکان Lazy loading پویای ماژول‌ها

در نسخه‌های قبل از Angular CLI 6.0، صرفا امکان Bundle کردن جداگانه‌ی ماژول‌هایی که در قسمت  loadChildren مرتبط با تنظیمات مسیریابی  ذکر شده بودند، وجود داشت. بنابراین در برخی از شرایط اگر نیاز به امکان بارگذاری ماژولی به صورت Lazy load بود، باید از سیستم مسیریابی استفاده می‌شد یا اینکه با یکسری ترفند، CLI و Webpack را مجبور به ساخت فایل chunk جداگانه برای ماژول مورد نظر می‌کردید. از زمان انتشار Angular CLI 6.0 امکان Lazy loading پویا نیز مهیا می‌باشد؛ به این ترتیب بدون وابستگی به سیستم مسیریابی، باز هم می‌توان از مزایای Lazy loading بهره برد. در این مطلب روش استفاده از این قابلیت و همچنین نحوه‌ی بارگذاری پویای یک کامپوننت مرتبط با یک ماژول Lazy load شده را بررسی خواهیم کرد. برای این منظور در ادامه با ایجاد یک TabLayout با استفاده از Angular Material Tabs با یکی از موارد پر استفاده‌ی قابلیت مذکور آشنا خواهیم شد.

پیش نیازها

کار را با طراحی و پیاده سازی TabService شروع می‌کنیم. برای این منظور یک سرویس را در فولدر services موجود در کنار CoreModule ایجاد خواهیم کرد؛ به این جهت ابتدا مدل‌های زیر را خواهیم داشت:

import { Type, ValueProvider } from '@angular/core';

export interface OpenNewTabModel {
  label: string;
  componentType: Type<any>;
  iconName: string;
  modulePath?: string;
  data?: ValueProvider[];
}
واسط تعریف شده‌ی در بالا به عنوان قرارداد مدل ورودی متد open مرتبط با سرویس TabService استفاده می‌شود. در اینجا componentType، نوع کامپوننت را مشخص می‌کند که قرار است داخل برگه‌ی جدید نمایش داده شود. modulePath هم به مسیر ماژولی که باید به صورت پویا بارگذاری شود، اشاره می‌کند. دلیل وجود خصوصیت data را نیز در ادامه خواهیم دید.
import { TabItemComponent } from './tab-item-component';

export interface TabItem {
  label: string;
  iconName: string;
  component: TabItemComponent;
}

OpenNewTabModel برای ارسال داده توسط مصرف کننده از این سرویس در نظر گرفته شده است. ولی واسط TabItem دارای خصوصیاتی می‌باشد که ما برای نمایش یک برگه‌ی جدید نیازمندیم. TabItemComponent نیز دارای خصوصیاتی است که مورد نیاز دایرکتیو« NgComponentOutlet» است. 

import { Injector, NgModuleFactory, Type } from '@angular/core';

export interface TabItemComponent {
  componentType: Type<any>;
  moduleFactory?: NgModuleFactory<any>;
  injector: Injector;
}

همانطور که اشاره شد، برای بارگذاری پویای یک کامپوننت از NgComponentOutlet استفاده خواهیم کرد؛ لذا اگر modulePath ای توسط مصرف کننده از TabService، مهیا شده باشد، لازم است ابتدا ماژول مورد نظر به صورت پویا بارگذاری شود و moduleFactory بدست آمده را به عنوان ورودی دایرکتیو مذکور ارسال کنیم. TabService، پیاده سازی به شکل زیر خواهد داشت:
import { BehaviorSubject, Observable } from 'rxjs';
import {
  Injectable,
  Injector,
  NgModuleFactory,
  NgModuleFactoryLoader
} from '@angular/core';

import { OpenNewTabModel } from '../models/open-new-tab-model';
import { TabItem } from '../models/tab-item';

@Injectable({
  providedIn: 'root'
})
export class TabService {
  private tabItemSubject: BehaviorSubject<TabItem[]> = new BehaviorSubject<
    TabItem[]
  >([]);

  constructor(
    private loader: NgModuleFactoryLoader,
    private injector: Injector
  ) {}

  get tabItems$(): Observable<TabItem[]> {
    return this.tabItemSubject.asObservable();
  }

  open(newTab: OpenNewTabModel) {
    if (newTab.modulePath) {
      this.loader
        .load(newTab.modulePath)
        .then((moduleFactory: NgModuleFactory<any>) => {
          this.openInternal(newTab, moduleFactory);
        });
    } else {
      this.openInternal(newTab);
    }
  }

  private openInternal(newTab: OpenNewTabModel, moduleFactory?: NgModuleFactory<any>) {
    const newTabItem: TabItem = {
      label: newTab.label,
      iconName: newTab.iconName,
      component: {
        componentType: newTab.componentType,
        moduleFactory: moduleFactory,
        injector: newTab.data
          ? Injector.create(newTab.data, this.injector)
          : this.injector
      }
    };

    this.tabItemSubject.getValue().push(newTabItem);
    this.tabItemSubject.next(this.tabItemSubject.getValue());
  }

  close(index: number) {
    this.tabItemSubject.getValue().splice(index, 1);
    this.tabItemSubject.next(this.tabItemSubject.getValue());
  }
}
روش کار به این شکل می‌باشد که یک مخزن، برای لیست برگه‌های درخواستی برای نمایش، تحت عنوان tabItemSubject و از نوع BehaviorSubject در نظر گرفته شده تا مصرف کننده از این سرویس که قصد نمایش برگه‌ها را دارد، از تغییرات لیست موجود آگاه شود. عموما TabsComponent، مشترک پراپرتی فقط خواندنی ‎‎‎tabItems‎$ خواهد شد و بقیه بخش‌ها صرفا دستور گشودن برگه‌ی جدید را با متد open صادر خواهند کرد.
یکی از وابستگی‌های این سرویس، وهله‌ای می‌باشد از کلاس  NgModuleFactoryLoader  که در سیستم مسیریابی نیز از همین کلاس برای بارگذاری ماژول‌ها استفاده می‌شود. البته نیاز است که یکی از پیاده سازی‌های این کلاس انتزاعی را به سیستم تزریق وابستگی‌ها نیز معرفی کنید:
{ provide: NgModuleFactoryLoader, useClass: SystemJsNgModuleLoader }
در بدنه متد open، ابتدا بررسی می‌شود که اگر modulePath مشخص شده‌است، ماژول مورد نظر ابتدا توسط متد load مرتبط با وهله NgModuleFactoryLoader به صورت پویا بارگذاری شود و سپس با استفاده از moduleFactory بدست آمده، متد openInternal فراخوانی خواهد شد.
 در بدنه متد openInternal، تنهای نکته‌ای که ذکر آن اهمیت دارد، مرتبط است به مقداردهی خصوصیت injector شیء ایجاد شده. باتوجه به اینکه تا زمان نگارش مطلب جاری امکان کار با Input‌ها و Output‌های کامپوننت مورد نظر که قرار است با استفاده از NgComponentOutlet بارگذاری شود، وجود ندارد، لذا راه حل فعلی، استفاده از سیستم تزریق وابستگی‌ها می‌باشد. برای این منظور، با استفاده از متد استاتیک create کلاس Injector یک child injector ایجاد شده و ValueProvider‌های مشخص شده توسط خصوصیت data، به صورت خودکار رجیستر خواهند شد. در نهایت آگاه سازی مشترکین خصوصیت ‎‎‎tabItems‎با استفاده از فراخوانی متد next مرتبط با tabItemSubject انجام می‌گیرد.

پیاده سازی TabsComponent
import { Component, OnInit } from '@angular/core';

import { TabService } from './../../../services/tab.service';

@Component({
  selector: 'app-tabs',
  templateUrl: './tabs.component.html',
  styleUrls: ['./tabs.component.scss']
})
export class TabsComponent implements OnInit {
  constructor(public service: TabService) {}

  ngOnInit() {}
}

همانطور که عنوان شد، مشترک اصلی خصوصیت tabItems سرویس TabService، کامپوننت تعریف شده‌ی بالا می‌باشد. قالب مرتبط با آن به شکل زیر است:
<mat-tab-group>
  <mat-tab
    *ngFor="let tabItem of (service.tabItems$ | async); let index = index"
  >
    <ng-template mat-tab-label>
      <mat-icon
        class="icon"
        aria-label="icon for tab"
      >{{tabItem.iconName}}</mat-icon>
      <span class="full">{{ tabItem.label }}</span>
    
      <mat-icon
        class="close"
        (click)="service.close(index)"
        aria-label="close tab button"
        >close</mat-icon
      >
      <!-- </button> -->
    </ng-template>

    <ng-container *ngIf="tabItem.component.moduleFactory">
      <ng-container
        *ngComponentOutlet="
          tabItem.component.componentType;
          ngModuleFactory: tabItem.component.moduleFactory;
          injector: tabItem.component.injector
        "
      >
      </ng-container>
    </ng-container>
    <ng-container *ngIf="!tabItem.component.moduleFactory">
      <ng-container
        *ngComponentOutlet="
          tabItem.component.componentType;
          injector: tabItem.component.injector
        "
      >
      </ng-container>
    </ng-container>
  </mat-tab>
</mat-tab-group>

در تکه کد بالا، ابتدا با استفاده از وهله تزریق شده TabService در کامپوننت مذکور، به شکل زیر، مشترک تغییرات لیست برگه‌ها شده‌ایم و با استفاده از دایرکتیو ‎*ngFor به ازای تک تک tabItem‌های درخواست شده برای گشوده شدن، به شکل زیر کار وهله سازی پویا از کامپوننت مشخص شده انجام می‌شود:

<ng-container *ngComponentOutlet="tabItem.component.componentType; ngModuleFactory: tabItem.component.moduleFactory; injector: tabItem.component.injector">
</ng-container>

خوب، با استفاده از آنچه تا اینجای مطلب بررسی شد، می‌توان یک سیستم راهبری مبتنی بر Tab را نیز برپا کرد که مطلب جدایی را می‌طلبد. برای تکمیل مکانیزم بارگذاری پویای ماژول‌ها، نیاز است تا مسیر ماژول مورد نظر را در فایل angular.json و بخش lazyModules به شکل زیر معرفی کنید:

"build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/MaterialAngularTabLayout",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
              "src/styles.scss"
            ],
            "lazyModules": [
              "src/app/lazy/lazy.module"
            ],
            "scripts": []
          },

به عنوان مثال قصد داریم ماژول LazyModule را به صورت پویا بارگذاری کرده و LazyComponent موجود در این ماژول را به صورت پویا در برگه‌ی جدیدی نمایش دهیم. برای این منظور کدهای فایل AppComponent.ts را به شکل زیر تغییر خواهیم داد:

import { Component } from '@angular/core';
import { IdModel } from './core/models/id-model';
import { LazyComponent } from './lazy/lazy.component';
import { OpenNewTabModel } from './core/models/open-new-tab-model';
import { TabService } from './core/services/tab.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'MaterialAngularTabLayout';
  constructor(private tabService: TabService) {}
  loadLazyComponent() {
    this.tabService.open(<OpenNewTabModel>{
      label: 'Loaded Lazy Component',
      iconName: 'thumb_up',
      componentType: LazyComponent,
      modulePath: 'src/app/lazy/lazy.module#LazyModule',
      data: [{ provide: IdModel, useValue: <IdModel>{ id: 1 } }]
    });
  }
}

در تکه کد بالا با تزریق TabService به سازنده‌ی آن، قصد گشودن برگه‌ی جدیدی را توسط متد open آن، داریم. در بدنه‌ی متد loadLazyComponent یک شیء با قرارداد OpenNewTabModel ایجاد شده و به عنوان آرگومان به متد open ارسال شده است. توجه داشته باشید که modulePath اینجا نیز به مانند خصوصیت loadChildren مرتبط با اشیاء مسیریابی، باید مقدار دهی شود. همچنین قصد داشتیم اطلاعاتی را نیز به کامپوننت مورد نظر ارسال کنیم؛ همانند مکانیزم مسیریابی که با پارامترها این چنین کارهایی صورت می‌پذیرد. در اینجا از یک کلاس به شکل زیر استفاده شده‌است:

export class IdModel {
  constructor(public id: number) {}
}

در این صورت پیاده سازی LazyComponent نیز به شکل زیر خواهد بود:

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

import { IdModel } from './../core/models/id-model';

@Component({
  selector: 'app-lazy',
  templateUrl: './lazy.component.html',
  styleUrls: ['./lazy.component.scss']
})
export class LazyComponent implements OnInit {
  constructor(private model: IdModel) {}

  ngOnInit() {
    console.log(this.model);
  }
}

البته فراموش نکنید کامپوننتی را که نیاز است به صورت پویا بارگذاری شود، در قسمت entryComponents مرتبط با NgModule متناظر به شکل نیز معرفی کنید:

import { CommonModule } from '@angular/common';
import { LazyComponent } from './lazy.component';
import { NgModule } from '@angular/core';

@NgModule({
  imports: [CommonModule],
  declarations: [LazyComponent],
  entryComponents: [LazyComponent]
})
export class LazyModule {}

با خروجی زیر:

و chunk تولید شده برای ماژول مورد نظر:


در صورتیکه در حالت production پروژه را بیلد کنید، هش مرتبط برای chunk تولید شده نیز ایجاد خواهد شد.


کدهای کامل این قسمت را می‌توانید از اینجا دریافت کنید.
مطالب
کامپوننت‌ها در AngularJS 1.5 - قسمت دوم - مسیریابی
در این قسمت به معرفی سیستم مسیریاب در Angular 1.5 خواهیم پرداخت. قبل از معرفی این سیستم ابتدا سیستم مسیریاب اصلی در Angular را بررسی خواهیم کرد.

مروری بر مسیریابی در AngularJS
برای استفاده از مسیریاب اصلی Angular کافی است از دایرکتیو ویژه‌ایی با نام ng-view به همراه یکسری تنظیمات پیکربندی استفاده کنیم. به عنوان مثال اگر آدرس صفحه با home/ مطابقت داشته باشد، تمپلیت home.html توسط دایرکتیو ng-view بارگذاری خواهد شد. برای فعال‌سازی این سیستم ابتدا باید پکیج angular-route را به پروژه مثال قسمت قبل اضافه کنید:
bower install angular-route --save
در ادامه لازم است وابستگی فوق را به صفحه‌ی index.html اضافه نمائید:
<script src="bower_components/angular-route/angular-route.min.js" type="text/javascript"></script>
اکنون درون صفحه به جای نمایش مستقیم کامپوننت، از دایرکتیو ng-view استفاده خواهیم کرد:
<div class="col-md-9">
                <ng-view></ng-view>
</div>

 همچنین مقدار فیلد url کامپوننت dntWidget را به صورت زیر تغییر دهید:
      model.panel = {
          title: "Panel Title",
          items: [
              {
                  title: "Home", url: "#/home"
              },
              {
                  title: "Articles", url: "#/articles"
              },
              {
                  title: "Authors", url: "#/authors"
              }
          ]
      };

در ادامه باید سیستم مسیریاب را به عنوان یک وابستگی به اپلیکیشن معرفی کنیم:
var module = angular.module("dntModule", ["ngRoute"]);
اکنون می‌توانیم پیکربندی موردنظر جهت هدایت آدرس‌ها به تمپلیت‌های مربوطه را بنویسیم:
module.config(function ($routeProvider) {
        $routeProvider
            .when("/home", { template: "<app-home></app-home>" })
            .when("/articles", { template: "<app-articles></app-articles>" })
            .when("/authors", { template: "<app-authors></app-authors>" })
            .otherwise({ redirectTo: "/home" });
    });

همانطور که مشاهده می‌کنید به route provider اعلام کرده‌ایم که در صورت مطابقت داشتن آدرس URL با هر کدام از حالت‌های فوق، تمپلیت متناسب با آن را نمایش بدهد. در نهایت توسط otherwise اگر آدرس، با هیچکدام از حالت‌های تعریف شده مطابقت نداشت، کاربر به آدرس home/ هدایت خواهد شد. 
نکته‌ایی که در کد فوق وجود دارد این است که سیستم مسیریاب اصلی Angular تا اینجا هیچ اطلاعی از وجود کامپوننت‌ها ندارد، اما می‌داند یک تمپلیت چیست. بنابراین از تمپلیت، جهت نمایش یک کامپوننت استفاده خواهد کرد.
برای ایجاد کامپوننت‌های فوق نیز می‌توانید آن را به صورت زیر ایجاد کنید:
module.component("appHome", {
        template: `
        <hr><div>
            <div>Panel heading = HomePage</div>
            <div>
                HomePage
            </div>
        </div>`
    });
    module.component("appArticles", {
        template: `
        <hr><div>
            <div>Panel heading = Articles</div>
            <div>
                Articles
            </div>
        </div>`
    });
    module.component("appAuthors", {
        template: `
        <hr><div>
            <div>Panel heading = Authors</div>
            <div>
                Authors
            </div>
        </div>`
    });

اکنون اگر برنامه را اجرا کنید، خواهید دید که به صورت پیش‌فرض به آدرس home/# هدایت خواهیم شد. زیرا آدرسی برای root، درون route configuration تعریف نکرده‌ایم:

اکنون توسط لینک‌های تعریف شده می‌توانیم به راحتی درون تمپلیت‌ها، پیمایش کنیم. همانطور که عنوان شد تا اینجا مسیریاب پیش‌فرض Angular هیچ اطلاعی از کامپوننت‌ها ندارد؛ بلکه آنها را با کمک template، به صورت غیر مستقیم، درون صفحه نمایش داده‌ایم.


معرفی Component Router

مزیت این روتر این است که به صورت اختصاصی برای کار با کامپوننت‌ها طراحی شده است. بنابراین دیگر نیازی به استفاده از template درون route configuration نیست. برای استفاده از این روتر ابتدا باید پکیج آن را نصب کنیم:

bower install angular-component-router --save

سپس وابستگی فوق را با روتر پیش‌فرضی که در مثال قبل بررسی کردیم، جایگزین خواهیم کرد:

<script src="bower_components/angular-component-router/angular_1_router.js"></script>

همچنین درون فایل module.js به جای وابستگی ngRoute از ngComponentrouter استفاده خواهیم کرد:

var module = angular.module("dntModule", ["ngComponentRouter"]);

در ادامه به جای تمامی route configurations قبلی، اینبار یک کامپوننت جدید را به صورت زیر ایجاد خواهیم کرد:

module.component("appHome", {
        template: `
        <hr>
        <div>
            <div>Panel heading = HomePage</div>
            <div>
                HomePage
            </div>
        </div>`
    });

همانطور که مشاهده می‌کنید برای پاسخ‌گویی به تغییرات URL، مقدار routeConfig$ را مقداردهی کرده‌ایم. در اینجا به جای بارگذاری تمپلیت، خود کامپوننت، در هر یک از ruleهای فوق بارگذاری خواهد شد. برای حالت otherwise نیز از سینتکس **/ استفاده کرده‌ایم.

تمپلیت کامپوننت فوق نیز به صورت زیر است:

<div class="container">
    <div class="row">
        <div class="col-md-3">
            <hr>
            <dnt-widget></dnt-widget>
        </div>
        <div class="col-md-9">
            <ng-outlet></ng-outlet>
        </div>
    </div>
</div>

لازم به ذکر است دیگر نباید از دایرکتیو  ng-view استفاده کنیم؛ زیرا این دایرکتیو برای استفاده از روتر اصلی طراحی شده است. به جای آن از دایرکتیو ng-outlet استفاده شده است. این کامپوننت به عنوان یک کامپوننت top level عمل خواهد کرد. بنابراین درون صفحه‌ی index.html از کامپوننت فوق استفاده خواهیم کرد:

<html ng-app="dntModule">
<head>
    <meta charset="UTF-8">
    <title>Using Angular 1.5 Component Router</title>
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
    <link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css">
</head>
<body>
    
    <dnt-app></dnt-app>

    <script src="bower_components/angular/angular.js" type="text/javascript"></script>
    <script src="bower_components/angular-component-router/angular_1_router.js"></script>
    <script src="scripts/module.js" type="text/javascript"></script>
    <script src="scripts/dnt-app.component.js"></script>
    <script src="scripts/dnt-widget.component.js"></script>
</body>
</html>

در نهایت باید جهت فعال‌سازی سیستم مسیریابی جدید، سرویس زیر را همراه با نام کامپوننت فوق ریجستر کنیم:

module.value("$routerRootComponent", "dntApp");

اکنون اگر برنامه را اجرا کنید خواهید دید که همانند قبل، کار خواهد کرد. اما اینبار از روتر جدید و سازگار با کامپوننت‌ها استفاده می‌کند.

کدهای این قسمت را نیز از اینجا می‌توانید دریافت کنید. 

مطالب
AngularJS #3
در این مقاله مفاهیم انقیاد داده (Data Binding)، تزریق وابستگی (Dependency Injection)،هدایت گر‌ها (Directives) و سرویس‌ها را بررسی خواهیم کرد و از مقاله‌ی آینده، به بررسی ویژگی‌ها و امکانات AngularJS در قالب مثال خواهیم پرداخت.
 
انقیاد داده (Data Binding)
سناریو هایی وجود دارد که در آن‌ها باید اطلاعات قسمتی از صفحه به صورت نامتقارن (Asynchronous) با داده‌های دریافتی جدید به روز رسانی شود. روش معمول برای انجام چنین کاری؛ دریافت داده‌ها از سرور است که عموما به فرم HTML میباشند و جایگزینی آن با بخشی از صفحه که قرار است به روز رسانی شود، اما حالتی را در نظر بگیرید که با داده هایی از جنس JSON طرف هستید و اطلاعات صفحه را با این داده‌ها باید به روز رسانی کنید. معمولا برای حل چنین مشکلی مجبور به نوشتن مقدار زیادی کد هستید تا بتوانید به خوبی اطلاعات View را به روز رسانی کنید. حتما با خودتان فکر کرده اید که قطعا راهی وجود دارد تا بدون نوشتن کدی، قسمتی از View را به Model متناظر خود نگاشت کرده و این دو به صورت بلادرنگ از تغییرات یکدیگر آگاه شوند. این عمل عموما به مفهوم انقیاد داده شناخته می‌شود و Angular هم به خوبی از انقیاد داده دوطرفه پشتیبانی می‌کند.
برای مشاهده این ویژگی در Angular، مثال مقاله‌ی قبل را به کد‌های زیر تغییر دهید تا پیغام به صورت پویا توسط کاربر وارد شود:
<!DOCTYPE html>
<html ng-app>
<head>
    <title>Sample2</title>
</head>
<body>
    <div>
        <input type="text" ng-model="greeting.text" />
        <p>{{greeting.text}}, World!</p>
    </div>
    <script src="../Scripts/angular.js"></script>
</body>
</html>
بدون نیاز به حتی یک خط کد نویسی! با مشخص کردن input به عنوان Model از طریق ng-model، خاصیت greeting.text که در داخل {{ }} مشخص شده را به متن داخل textbox  مقید (bind) کردیم.  نتیجه می‌گیریم که جفت آکلود {{ }} برای اعمال Data Binding استفاده می‌شود.
حال یک دکمه نیز بر روی فرم قرار می‌دهیم که با کلیک کردن بر روی آن، متن داخل textbox را نمایش دهد.
<!DOCTYPE html>
<html ng-app>
<head>
    <title>Sample2</title>
</head>
<body>
    <div ng-controller="GreetingController">
        <input type="text" ng-model="greeting.text" />
        <p>{{greeting.text}}, World!</p>
        <button ng-click="showData()">Show</button>
    </div>
    <script src="../Scripts/angular.js"></script>
    <script>
        var GreetingController = function ($scope, $window) {
            $scope.greeting = {
                text: "Hello"
            };

            $scope.showData = function () {
                $window.alert($scope.greeting.text);
            };
        };
    </script>
</body>
</html>
به کمک ng-click، تابع showData به هنگام کلیک شدن، فراخوانی می‌شود. window$ نیز به عنوان پارامتر کلاس GreetingController مشخص شده است. window$ نیز یکی از سرویس‌های پیش فرض تعریف شده توسط Angular است و ما در اینجا در سازنده‌ی کلاس آن را به عنوان وابستگی درخواست کرده ایم تا توسط سیستم تزریق وابستگی توکار، نمونه‌ی مناسب آن در اختیار ما بگذارد. window$ نیز تقریبا معادل شی window است و یکی از دلایل استفاده از آن ساده‌تر شدن نوشتن آزمون‌های واحداست.
حال متنی را داخل textbox نوشته  و دکمه‌ی show را فشار دهید. متن نوشته شده را به صورت یک popup  مشاهده خواهید کرد.
همچنین شی scope$ نیز نمونه‌ی مناسب آن توسط سیستم تزریق وابستگی Angular، در اختیار Controller قرار می‌گیرد و نمونه‌ی در اختیار قرارگرفته، برای ارتباط با View Model و سیستم انقیاد داده استفاده می‌شود.
معمولا انقیاد داده در الگوی طراحی (ModelView-ViewModel(MVVM مطرح است و به این دلیل که این الگوی طراحی به خوبی با الگوی طراحی MVC سازگار است، این امکان در Angular گنجانده شده است. 
   
تزریق وابستگی (Dependency Injection)
تا به این جای کار قطعن  بار‌ها و بار‌ها اسم آن را خوانده اید. در مثال فوق، پارامتری با نام scope$ را برای سازنده‌ی کنترلر خود در نظر گرفتیم و ما بدون انجام هیچ کاری نمونه‌ی مناسب آن را که برای انجام اعمال انقیاد داده با viewmodel استفاده می‌شود را دریافت کردیم. به عنوان مثال، window$ را نیز در سازنده‌ی کلاس کنترلر خود به عنوان یک وابستگی تعریف کردیم و تزریق نمونه‌ی مناسب آن توسط سیستم تزریق وابستگی توکار Angular صورت می‌گرفت.
اگر با IOC Container‌ها در زبانی مثل #C کار کرده باشید، قطعا با IOC Container فراهم شده توسط Angular هم مشکلی نخواهید داشت.
اما یک مشکل! در زبانی مثل #C که همه‌ی متغیر‌های دارای نوع هستند، IOC Container با استفاده از Reflection، نوع پارامترهای درخواستی توسط سازنده‌ی کلاس را بررسی کرده و با توجه به اطلاعاتی که ما از قبل در دسترس آن قرار داده بودیم، نمونه‌ی مناسب آن را در اختیار در خواست کننده می‌گذارد.
اما در زبان جاوا اسکریپت که متغیر‌ها دارای نوع نیستند، این کار به چه شکل انجام می‌گیرد؟
Angular برای این کار از نام پارامتر‌ها استفاده می‌کند. برای مثال Angular از نام پارامتر scope$ می‌فهمد که باید چه نمونه ای را به کلاس تزریق کند. پس نام پارامتر‌ها در سیستم تزریق وابستگی Angular نقش مهمی را ایفا می‌کنند.
اما در زبان جاوا اسکریپت، به طور پیش فرض امکانی برای به دست آوردن نام پارامتر‌های یک تابع وجود ندارد؛ پس Angular چگونه نام پارامتر‌ها را به دست می‌آورد؟ جواب در سورس کد Angular و در تابعی به نام annotate نهفته است که اساس کار این تابع استفاده از چهار عبارت با قاعده (Regular Expression) زیر است.
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
تابع annotate تابعی را به عنوان پارامتر دریافت می‌کند و سپس با فراخواندن متد toString آن، کدهای آن تابع را به شکل یک رشته در می‌آورد. حال کدهای تابع را که اکنون به شکل یک رشته در دسترس است را با استفاده از عبارات با قاعده‌ی فوق پردازش می‌کند تا نام پارامتر‌ها را به دست آورد. در ابتدا کامنت‌های موجود در تابع را حذف می‌کند، سپس نام پارامتر‌ها را استخراج می‌کند و با استفاده از "," آن‌ها را جدا می‌کند و در نهایت نام پارامتر‌ها را در یک آرایه باز می‌گرداند.
استفاده از تزریق وابستگی، امکان نوشتن کدهایی با قابلیت استفاده مجدد و نوشتن ساده‌تر آزمون‌های واحد را فراهم می‌کند. به خصوص کدهایی که با سرور ارتباط برقرار می‌کنند را می‌توان به یک سرویس انتقال داد و از طریق تزریق وابستگی، از آن در کنترلر استفاده کرد. سپس در آزمون‌های واحد می‌توان قسمت ارتباط با سرور را با یک نمونه فرضی جایگزین کرد تا برای تست، احتیاجی به راه اندازی یک وب سرور واقعی و یا مرورگر نباشد.
    
Directives
یکی از مزیت‌های Angular این است که قالب‌ها را می‌توان با HTML نوشت و این را باید مدیون موتور قدرتمند تبدیل گر DOM بدانیم  که در آن گنجانده شده است و به شما این امکان را می‌دهد تا گرامر HTML را گسترش دهید.
تا به این جای کار با attribute‌های زیادی در قالب HTML روبرو شدید که متعلق به HTML نیست. به طور مثال: جفت آکولاد‌ها که برای انقیاد داده به کار برده می‌شود، ng-app که برای مشخص کردن بخشی که باید توسط Angular کامپایل شود، ng-controller که برای مشخص کردن این که کدام بخش از View متعلق به کدام Controller است و ... تمامی Directive‌های پیش فرض Angular هستند.
با استفاده از Directive‌ها می‌توانید عناصر و خاصیت‌ها و حتی رویداد‌های سفارشی برای HTML بنویسید؛ اما واقعا چه احتیاجی به تعریف عنصر سفارشی و توسعه گرامر HTML وجود دارد؟
HTML یک زبان طراحی است که در ابتدا برای تولید اسناد ایستا به وجود آمد و هیچ وقت هدفش تولید وب سایت‌های امروزی که کاملا پویا هستند نبود. این امر تا جایی پیش رفته است که HTML را از یک زبان طراحی تبدیل به یک زبان برنامه نویسی کرده است و احتیاج به چنین زبانی کاملا مشهود است. به همین دلیل جامعه‌ی وب مفهومی را به نام Web Components  مطرح کرده است. Web Components به شما امکان تعریف عناصر HTML سفارشی را می‌دهد. برای مثال شما یک تگ سفارشی به نام datepicker می‌نویسید که دارای رفتار و ویژگی‌های خاص خود است و به راحتی عناصر HTML رابا استفاده از آن توسعه می‌دهید. مطمئنا آینده‌ی وب این گونه است، اما هنوز خیلی از مرورگرها از این ویژگی پشتیبانی نمی‌کنند.
یکی دیگر از معادل‌های  Web Component‌های امروز را می‌توان ویجت‌های jQuery UI دانست. اگر بخواهم تعریفی از ویجت ارائه دهم به این گونه است که یک ویجت؛ کدهای HTML، CSS و javascript مرتبط به هم را کپسوله کرده است. مهم‌ترین مزیت ویجت ها، قابلیت استفاده‌ی مجدد آن‌هاست، به این دلیل که تمام منطق مورد نیاز را در خود کپسوله کرده است؛ برای مثال ویجت datepicker که به راحتی در برنامه‌های مختلف بدون احتیاج به نوشتن کدی قابل استفاده است.
خب، متاسفانه Web Component‌ها هنوز در دنیای وب امروزی رایج نشده اند و ویجت‌ها هم آنچنان  قدرت Web Component‌ها را ندارند. خب Angular با استفاده از امکان تعریف Directive‌های سفارشی به صورت cross-browser امکان تعریف عناصر سفارشیه همانند web Component‌ها را به شما می‌دهد. حتی به عقیده‌ی عده ای Directive‌ها بسیار قدرتمند‌تر از Web Components عمل می‌کنند و راحتی کار با آن‌ها بیشتر است.
با استفاده از Directive‌ها می‌توانید عنصر HTML سفارشی مثل </ datepicker>،  خاصیت سفارشی مثل ng-controller، رویداد سفارشی مثل ng-click را  تعریف کنید و یا حتی حالت و اتفاقات رخ داده در برنامه را زیر نظر بگیرید.
و این یکی از دلایلی است که می‌گویند Angular دارای ویژگی forward-thinking است.
البته Directive‌ها یکی از قدرتمند‌ترین امکانات فریم ورک AngularJS است و در آینده به صورت مفصل بر روی آن بحث خواهد شد.
    
سرویس‌ها در AngularJS
 حتما این جمله را در هنگام نوشتن برنامه‌ها با الگوی طراحی MVC بار‌ها و بار‌ها شنیده اید که در Controller‌ها نباید منطق تجاری و پیچیده ای را پیاده سازی کرد و باید به قسمت‌های دیگری به نام سرویس‌ها منتقل شوند و سپس در سازنده‌ی کلاس کنترلر به عنوان پارامتر تعریف شوند تا توسط Angular نمونه‌ی مناسب آن به کنترلر تزریق شود. Controller‌ها نباید پیاده کننده‌ی هیچ منطق تجاری و یا اصطلاحا business برنامه باشد و باید از لایه‌ی سرویس استفاده کنند و تنها وظیفه‌ی کنترلر باید مشخص کردن انقیاد داده و حالت برنامه باشد.
دلیل استفاده از سرویس‌ها در کنترلر ها، نوشتن ساده‌تر آزمون‌های واحد و استفاده‌ی مجدد از سرویس‌ها در قسمت‌های مختلف پروژه و یا حتی پروژه‌های دیگر است.
معمولا اعمال مرتبط در ارتباط با سرور را در سرویس‌ها پیاده سازی می‌کنند تا بتوان در موقع نوشتن آزمون‌های واحد یک نمونه‌ی فرضی را خودمان ساخته و آن را به عنوان وابستگی به کنترلری که در حال تست آن هستیم تزریق کنیم، در غیر این صورت احتیاج به راه اندازی یک وب سرور واقعی برای نوشتن آزمون‌های واحد و در نتیجه کند شدن انجام آزمون را در بر دارد. قابلیت استفاده‌ی مجدد سرویس هم به این معناست که منطق پیاده سازی شده در آن نباید ربطی به رابط کاربری و ... داشته باشد. برای مثال یک سرویس به نام userService باید دارای متد هایی مثل دریافت لیست کاربران، افزودن کاربر و ... باشد و بدیهی است که از این سرویس‌ها می‌شود در قسمت‌های مختلف برنامه استفاده کرد. همچنین سرویس‌ها در Angular به صورت Singleton در اختیار کنترلر‌ها قرار می‌گیرند  و این بدین معناست که یک نمونه از هر سرویس ایجاد شده و به بخش‌های مختلف برنامه تزریق می‌شود. 
    
مفاهیم پایه ای AngularJs به پایان رسید. در مقاله بعدی یک مثال تقریبا کامل را نوشته و با اجزای مختلف Angular بیشتر آشنا می‌شویم.
   
با تشکر از مهدی محزونی برای بازبینی مطلب
مطالب
پیاده سازی یک سیستم دسترسی Role Based در Web API و AngularJs - بخش سوم (پایانی)
در بخش پیشین  به بررسی جزئی‌تر ایجاد پایگاه داده و همچنین توسعه Custom Filter Attribute پرداختیم که وظیفه تایید صلاحیت کاربر جاری و بررسی دسترسی وی به API Method مورد نظر را بررسی می‌کرد. در این مقاله به این بحث می‌پردازیم که در Filter Attribute توسعه داده شده، قصد داریم یک سرویس Access Control ایجاد نماییم.
این سرویس وظیفه تمامی اعمال مربوط به نقش‌ها و دسترسی‌های کاربر را بر عهده خواهد داشت. این سرویس به صورت زیر تعریف می‌گردد:
public class AccessControlService
{
        private DbContext db;

        public AccessControlService()
        {
            db = new DbContext();
        }

        public IEnumerable<Permission> GetUserPermissions(string userId)
        {
            var userRoles = this.GetUserRoles(userId);
            var userPermissions = new List<Permission>();
            foreach (var userRole in userRoles)
            {
                foreach (var permission in userRole.Permissions)
                {
                    // prevent duplicates
                    if (!userPermissions.Contains(permission))
                        userPermissions.Add(permission);
                }
            }
            return userPermissions;
        }
        public IEnumerable<Role> GetUserRoles(string userId)
        {
            return db.Users.FirstOrDefault(x => x.UserId == userId).Roles.ToList();
        }

        public bool HasPermission(string userId, string area, string control)
        {
            var found = false;
            var userPermissions = this.GetUserPermissions(userId);
            var permission = userPermissions.FirstOrDefault(x => x.Area == area && x.Control == control);
            if (permission != null)
                found = true;
            return found;
        }
{
همانطور که ملاحظه می‌کنید، ما سه متد GetUserPermissions، GetUserRoles و HasPermission را توسعه داده‌ایم. حال اینکه بر حسب نیاز، می‌توانید متدهای بیشتری را نیز به این سرویس اضافه نمایید. متد اول، وظیفه‌ی واکشی تمامی permissionهای کاربر را عهده دار می‌باشد. متد GetUserRoles نیز تمامی نقش‌های کاربر را در سیستم، بازمی‌گرداند و در نهایت متد سوم، همان متدی است که ما در Filterattribute از آن استفاده کرد‌ه‌ایم. این متد با دریافت پارامترها و بازگردانی یک مقدار درست یا نادرست، تعیین می‌کند که کاربر جاری به آن محدوده دسترسی دارد یا خیر.
تمامی حداقل‌هایی که برای نگارش سمت سرور نیاز بود، به پایان رسید. حال به بررسی این موضوع خواهیم پرداخت که چگونه این سطوح دسترسی را با سمت کاربر همگام نماییم. به طوری که به عنوان مثال اگر کاربری حق دسترسی به ویرایش مطالب یک سایت را ندارد، دکمه مورد نظر که او را به آن صفحه هدایت می‌کند نیز نباید به وی نشان داده شود. سناریویی که ما برای این کار در نظر گرفته‌ایم، به این گونه می‌باشد که در هنگام ورود کاربر، لیستی از تمامی دسترسی‌های او به صورت JSON به سمت کلاینت ارسال می‌گردد. حال وظیفه مدیریت نمایش یا عدم نمایش المان‌های صفحه، بر عهده زبان سمت کلاینت، یعنی AngularJs خواهد بود. بنابراین در ابتدایی‌ترین حالت، ما نیاز به یک کنترلر و متد Web API داریم تا لیست دسترسی‌های کاربر را بازگرداند. این کنترلر در حال حاضر شامل یک متد است. اما بر حسب نیاز، می‌توانید متدهای بیشتری را به کنترلر اضافه نمایید.
    [RoutePrefix("َAuth/permissions")]
    public class PermissionsController : ApiController
    {
        private AccessControlService _AccessControlService = null;
        public PermissionsController()
        {
            _AccessControlService = new AccessControlService();
        }

        [Route("GetUserPermissions")]
        public async Task<IHttpActionResult> GetUserPermissions()
        {
            if (!User.Identity.IsAuthenticated)
            {
                return Unauthorized();
            }
            return Ok(_AccessControlService.GetPermissions(User.Identity.GetCurrentUserId()));
        }        
    }
در متد فوق ما از متد سرویس Access Control که لیست تمامی permissionهای کاربر را باز می‌گرداند، کمک گرفتیم. متد GetUserPermissions پس از ورود کاربر توسط کلاینت فراخوانی می‌گردد و لیست تمامی دسترسی‌ها در سمت کلاینت، در rootScope انگیولار ذخیره سازی می‌گردد. حال نوبت به آن رسیده که به بررسی عملیات سمت کلاینت بپردازیم.

توسعه سرویس‌ها و فرآیندهای سمت وب کلاینت AngularJS

در ابتدا در سمت کلاینت نیاز به سرویسی داریم که دسترسی‌های سمت سرور را دریافت نماید. از این رو ما نام این سرویس را permissionService می‌نامیم.
'use strict';
angular.module('App').factory('permissionService', ['$http', '$q', function ($http, $q) {

    var _getUserPermissions = function () {
        return $http.get(serviceBaseUrl + '/api/permissions/GetUserPermissions/');
    }

    var _isAuthorize = function (area, control) {
        return _.some($scope.permissions, { 'area': area, 'control': control });
    }

    return {
        getUserPermissions: _getUserPermissions,
        isAuthorize: _isAuthorize
    };
}]);
اگر بخواهیم مختصری درباره‌ی این سرویس صحبت کنیم، متد اول که یک دستور GET ساده است و لیست دسترسی‌ها را از PermissionController دریافت می‌کند. متد بعدی که در آینده بیشتر با آن آشنا می‌شویم، عملیات تایید صلاحیت کاربر را به ناحیه مورد نظر، انجام می‌دهد (همان مثال دسترسی به دکمه ویرایش مطلب در یک صفحه). در این متد برای جستجوی لیست permissions از کتابخانه‌ای با نام Lodash کمک گرفته‌ایم. این کتابخانه کاری شبیه به دستورات Linq را در کالکشن‌ها و آرایه‌های جاوااسکریپتی، انجام می‌دهد. متد some یک مقدار درست یا نادرست را بازمی‌گرداند. بازگردانی مقدار درست، به این معنی است که کاربر به ناحیه‌ی مورد نظر اجازه‌ی دسترسی را خواهد داشت.
حال باید متد‌های این سرویس را در کنترلر لاگین فراخوانی نماییم. در این مرحله ما از rootScope dependency استفاده می‌کنیم. برای نحوه‌ی عملکرد rootScope میتوانید به مقاله‌ای در این زمینه در وب سایت toddomotto مراجعه کنید. در این مقاله ویژگی‌ها و اختلاف‌های scope و rootScope به تفصیل بیان شده است. مقاله‌ای دیگر در همین زمینه نوشته شده است که در انتهای مقاله به بررسی چند نکته در مورد کدهای مشترک پرداخته شده‌است. تکه کد زیر، متد login را نمایش می‌دهد که در loginController قرار گرفته است و ما در آن از نوشتن کل بلاک loginController چشم پوشی کرده‌ایم. متد savePermissions تنها یک کار را انجام می‌دهد و آن هم این است که در ابتدا، به سرویس permissionService متصل شده و تمامی دسترسی‌ها را واکشی می‌نماید و پس از آن، آنها را درون rootScope قرار می‌دهد تا در تمامی کنترلرها قابل دسترسی باشد.
    $scope.login = function () {
        authService.login($scope.loginData).then(function (response) {
            savePermissions();
            $location.path('/userPanel');
        },
         function (err) {
             $scope.message = err.error_description;
         });
    };

    var savePermissions = function () {
        permissionService.getUserPermissions().then(function (response) {
            $rootScope.permissions = response.data;
        },
        function (err) {
        });
    }
}

   حال تمامی اطلاعات دسترسی، در سمت کلاینت نیز قابل دسترسی می‌باشد. تنها کاری که نیاز است، broadCast کردن متد isAuthorize است که آن هم باید در rootScope قرار بگیرد. ما برای این انتساب یک راهکار را ارائه کرده‌ایم. معماری سیستم کلاینت به این صورت است که تمامی کنترلرها درون یک parentController قرار گرفته‌اند. از این رو می‌توان در parentController این انتساب (ایجاد دسترسی عمومی برایisAuthorize) صورت گیرد. برای این کار در parentController تغییرات زیر صورت می‌گیرد:
App.controller('parentController', ['$rootScope', '$scope', 'authService', 'permissionService', function ($rootScope, $scope, authService, permissionService) {
        $scope.authentication = authService.authentication;

        // isAuthorize Method
        $scope.isAuthorize = permissionService.isAuthorize();

        // rest of codes
}]);
در کد فوق ما isAuthorize را درون scope قرار داده‌ایم. دلیل آن هم این است که هر چه که در scope قرار بگیرد، تمامی کنترلر‌های child نیز به آن دسترسی خواهند داشت. البته ممکن است که این بهترین نوع پیاده سازی برای به اشتراک گذاری یک منبع نباشد.
 در گام بعدی کافیست المان‌های صفحه را بر اساس همین دسترسی‌ها فعال یا غیر فعال کنیم. برای این کار از دستور ng-if میتوان استفاده کرد. برای این کار به مثال زیر توجه کنید:
<div ng-controller="childController">
    <div ng-if="isAuthorize('articles', 'edit')" >
    <!-- the block that we want to not see unauthorize person -->
    </div>
</div>
همانطور که مشاهده می‌کنید، تمامی المان‌ها را می‌توان با دستور ساده ng-if، از دید کاربران بدون صلاحیت، پنهان نمود. البته توجه داشته باشید که شما نمی‌توانید تنها به پنهان کردن این اطلاعات اکتفا کنید. بلکه باید تمامی متدهای کنترلرهای سمت سرور را هم با همین روش (فیلتر کردن با Filter Attribute) بررسی نمایید. به ازای هر درخواست کاربر باید بررسی شود که او به منبع مورد نظر دسترسی دارد یا خیر.
تنها نکته‌ای که باقی می‌ماند این است که طول عمر scope و rootScope چقدر است؟! برای پاسخ به این سوال باید بگوییم هر بار که صفحه refresh می‌شود، تمامی مقادیر scope و rootScope خالی می‌شوند. برای این کار هم یک راهکار خیلی ساده (و شاید کمی ناشیانه) در نظر گرفته شده‌است. میتوان بلاک مربوط به پر کردن rootScope.permissions را که در loginController نوشته شده بود، به درون parentController انتقال داد و آن را با استفاده از emit اجرا کرد و در حالت عادی، در هنگام refresh شدن صفحه نیز چون parentController در اولین لحظه اجرا می‌شود، میتوان تمامی مقادیر rootScope.permissions را دوباره از سمت سرور دریافت کرد.
مطالب
آشنایی با angular.extend در AngularJs
در این مقاله قصد دارم تکنیکی ساده ولی در عین حال موثر را معرفی کنم که در نوشتن کنترلر‌ها بسیار کارآمد است. همانطور که می‌دانید به طور معمول برای تعریف متد‌ها و پراپرتی‌های درون کنترلر و انتقال آن به View، از دو روش انتساب به scope$ و controller as استفاده می‌شود.
در AngularJs متدی به صورت built-in وجود دارد که قادر است یک شیء را درون شیء دیگر کپی کند. این متد در بسیاری از مواقع بسیار کارآمد است که به بررسی آن خواهیم پرداخت. جزئیات بیشتر و دقیق‌تر در مورد angular.extend را می‌توانید در اسناد سایت رسمی AngularJs  مشاهده کنید.
به تکه کد زیر توجه کنید. این کد نمونه‌ای از استفاده‌ی از scope$ است. مشاهده می‌کنید که متغییر‌های thingOne و thingTwo چگونه درون scope$ قرار میگیرد.
app.controller(‘ThingController’, [ ‘$scope’, function($scope) {
   $scope.thingOne = ‘one’;
   $scope.thingTwo = ‘two’;
   $scope.getThings = function() { 
       return $scope.thingOne + ‘ ‘ + $scope.thingTwo; 
   };
}]);
حال ببینیم که متد angular.extend کد فوق را چگونه ساده سازی می‌کند. همانطور که مشاهده می‌کنید توسط extend، تمامی متدها و پراپرتی‌ها به صورت یکجا به scope انتساب داده شده است.
app.controller(‘ThingController’, [ ‘$scope’, function($scope) {
    angular.extend($scope, {
        thingOne: ‘one’,
        thingTwo: ‘two’,
        getThings: function() { 
            return $scope.thingOne + ‘ ‘ + $scope.thingTwo; 
        }
    });
}]);
استفاده از angular.extend باعث می‌شود که تمامی assignmentها به صورت‌‌تر و تمیز درون scope یا this قرار بگیرند. حال اینکه نیازی نیست تا شما تمامی این assingmentها را به صورت یکجا و توسط یک بار صدا زدن extend انجام دهید. شما می‌توانید در نقاط مختلف کنترلر این متد را با مقادیر انتسابی متفاوتی فراخوانی کنید. تکه کد زیر نمونه‌ی تغییر یافته به صورت دو بار فراخوانی extend است:
app.controller(‘ThingController’, [ ‘$scope’, function($scope) {
    // models
    angular.extend($scope, {
        thingOne: ‘one’,
        thingTwo: ‘two’
    });

    // methods
    angular.extend($scope, {
      // in HTML template, something like {{ getThings() }}
       getThings: function() { 
            return $scope.thingOne + ‘ ‘ + $scope.thingTwo; 
        }
    });
}]);
حال قصد داریم تا کمی ساختار کد پیشین را مستحکم‌تر کنیم. در بسیاری از مواقع اطلاعات دریافتی از view به این صورت است که در ابتدا نیاز به تایید صلاحیت دارند. در تکه کد زیر قصد داریم تا به متغیر‌های thingObe و thingTwo دو متد getter و setter را اضافه نماییم و پراپرتی‌ها را از حالت public خارج کنیم. در کد زیر ما، دو متد getter و setter برای هر کدام از متغیر‌ها که به حالت private تعریف شده‌اند، اضافه کرده ایم:
app.controller(‘ThingController’, [ ‘$scope’, function($scope) {
    // private
    var _thingOne = ‘one’,
        _thingTwo = ‘two’;

    // models
    angular.extend($scope, {
        get thingOne() {
        return _thingOne;
        },
        set thingOne(value) {
           if (value !== ‘one’ && value !== ‘two’) {
             throw new Error(‘Invalid value (‘+value+‘) for thingOne’);
        },
        get thingTwo() {
        return _thingTwo;
        },
        set thingTwo(value) {
           if (value !== ‘two’ && value !== ‘three’) {
             throw new Error(‘Invalid value (‘+value+‘) for thingTwo’);
        }
   });

    // methods
    angular.extend($scope, {
       // in HTML template, something like {{ things }}
       get things() { 
            return _thingOne + ‘ ‘ + _thingTwo; 
        }
    });
}]);
و در آخر اینکه با استفاده از این متد می‌توان ارث بری در سرویس‌های Angular را پیاده سازی کرد که جزئیات آن را می‌توانید از اینجا  دریافت کنید.
مطالب
ساختار پروژه های Angular
با توجه به پست‌ها منتشر شده قبلی درباره AngularJs به احتمال قوی شما نیز به این نتیجه رسیده اید که این فریم ورک برای انواع پروژه‌ها به ویژه پروژه هایی با مقیاس بزرگ بسیار مناسب است. منظور از ساختار پروژه Angular این است که به چه سبکی فایل‌های پروژه را سازمان دهی کنیم طوری که در هنگام توسعه و تغییرات با مشکل مواجه نشویم. عموما کد‌های مربوط به بخش frontend پروژه دارای ساختار قوی نمی‌باشند در نتیجه developer‌ها بیشتر سلیقه ای کد‌های مربوطه را می‌نویسند که با گذر زمان این مورد باعث بروز مشکل در امر توسعه نرم افزار می‌شود (نمونه بارز آن کدهای نوشته شده Jquery در صفحات است). AngularJs نیز همانند سایر کتابخانه‌ها و فریم ورک‌های جاوااسکریپتی دیگر از این امر مستثنی نیست و فایل‌های آن باید طبق روشی مناسب پیاده سازی و مدیریت شوند. انتخاب ساختار و روش سازمان دهی فایل‌ها وابستگی مستقیم به مقیاس پروژه دارد. ساختار پروژه‌های کوچک می‌تواند کاملا متفاوت با ساختار پروژه‌های بزرگ باشد. در این پست به بررسی چند روش در این زمینه خواهم پرداخت.
پروژه‌های کوچک عموما دارای ساختاری مشابه تصویر ذیل می‌باشند:

این مورد، روش پیشنهادی در Angular Seed است و بدین صورت است که تعاریف ماژول‌ها در فایل app.js انجام می‌گیرد. تعاریف و پیاده سازی تمام کنترلر‌ها در فایل controller.js است. و همچنین دایرکتیوها و فیلترها و سرویس‌ها هر کدام در فایل‌ها جداگانه تعریف و پیاده سازی می‌شوند. این روش راه حلی سریع برای پروژه‌های کوچک با تعداد developer‌های کم است. برای مثال زمانی که یک developer در حال ویرایش فایل controller.js است، از آن جا که فایل مورد نظر checkout خواهد شد در نتیجه سایر developer‌ها امکان تغییر در فایل مورد نظر را نخواهند داشت. سورس فایل‌ها به مرور زیاد خواهد شد و در نتیجه debug آن سخت می‌شود. 

روش دوم

در این حالت تعاریف کنترلر ها، مدل‌ها و سرویس‌ها هرکدام در یک دایرکتوری مجزا قرار خواهد گرفت. برای هر view یک کنترلر و بنا بر نیاز مدل تعریف می‌کنیم. ساختار آن به صورت زیر می‌شود:

دایرکتیو‌ها و فیلتر‌ها عموما در یک فایل قرار داده خواهند شد تا بنابر نیاز در جای مناسب رفرنس داده شوند. این روش ساختار مناسب‌تری نسبه به روش قبلی دارد اما دارای معایبی هم چون موارد زیر است:
»وابستگی بین فایل‌ها مشخص نیست در نتیجه بدون استفاده از کتابخانه هایی نظیر requireJs  با مشکل مواجه خواهید شد.
»refactoring کد‌ها تا حدودی سخت است.

روش سوم
این ساختار مناسب برای پیاده سازی پروژه‌ها به صورت ماژولار است و برای پروژه‌های بزرگ نیز بسیار مناسب است. در این حالت شما فایل‌های مربوط به هر ماژول را در دایرکتوری خاص آن قرار خواهید داد. به صورت زیر:

همان طور که ملاحظه می‌کنید سرویس ها، کنترلر‌ها و حتی مدل‌های مربوط به هر بخش در یک مسیر جداگانه قرار می‌گیرند. علاوه بر آن فایل هایی که قابلیت اشتراکی دارند در مسیری به نام common وجود دارند تا بتوان در جای مناسب برای استفاده از آن‌ها رفرنس داده شود. حتی اگر در پروژه خود فقط یک ماژول دارید باز سعی کنید از این روش برای مدیریت فایل‌های خود استفاده نمایید. اگر با ngStart آشنایی داشته باشید به احتمال زیاد با این روش بیگانه نیستید.
بررسی چند نکته درباره کد‌های مشترک
در اکثر پروژه‌های بزرگ، فایل‌ها و کد هایی وجود خواهد داشت که حالت اشتراکی بین ماژول‌ها دارند. در این روش این فایل‌ها در مسیری به نام common یا shared ذخیره می‌شوند. علاوه بر آن در Angular تکنیک هایی برای به اشتراک گذاشتن این اطلاعات وجود دارد.
»اگر ماژول‌ها وابستگی شدیدی به فایل‌ها و سورس‌های مشترک دارند باید اطمینان حاصل نمایید که این ماژولها فقط به اطلاعات مورد نیاز دسترسی دارند. این اصل interface segregation principle اصول SOLID است.
»توابعی که کاربرد زیادی دارند و اصطلاحا به عنوان Utility شناخته می‌شوند باید به rootScope$ اضافه شوند تا scope‌های وابسته نیز به آن‌ها دسترسی داشته باشند. این مورد به ویژه باعث کاهش تکرار وابستگی‌های مربوط به هر کنترلر می‌شود.
»برای جداسازی وابستگی‌های بین دو component بهتر از event‌ها استفاده نمایید. AngularJs این امکان را با استفاده از سرویس‌های on$ و emit$ و broadcast$ به راحتی میسر کرده است.
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت یازدهم - کار با فرم‌ها - قسمت دوم
مثال‌های این سری به نگارش RC2 به روز رسانی شدند. تغییرات صورت گرفته را در اینجا می‌توانید مشاهده کنید.
خلاصه‌ی آن در مورد «فرم‌های reactive جدید» به صورت زیر است:
1- به فایل systemjs.config.js، در قسمت ngPackageNames نام forms هم اضافه می‌شود. این forms از طریق وابستگی زیر
"dependencies": {
    "@angular/forms": "^0.1.0",
  },
که به فایل package.json اضافه می‌شود، تامین خواهد شد.
2- به فایل main.ts این تغییرات اضافه می‌شوند:
// ...
import {disableDeprecatedForms, provideForms} from '@angular/forms';
// ...
bootstrap(AppComponent, [
    disableDeprecatedForms(),
    provideForms()
]);
3- در کدها هرجایی Control هست، به FormControl تبدیل می‌شود که از ماژول ذیل تامین خواهد شد:
import { FormControl } from '@angular/forms';
4- ngFormModel به formGroup تغییرنام خواهد یافت. این دایرکتیو جدید به صورت زیر تامین می‌شود:
directives: [REACTIVE_FORM_DIRECTIVES]
5- تمام inputها باید دارای name باشند. در غیراینصورت خطا خواهید گرفت.
6- Validators اینبار از ماژول ذیل تامین می‌شود:
import { Validators, REACTIVE_FORM_DIRECTIVES, FormControl, FormGroup } from '@angular/forms';
بجای ماژول قدیمی angular/common. اگر این تغییر را اعمال نکنید، امکان استفاده‌ی از آن‌ها را در new FormControl نخواهید داشت.
7- FormBuilder سازگاری با reactive forms جدید ندارد و با FormGroup جایگزین شد.
نظرات مطالب
دریافت اطلاعات از پایگاه داده بواسطه Stored Procedure در EF Core 2.0