مطالب
ساختار پروژه های 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$ به راحتی میسر کرده است.
اشتراک‌ها
راه اندازی IdentityServer 4 و اتصال از طریق یک webApp Client

IdentityServer : سرور اصلی جهت احراز هویت

Clients
Resources
Scopes

IdentityOpenIdClient : یک کلاینت اپ جهت اتصال به سرور احراز هویت

هر دو پروژه همزمان باید اجرا شود

سپس در پروژه SampleClient ،از منو روی Contact جهت اتصال به سرور احراز هویت کلیک نمایید

جهت تکمیل و یا توسعه مشارکت نمایید.

راه اندازی IdentityServer 4 و اتصال از طریق یک webApp Client
اشتراک‌ها
آموزش AngularJS از کمپانی لیندا
مدت زمان فیلم یک ساعت
سرفصلهای آموزش شامل :

Introduction
  Welcome
  What you need to know
Using the exercise files
  Using the challenges
1. Configuring a New Angular Project
  Why Angular?
  Downloading Angular and dependencies
  Developing an application boilerplate
  Starting a Node server
2. Templates
  Supplying scope data
  Filtering output
  Controlling scopes
  Including partials
  Challenge: Editing airports
  Solution: Editing airports
3. Application Structure
  Routing views
  Supplying navigation
  Nesting scopes
  Linking individual records
  Challenge: Displaying two airports
  Solution: Displaying two airports
4. Server-Side Integration
  Defining services
  Retrieving individual records
  Searching through models
  Saving form data
  Challenge: Combining multiple data sources
  Solution: Combining multiple data sources
Conclusion
  Exploring advanced techniques
  Finding Angular resources 
آموزش AngularJS از کمپانی لیندا
نظرات مطالب
نوشتن اعتبارسنج‌های سفارشی برای فرم‌های مبتنی بر قالب‌ها در Angular
با تشکر از مطلب مفیدتون.
در اینجا یکسری دایرکتیو جهت اعتبار سنجی سفارشی بنده پیاده سازی کرده ام (فعلا فقط دو تا!) که نیازمند همکاری دوستان علاقمند دارد. (میتوان اعتبار سنجی‌های مربوط به فرمهای فارسی را نیز در اینجا به عنوان مرجعی مناسب برای استفاده سایرین نیز ایجاد کرد. از جمله اعتبار سنجی کدملی، شناسه ثبت شرکت، فارسی بودن اعداد در ورودی و ... که بخشی از این توابع در اینجا پیاده سازی شده است.)
همچنین در این دایرکتیوها دایرکتیوی به نام RemoteValidation پیاده سازی شده است که اعتبار سنجی نامتقارن را انجام میدهد. مشاهده کدهای این قسمت، برای آشنایی با اعتبار سنجی نامتقارن در Angular، خالی از لطف نیست.
نظرات مطالب
ساختار پروژه های Angular
تشکر.شما در مورد مسیر یابی هم قطعه کدی قرار دادید که میشود وابستگی‌ها و ... را تزریق کرد.منتها اگر ما بیش از 100 مسیر داشته باشیم باید چه کنیم؟ یعنی به ازای هر مسیر باید این قطعه کد تکرار شود :
$routeProvider.when('/about', {templateUrl:'views/about.html', resolve:{deps:function($q, $rootScope)
{
    var deferred = $q.defer();
    var dependencies =
    [
        'controllers/AboutViewController.js',
        'directives/some-directive.js'
    ];
  
    //*نکته اول 
    $script(dependencies, function()
    {
        // *نکته دوم
        $rootScope.$apply(function()
        {
            deferred.resolve();
        });
    });
  
    return deferred.promise;
}}})
راه حل پویایی وجود دارد؟
مثلا شما در ساختار سوم بیان کردید که فایل‌های مربوط به هر قسمت در کنار هم باشند. اعم از کنترلر و دایرکتیوها و فیلترها و ... .
آیا میشود برای هر قسمت مثل product , user ,cart ، یک ماژول app جدا نوشت و در آن طبق مثال شما مسیریابی را تولید کرد؟ یعنی چندین ماژول انگولار app.js برای یک پروژه نوشت؟استاندارد است؟ بدین صورت دیگر نگران تعداد مسیرهای زیاد نیستیم و مشخص میشود که مسیریابی هر قسمت در کنار آن وجود دارد.
امکان پذیر است؟ اگر نیست شما چه راهی برای این کار دارید. ممنون
مطالب
Lazy Loading در AngularJS
وقتی پروژه انگیولاری‌تان کمی گسترش پیدا کند، تعداد زیادی فایل شامل کنترلرها، سرویس‌ها، دایرکتیوها و ... خواهید داشت. واضح است که همه این اجزا همراه با هم مورد نیاز نیستند و برای افزایش سرعت بارگذاری سایت و صرفه جویی در مصرف پهنای باند بهتر است هرکدام از آن‌ها را در هنگام نیاز بارگذاری کنیم. این یعنی همان lazy loading خودمان!
در AngularJS امکانی برای lazy loading فایل‌ها پیشبینی نشده است، پس باید از ابزارهای دیگری که این امکان را فراهم می‌کنند استفاده کرد. من در ادامه از Script.js برای این کار استفاده خواهم کرد، ولی شما می‌توانید از هر کتابخانه دیگری استفاده کنید.
اما مسئله دیگری که پیش از lazy loading  فایل‌ها باید تکلیفش را معلوم کنیم، این است که چطور می‌توانیم اجزایی را به ماژولی که قبلا راه‌اندازی (bootstrap) شده اضافه کنیم. اگر بخواهیم برای مثال کنترلری را در یک فایل مجزا تعریف کنیم، باید آن را به شکلی در ماژول برنامه‌مان ثبت کنیم. فرض کنید این کار را به این ترتیب انجام دهیم: 
angular.module('app').controller('SomeLazyController', function($scope)
{
    $scope.key = '...';
});
در این صورت اگر این کنترلر را در قسمتی از برنامه به صورت ng-controller='SomeLazyController' استفاده کنیم با این خطا مواجه خواهیم شد:
Error: Argument ‘SomeLazyController’ is not a function, got undefined
برای این کار (افزودن اجزایی به ماژولی که قبلا راه اندازی شده) می‌توانیم بجای استفاده از API‌های ماژول، از provider های AngularJS استفاده کنیم. به این ترتیب برای ثبت یک کنترلر باید از  controllerProvider$، برای ثبت یک directive از  compileProvider$، برای ثبت فیلترها از filterProvider$ و برای ثبت سایر اجزا در ماژول از provide$ استفاده کنیم:
// Registering a controller after app bootstrap
$controllerProvider.register('SomeLazyController', function($scope)
{
   $scope.key = '...'; 
});
 
// Registering a directive after app bootstrap
$compileProvider.directive('SomeLazyDirective', function()
{
    return {
        restrict: 'A',
        templateUrl: 'templates/some-lazy-directive.html'
    }
})
 
// etc
اما نکته‌ای که درباره providerها وجود دارد این است که آن‌ها تنها در روال config یک ماژول در دسترس هستند. بنا بر این برای دسترسی به آن‌ها پس از اجرای این روال، ارجاعی به آنها را باید نگهداری کنیم: 
(function () {
    app = angular.module("app", []);

    app.config([
        '$controllerProvider',
        '$compileProvider',
        '$filterProvider',
        '$provide',

    function ($controllerProvider, $compileProvider, $filterProvider, $provide) {
        //برای رجیستر کردن غیر همروند اجزای انگیولاری در آینده
        app.lazy =
        {
            controller: $controllerProvider.register,
            directive: $compileProvider.directive,
            filter: $filterProvider.register,
            factory: $provide.factory,
            service: $provide.service
        };
    }]);
})();
اکنون SomeLazyController  را به این ترتیب می‌توانیم ثبت کنیم: 
angular.module('app').lazy.controller('SomeLazyController', function($scope)
{
    $scope.key = '...';
});
نکته دیگر این است که کجا باید lazy loadign را انجام دهیم. به نظر می‌رسد مناسب‌ترین محل برای انجام این کار خصوصیت resolve مسیریابی است. در  این مطلب و این مطلب resolve در route$ و UI-Router معرفی شده است:
$stateProvider
            .state('state1', {
                url: '/state1',
                template: '<div>{{st1Ctrl.msg}}</div>',
                controller: 'state1Controller as st1Ctrl',
                resolve: {
                    fileDeps: ['$q', '$rootScope', function ($q, $rootScope) {
                        var deferred = $q.defer();
                        var deps = [
                            'app/messageService.js',
                            'app/state1Controller.js'];
                        $script(deps, function () {
                            $rootScope.$apply(function () {
                                deferred.resolve();
                            });
                        });
                        return deferred.promise;
                    }]
                }
            })
            .state('state2', {
                url: '/state2',
                template: '<div>{{st2Ctrl.msg}}</div>',
                controller: 'state2Controller as st2Ctrl',
                resolve: {
                    fileDeps: ['$q', '$rootScope', function ($q, $rootScope) {
                        var deferred = $q.defer();
                        var deps = [
                            'app/messageService.js',
                            'app/state2Controller.js'];
                        $script(deps, function () {
                            $rootScope.$apply(function () {
                                deferred.resolve();
                            });
                        });
                        return deferred.promise;
                    }]
                }
            });
    }]);
کنترلر state1Controller که در فایلی با همین نام پیاده‌سازی شده است تنها در مسیر state1/ مورد نیاز است، و state2Controller تنها در مسیر state2/ لازم است بارگذاری شود. هردوی این کنترلرها به messageService وابستگی دارند که در messageService.js پیاده سازی شده است (همانطور که در این مطلب   اشاره شده می‌توانیم یک حالت انتزاعی به عنوان پدر دو حالت موجود تعریف کرده و وابستگی مشترک را به آن منتقل کنیم).
 برای بارگذاری فایل‌های مورد نیاز در ابتدای کار و راه اندازی اولیه برنامه هم می‌توان به این ترتیب عمل کرد:  
<script type="text/javascript">
        // ----Script.js----
        !function (a, b, c) { function t(a, c) { var e = b.createElement("script"), f = j; e.onload = e.onerror = e[o] = function () { e[m] && !/^c|loade/.test(e[m]) || f || (e.onload = e[o] = null, f = 1, c()) }, e.async = 1, e.src = a, d.insertBefore(e, d.firstChild) } function q(a, b) { p(a, function (a) { return !b(a) }) } var d = b.getElementsByTagName("head")[0], e = {}, f = {}, g = {}, h = {}, i = "string", j = !1, k = "push", l = "DOMContentLoaded", m = "readyState", n = "addEventListener", o = "onreadystatechange", p = function (a, b) { for (var c = 0, d = a.length; c < d; ++c) if (!b(a[c])) return j; return 1 }; !b[m] && b[n] && (b[n](l, function r() { b.removeEventListener(l, r, j), b[m] = "complete" }, j), b[m] = "loading"); var s = function (a, b, d) { function o() { if (!--m) { e[l] = 1, j && j(); for (var a in g) p(a.split("|"), n) && !q(g[a], n) && (g[a] = []) } } function n(a) { return a.call ? a() : e[a] } a = a[k] ? a : [a]; var i = b && b.call, j = i ? b : d, l = i ? a.join("") : b, m = a.length; c(function () { q(a, function (a) { h[a] ? (l && (f[l] = 1), o()) : (h[a] = 1, l && (f[l] = 1), t(s.path ? s.path + a + ".js" : a, o)) }) }, 0); return s }; s.get = t, s.ready = function (a, b, c) { a = a[k] ? a : [a]; var d = []; !q(a, function (a) { e[a] || d[k](a) }) && p(a, function (a) { return e[a] }) ? b() : !function (a) { g[a] = g[a] || [], g[a][k](b), c && c(d) }(a.join("|")); return s }; var u = a.$script; s.noConflict = function () { a.$script = u; return this }, typeof module != "undefined" && module.exports ? module.exports = s : a.$script = s }(this, document, setTimeout)

        $script('Scripts/angular.js', function () {
            $script('Scripts/angular-ui-router.js', function () {
                $script('app/app.js', function () {
                    angular.bootstrap(document, ['app']);
                });
            });
        });
    </script>
توجه داشته باشید که لازم نیست بارگذاری فایل‌ها حتما یکی پس از دیگری باشد. ترتیب بارگذاری فایل‌ها تنها در آن‌هایی که وابستگی به هم دارند باید رعایت شود. همچنین، می‌توانید همه فایل‌های مورد نیاز در این مرحله را Bundle کنید. 
از اینجا می‌توانید پروژه بسیار ساده‌ای که در آن lazy loading پیاده شده است را دانلود کرده و مطالب توضیح داده شده را مشاهده کنید. 
مطالب
ایجاد Structural Directives در Angular 2
در مقاله پیشین در مورد Attribute Directive‌ها صحبت کردیم که به کمک آن‌ها میتوانیم باعث تغییری در یک المان شویم. در این مقاله، نوع جدید دیگری از دایرکتیوها را به نام Structural Directive، بررسی می‌نماییم. این نوع دایرکتیوها در انگیولار به وفور استفاده میشوند و ngIf و ngFor نمونه‌ای از آن‌ها هستند. در این ادامه قصد داریم نمونه‌ای از ngIf را بازسازی کنیم و در واقع این بازسازی به ما میگوید که ngIf چگونه کار می‌کند:
import { Directive, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[myIf]',
  inputs:["myIf"]
})
export class UnlessDirective {

  constructor(private _templateRef:TemplateRef<any>,private _viewContainerRef:ViewContainerRef) { 
  }

  set myIf(condition:true)
  {
    if(!condition)
    {
      this._viewContainerRef.clear();
    }
    else{
      this._viewContainerRef.createEmbeddedView(this._templateRef);
    }
  }
}
نام این دایرکتیو را myIf قرار میدهیم و ورودی آن را هم به همین نام، تعیین میکنیم. سازنده‌ی این کلاس دارای دو پارامتر هست که یکی از آن‌ها مقدار TemplateRef است که تگ و فرزندان آن تگی را که دایرکتیو بر روی آن اعمال شده است، به همراه دارد که بعدا موقع نمایش، باید از این TemplateRef برای بازسازی‌های داخلی آن شامل بایند کردن‌ها و ... استفاده کند.  ElementRef نیز در ساختار داخلی آن قرار دارد. دومین پارامتر آن از نوع ViewContainerRef است که شما میتوانید TemplateRef را به آن attach کنید.

سپس ورودی را به شکل متد نوشته و عبارت set را جلوی آن می‌نویسیم. عبارت set به این معناست که وقتی مقداری به این ورودی set شد، متد myIf را اجرا کن. اگر نتیجه شرط ورودی false شده باشد، محتوای ViewContainer پاک خواهد شد و اگر محتوای true داشته باشد templateRef به آن اضافه خواهد شد.

برای استفاده‌ی از دایرکتیو نوشته شده، از تکه کد زیر استفاده میکنیم:
<input type="text" #x  (keyup)="0"/>
<div *myIf="x.value=='yes'">
it means we are on condition
</div>
ابتدا یک المان ورودی را تعریف کرده و برای این المان از طریق # یک ID تعریف میکنیم تا از این طریق به آن متصل شویم. از آنجاکه در خط بعدی دایرکتیو را صدا میزنیم و مقدار این ورودی را بررسی مینماییم، باید به کنترل ورودی، رویداد KeyUp را معرفی کنیم تا این رویداد فعال شود. از آنجاکه قصد معرفی تابعی را برای keyup نداریم، عدد 0 را وارد می‌نماییم تا تنها این رویداد را فعال کنیم. حال هر موقع عبارت yes در کنترل ورودی نوشته شود، شرط true شده و عبارت it means we are on Condition به کاربر نمایش داده خواهد شد.
مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت سوم - غنی سازی کامپوننت‌ها
در قسمت قبل، مقدمه‌ای بر نحوه‌ی تعریف یک کامپوننت در AngularJS 2.0 عنوان شد و همچنین نحوه‌ی بوت استرپ و آغاز اینگونه برنامه‌ها بررسی گردید. در این قسمت می‌خواهیم امکانات پیشرفته‌تری از کامپوننت‌ها را بررسی کنیم.


روش‌های مختلف تعریف خاصیت template در یک کامپوننت

در قسمت قبل، روش تعریف inline یک template را مشاهده کردید:
template:`
          <div><h1>{{pageTitle}}</h1>
               <div>My First Component</div>
          </div>
 `
در اینجا رشته‌ی قالب نهایی این View، در همان تعاریف متادیتای Component قرار گرفته‌است (روش inline). اگر این رشته تک سطری باشد، از روش متداول ذکر "" برای تعریف رشته‌ها در جاوا اسکریپت استفاده می‌شود و اگر این رشته چند سطری باشد، از back tick مربوط به ES 6 مانند مثال فوق کمک گرفته خواهد شد. استفاده از back ticks و رشته‌های چند سطری، نحوه‌ی تعریف قالب‌های inline را خواناتر می‌کند.
هر چند این روش تعریف قالب‌ها، مزیت سادگی و امکان مشاهده‌ی View را به همراه کدهای مرتبط با آن، در یک فایل میسر می‌کند، اما به دلیل رشته‌ای بودن، مزیت کار کردن با ادیتورهای وب، مانند داشتن intellisense، فرمت خودکار کدها و بررسی syntax را از دست خواهیم داد و با بیشتر شدن حجم این رشته، این مشکلات بیشتر نمایان خواهند شد.
به همین جهت قابلیت دیگری به نام linked template نیز در اینجا درنظر گرفته شده‌است:
 templateUrl: 'product-list.component.html'
در این حالت، محتوای قالب، به یک فایل html مجزا منتقل شده و سپس لینک آن در خاصیت دیگری از متادیتای Component به نام templateUrl ذکر می‌شود.


ساخت کامپوننت نمایش لیست محصولات

در ادامه می‌خواهیم کامپوننتی را طراحی کنیم که آرایه‌ای از محصولات را نمایش می‌دهد. در اینجا مرسوم است هر ویژگی برنامه، در یک پوشه‌ی مجزا قرار گیرد. به همین جهت در ادامه‌ی مثال قسمت قبل که پوشه‌ی app را به ریشه‌ی پروژه اضافه کردیم و سپس main.ts راه انداز و کامپوننت ریشه‌ی سایت یا app.component.ts را در آن تعریف کردیم، در داخل همین پوشه‌ی app، پوشه‌ی جدیدی را به نام products اضافه می‌کنیم. سپس به این پوشه‌ی جدید محصولات، فایل جدیدی را به نام product-list.component.html اضافه کنید. از این فایل جهت تعریف قالب کامپوننت لیست محصولات استفاده خواهیم کرد. در اینجا نیز مرسوم است نام قالب یک Component را به صورت نام ویژگی ختم شده‌ی به کلمه‌ی Component، با پسوند html تعریف کنیم.


پس از اضافه شدن فایل product-list.component.html، محتوای آن‌را به نحو ذیل تغییر دهید:
<div class='panel panel-default'>
    <div class='panel-heading'>
        {{pageTitle}}
    </div>
    <div class='panel-body'>
        <div class='row'>
            <div class='col-md-2'>Filter by:</div>
            <div class='col-md-4'>
                <input type='text' />
            </div>
        </div>
        <div class='row'>
            <div class='col-md-6'>
                <h3>Filtered by: </h3>
            </div>
        </div>
        <div class='table-responsive'>
            <table class='table'>
                <thead>
                    <tr>
                        <th>
                            <button class='btn btn-primary'>
                                Show Image
                            </button>
                        </th>
                        <th>Product</th>
                        <th>Code</th>
                        <th>Available</th>
                        <th>Price</th>
                        <th>5 Star Rating</th>
                    </tr>
                </thead>
                <tbody>
 
                </tbody>
            </table>
        </div>
    </div>
</div>
در اینجا قصد داریم داخل پنل بوت استرپ 3، لیستی از محصولات را به صورت یک جدول نمایش دهیم. همچنین می‌خواهیم قابلیت جستجوی داخل این لیست را نیز فراهم کنیم. فعلا شکل کلی این قالب را به نحو فوق تهیه می‌کنیم. قسمت tbody جدول آن را که قرار است لیست محصولات را رندر کند، در ادامه‌ی بحث تکمیل خواهیم کرد.
تنها نکته‌ی AngularJS 2.0 قالب فوق، اتصال به pageTitle است که نمونه‌ای از آن‌را در قسمت قبل با معرفی اولین کامپوننت مشاهده کرده‌اید.

در ادامه نیاز است برای این قالب و view، یک کامپوننت را طراحی کنیم که متشکل است از یک کلاس TypeScript ایی مزین شده به Component. بنابراین فایل ts جدیدی را به نام product-list.component.ts به پوشه‌ی App\products اضافه کنید؛ با این محتوا:
import { Component } from 'angular2/core';
 
@Component({
    selector: 'pm-products',
    templateUrl: 'app/products/product-list.component.html'
})
export class ProductListComponent {
    pageTitle: string = 'Product List';
}


با جزئیات نحوه‌ی تعریف یک کامپوننت در قسمت قبل در حین معرفی کامپوننت‌ها آشنا شدیم. در اینجا کلاس ProductListComponent با واژه‌ی کلیدی export همراه است تا توسط module loader برنامه قابلیت بارگذاری را پیدا کند. همچنین خاصیت عمومی pageTitle نیز در آن تعریف شده‌است تا در قالب مرتبط مورد استفاده قرار گیرد.
سپس این کلاس، با decorator ویژه‌ای به نام Component مزین شده‌است تا AngularJS 2.0 بداند که هدف از تعریف آن، ایجاد یک کامپوننت جدید است. مقدار selector آن که تشکیل دهنده‌ی یک تگ HTML سفارشی متناظر با آن خواهد شد، به pm-products تنظیم شده‌است و اینبار بجای تعریف inline قالب آن به صورت یک رشته، از خاصیت templateUrl جهت معرفی مسیر فایل html قالبی که پیشتر آماده کردیم، کمک گرفته شده‌است.


نمایش کامپوننت لیست محصولات در صفحه‌ی اصلی سایت

خوب، تا اینجا یک کامپوننت جدید را به نام لیست محصولات، ایجاد کردیم؛ اما چگونه باید آن‌را نمایش دهیم؟
در قسمت قبل که کامپوننت ریشه‌ی برنامه یا AppComponent را تعریف کردیم، نام selector آن را pm-app درنظر گرفتیم و در نهایت این directive سفارشی را به نحو ذیل در body صفحه‌ی اصلی سایت نمایش دادیم:
    <div>
        @RenderBody()
        <pm-app>Loading App...</pm-app>
    </div>
اما این روش، تنها برای root component سایت مناسب است. برای سایر کامپوننت‌های غیر ریشه‌ای (یعنی تمام کامپوننت‌ها)، سه مرحله‌ی زیر باید طی شوند:
الف) تگ سفارشی این دایرکتیو جدید را به کامپوننت ریشه‌ی سایت یا همان AppComponent اضافه می‌کنیم. بنابراین فایل app.component.ts را گشوده و سپس selector کامپوننت لیست محصولات را به قالب آن اضافه کنید:
import { Component } from 'angular2/core';
 
@Component({
    selector: 'pm-app',
    template:`
    <div><h1>{{pageTitle}}</h1>
        <pm-products></pm-products>
    </div>
    `
})
export class AppComponent {
    pageTitle: string = "DNT AngularJS 2.0 APP";
}
همانطور که مشاهده می‌کنید، تگ جدید pm-products بر اساس نام selector کامپوننت لیست محصولات، به قالب کامپوننت ریشه‌ی سایت اضافه شده‌است.
ب) تا اینجا یک دایرکتیو جدید را به نام pm-products به یک کامپوننت دیگر اضافه کرده‌ایم. اما این کامپوننت نمی‌داند که اطلاعات آن‌را باید از کجا تامین کند. برای این منظور خاصیت جدیدی را به نام directives به لیست خاصیت‌های Component ریشه‌ی سایت اضافه می‌کنیم. این خاصیت، آرایه‌ای از دایرکتیوهای سفارشی را قبول می‌کند:
 directives: [ProductListComponent]
ج) بلافاصله که این تغییر را اعمال کنید، در ادیتور TypeScript ایی موجود، ذیل کلمه‌ی ProductListComponent خط قرمز کشیده خواهد شد. چون هنوز مشخص نکرده‌ایم که این شیء جدید باید از کدام ماژول تامین شود و ناشناخته‌است. بنابراین import مربوطه را به ابتدای فایل اضافه می‌کنیم:
import { Component } from 'angular2/core';
import { ProductListComponent } from './products/product-list.component';
 
@Component({
    selector: 'pm-app',
    template:`
    <div><h1>{{pageTitle}}</h1>
        <pm-products></pm-products>
    </div>
    `,
    directives: [ProductListComponent]
})
export class AppComponent {
    pageTitle: string = "DNT AngularJS 2.0 APP";
}
کدهای فوق، کد نهایی کامپوننت ریشه‌ی سایت هستند که به آن selector جدیدی به نام pm-products اضافه شده‌است. سپس directive متناظر آن به لیست دایرکتیوهای کامپوننت جاری اضافه شده و در نهایت این دایرکتیو، از ماژول مرتبط با آن import شده‌است.

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

خوب، اکنون اگر برنامه را اجرا کنیم، چنین خروجی را می‌توان مشاهده کرد:


یک نکته
اگر برنامه را اجرا کردید و خروجی را مشاهده نکردید، مطمئن شوید که فایل‌های ts شما کامپایل شده‌اند. فشردن دکمه‌ی ctrl+s مجدد در این فایل‌ها، سبب کامپایل مجدد آن‌ها می‌شوند و یا انتخاب گزینه‌ی Build و سپس ReBuild solution نیز همینکار را انجام می‌دهد.


غنی سازی کامپوننت‌های AngularJS 2.0 با data-binding

در AngularJS 2.0 عملیات binding، کار مدیریت ارتباطات بین یک کلاس کامپوننت و قالب آن‌را انجام می‌دهد. نمونه‌ای از آن‌را پیشتر با خاصیت pageTitle و سپس نمایش آن در قالب کامپوننت متناظر با آن کلاس، مشاهده کرده‌اید. همچنین در اینجا یک قالب می‌تواند متدهای داخل کلاس کامپوننت خود را توسط رخدادها نیز فراخوانی کند.
به نحوه‌ی نمایش {{pageTitle}} اصطلاحا interpolation می‌گویند. در اینجا خاصیت pageTitle اطلاعات خود را از کلاس کامپوننت دریافت می‌کند. به این نوع binding، انقیاد یک طرفه یا one-way binding نیز گفته می‌شوند؛ از خاصیت کلاس شروع شده و به قالب خاتمه می‌یابد.
ویژگی interpolation فراتر است از صرفا نمایش یک خاصیت و می‌تواند حاوی محاسبات نیز باشد:
{{'Title: ' + pageTitle}}
{{2*20+1}}
و یا حتی در آن می‌توان متدی از کلاس کامپوننت را نیز فراخوانی کرد. در مثال زیر فرض شده‌است که متد getTitle، در کلاس متناظر با کامپوننت این قالب، تعریف شده‌است:
{{'Title: ' + getTitle()}}
کار interpolation درج عبارت محاسبه شده‌ی نهایی بین المان‌های html است؛ مانند:
 <h1>{{pageTitle}}</h1>
و یا حتی می‌توان این مقدار نهایی را به خواص المان‌های html نیز نسبت داد:
 <h1 innerText={{pageTitle}}></h1>
در این مثال خاصیت innerText المان h1 توسط interpolation مقدار دهی شده‌است.

بنابراین به صورت خلاصه هر زمانیکه نیاز به نمایش اطلاعات فقط خواندنی (one-way binding) داریم، ابتدا خاصیتی را در کلاس کامپوننت تعریف کرده و سپس مقدار این خاصیت را توسط interpolation، در قالب کامپوننت درج می‌کنیم. حین استفاده از interpolation نیازی به ذکر "" نیست.
در مورد مباحث تکمیلی binding در قسمت‌های بعدی بیشتر بحث خواهیم کرد.


افزودن منطقی سفارشی به قالب یک کامپوننت

دایرکتیوها به صورت المان‌ها و یا ویژگی‌های سفارشی HTML، قابلیت توسعه‌ی امکانات پیش فرض آن‌را دارند. در اینجا می‌توان دایرکتیوهای سفارشی خود را تولید کرد (مانند pm-products فوق) و یا از دایرکتیوهای توکار AngularJS 2.0 استفاده کرد. برای مثال ngIf* و ngFor* جزو structural directives توکار AngularJS 2.0 هستند. ستاره‌ای که پیش از نام این دایرکتیوها قرار گرفته‌است، آن‌‌ها را در گروه structural directives قرار می‌دهد.
کار دایرکتیوهای ساختاری، تغییر ساختار یا همان view کامپوننت‌ها است؛ با افزودن، حذف و یا تغییر المان‌های HTML تعریف شده‌ی در صفحه.

بررسی ngIf*

فایل قالب product-list.component.html را گشوده و تعریف جدول آن‌را به نحو ذیل تغییر دهید:
 <table class='table' *ngIf='products && products.length'>
کار ngIf* نمایش یا عدم نمایش قسمتی از DOM یا document object model بر اساس برآورده شدن منطقی است که توسط آن بررسی می‌شود. اگر حاصل عبارتی که به ngIf* انتساب داده می‌شود به false تعبیر شود، آن المان و فرزندان آن از DOM حذف می‌شوند و اگر این عبارت به true تعبیر شود، آن المان و فرزندانش مجددا به DOM اضافه خواهند شد.
برای نمونه عبارت انتساب داده شده‌ی به ngIf* در مثال فوق به این معنا است که اگر خاصیت و آرایه‌ی products در کلاس کامپوننت این قالب تعریف شده بود و همچنین دارای اعضایی نیز بود، آنگاه این جدول را نمایش بده.
برای آزمایش آن، فایل product-list.component.ts را گشوده و خاصیت عمومی آرایه‌ی products را به نحو ذیل به آن اضافه کنید:
import { Component } from 'angular2/core';
 
@Component({
    selector: 'pm-products',
    templateUrl: 'app/products/product-list.component.html'
})
export class ProductListComponent {
    pageTitle: string = 'Product List';
    products: any[] = [
        {
            "productId": 2,
            "productName": "Garden Cart",
            "productCode": "GDN-0023",
            "releaseDate": "March 18, 2016",
            "description": "15 gallon capacity rolling garden cart",
            "price": 32.99,
            "starRating": 4.2,
            "imageUrl": "app/assets/images/garden_cart.png"
        },
        {
            "productId": 5,
            "productName": "Hammer",
            "productCode": "TBX-0048",
            "releaseDate": "May 21, 2016",
            "description": "Curved claw steel hammer",
            "price": 8.9,
            "starRating": 4.8,
            "imageUrl": "app/assets/images/rejon_Hammer.png"
        }
    ];
}
فعلا چون اینترفیسی را برای شیء محصول تعریف نکرده‌ایم، نوع این آرایه را any یا همان حالت پیش فرض جاوا اسکریپت تعریف می‌کنیم.
همچنین فعلا در اینجا اطلاعات را بجای دریافت از سرور، توسط آرایه‌ی مشخصی از اشیاء تعریف کرده‌ایم. این موارد را در قسمت‌های بعدی بهبود خواهیم بخشید.

اکنون که خاصیت عمومی products تعریف شده‌است، امکان استفاده‌ی از ngIf* ایی که پیشتر تعریف کردیم، میسر شده‌است. در این حالت اگر برنامه را اجرا کنید، قسمت table header تصویر قبلی نمایش سایت، هنوز نمایان است. یعنی ngIf* تعریف شده کار می‌کند؛ چون خاصیت products تعریف شده‌است و همچنین دارای اعضایی است.
برای آزمایش بیشتر، خاصیت products را کامنت کنید و یکبار نیز فایل ts آن‌را ذخیره کنید تا فایل js متناظر با آن کامپایل شود. سپس مجددا برنامه را اجرا کنید. در این حالت دیگر نباید هدر جدول نمایان باشد؛ چون products تعریف نشده‌است.


بررسی ngFor*

تا اینجا بر اساس داشتن لیستی از محصولات یا عدم آن، جدول متناظری را نمایش داده و یا مخفی کردیم. اما این جدول هنوز فاقد ردیف‌های نمایش اعضای آرایه‌ی products است.
برای این منظور مجددا فایل قالب product-list.component.html را گشوده و سپس بدنه‌ی جدول را به نحو ذیل تکمیل کنید:
<tbody>
    <tr *ngFor='#product of products'>
        <td></td>
        <td>{{ product.productName }}</td>
        <td>{{ product.productCode }}</td>
        <td>{{ product.releaseDate }}</td>
        <td>{{ product.price }}</td>
        <td>{{ product.starRating }}</td>
    </tr>
</tbody>
یکی دیگر از دایرکتیوهای ساختاری، ngFor* نام دارد. کار آن تکرار قسمتی از DOM، به ازای تک تک عناصر لیست انتساب داده شده‌ی به آن است.
بنابراین ابتدا قسمتی از عناصر HTML را طوری کنار هم قرار می‌دهیم که جمع آن‌ها یک تک آیتم را تشکیل دهند. سپس با استفاده از ngFor* به AngularJS 2.0 اعلام می‌کنیم که این قطعه را به ازای عناصر لیست دریافتی، تکرار و رندر کند.
برای نمونه در مثال فوق می‌خواهیم ردیف‌های جدول تکرار شوند. بنابراین هر ردیف را به عنوان یک قطعه‌ی تکرار شونده‌ی توسط ngFor* مشخص می‌کنیم. به این ترتیب این ردیف و عناصر فرزند آن، به ازای تک تک محصولات موجود در آرایه‌ی products، تکرار خواهند شد.
علامت # در اینجا (product#) یک متغیر محلی را تعریف می‌کند که تنها در قالب جاری قابل استفاده خواهد بود و همچنین فقط در فرزندان tr تعریف شده قابل دسترسی هستند.
به علاوه در اینجا بجای in از of استفاده شده‌است. این of از ES 6 گرفته شده‌است. زمانیکه از حلقه‌ی جدید for...of استفاده می‌شود، متغیر محلی product حاوی یک عنصر از لیست product خواهد بود؛ اما اگر از حلقه‌ی قدیمی for...in استفاده می‌شد، تنها ایندکس عددی این عناصر در دسترس قرار می‌گرفتند. به همین جهت است که در این حلقه، اکنون product.productName به نام محصول آن عنصر آرایه‌ی دریافتی اشاره می‌کند و قابل استفاده است.

تا اینجا اگر برنامه را اجرا کنید، چنین خروجی را مشاهده خواهید کرد:


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: MVC5Angular2.part3.zip


خلاصه‌ی بحث

از inline templateها جهت معرفی قالب‌های کوتاه استفاده می‌شود. در اینجا از "" برای معرفی قالب یک سطری و یا از back tickهای ES 6، برای تعریف قالب‌های چندسطری استفاده خواهد شد. برای قالب‌های مفصل‌تر، بهتر است Linked templateها استفاده شود؛ با پشتیبانی کامل ادیتورهای موجود از لحاظ تکمیل و بررسی کدها.
برای استفاده از یک کامپوننت در کامپوننتی دیگر، نام selector آن‌را به صورت یک المان جدید HTML در قالب دیگری ذکر کرده و سپس با استفاده از خاصیت directives، نام کلاس متناظر با آن‌را نیز ذکر می‌کنیم. همچنین کار import ماژول آن نیز باید در ابتدای فایل صورت گیرد.
جهت غنی سازی قالب‌ها و کامپوننت‌ها و نمایش اطلاعات فقط خواندنی می‌توان از binding یک طرفه‌ی ویژه‌ای به نام interpolation استفاده کرد. کار آن اتصال یک خاصیت عمومی کلاس کامپوننت، به قالب آن است. interpolation توسط {{}} تعریف می‌شود و می‌تواند شامل محاسبات نیز باشد.
همچنین در ادامه‌ی بحث، نحوه‌ی کار با دو دایرکتیو توکار ساختاری AngularJS 2.0 را نیز بررسی کردیم. این دایرکتیوهای ساختاری نیاز است با ستاره شروع شوند و عبارت انتساب داده شده‌ی به آن‌ها باید داخل "" قرار گیرد (برخلاف interpolation که نیازی به اینکار ندارد). از ngIf* برای حذف یا افزودن یک المان و فرزندان آن از/به DOM استفاده می‌شود. اگر عبارت منتسب به آن به true ارزیابی شود، این المان از صفحه حذف خواهد شد. از ngFor* برای تکرار المانی مشخص به همراه فرزندان آن به تعداد اعضای لیستی که برای آن تعیین می‌گردد، استفاده می‌شود. متغیر محلی این پیمایشگر با # مشخص شده و حلقه‌ی آن با of بجای in تعریف می‌شود.