مطالب
ساخت کتابخانه‌های عمومی جاوا اسکریپتی توسط Angular CLI
این روزها ساخت کتابخانه‌های مدرن جاوا اسکریپتی که نیازهای انواع و اقسام توسعه دهندگان آن‌ها را پوشش دهند، مشکل است. این کتابخانه‌ها باید دارای بسته‌های مختلفی با پشتیبانی از ES5 و ES2015 به بعد باشند؛ همچنین ارائه‌ی متادیتای مخصوص TypeScript را نیز پشتیبانی کنند. به علاوه مباحث کارآیی، minification و tree shaking (حذف کدهای مرده) را نیز مدنظر داشته باشید.
پیشتر مطلبی را در مورد ساخت کتابخانه‌های مخصوص Angular را به کمک Angular CLI، در این سایت مطالعه کرده بودید. در این مطلب فرض بر این است که شما توسعه دهنده‌ی Angular «نیستید»، اما قصد دارید با استفاده از ابزار Angular CLI، کتابخانه‌ی جاوا اسکریپتی عمومی بسیار مدرنی را با پشتیبانی از تمام موارد یاد شده، تولید کنید.


ساخت قالب آغازین کتابخانه به کمک Angular CLI

برای تبدیل کتابخانه‌های جاوا اسکریپتی خود به قالب مدرنی که در مقدمه عنوان شد، نیاز به ابزاری جهت خودکارسازی فرآیند‌های آن داریم و این ویژگی‌ها مدتی است که به ابزار Angular CLI اضافه شده‌اند و همانطور که عنوان شد، مخاطب این مطلب، توسعه دهندگان عمومی JavaScript است و نه صرفا توسعه دهندگان Angular. به همین جهت نیاز است ابتدا این ابزار را نصب کرد:
npm install -g @angular/cli
برای اجرای دستور فوق در خط فرمان، ابتدا باید آخرین نگارش nodejs را نیز نصب کرده باشید.
پس از نصب Angular CLI، از آن جهت ساخت قالب تولید کتابخانه‌های TypeScript ای استفاده می‌کنیم:
 ng new my-math-app
این دستور یک قالب پروژه‌ی آغازین Angular را ایجاد کرده و همچنین وابستگی‌های npm آن‌را نیز نصب می‌کند (بنابراین نیاز است به اینترنت نیز متصل باشید). البته ممکن است در حین اجرای این دستور سؤالاتی مبنی بر ایجاد مسیریابی و یا انتخاب بین css و sass نیز پرسیده شود. این موارد برای کار ما در اینجا مهم نیستند و هر پاسخی را که مایل بودید، ارائه دهید. در این مطلب ما کاری به این قالب نخواهیم داشت. فقط هدف ما افزودن یک کتابخانه‌ی جدید به آن است.
بنابراین پس از اجرای دستور فوق، از طریق خط فرمان به پوشه‌ی my-math-app وارد شده و سپس دستور زیر را اجرا کنید:
 ng generate library ts-math-example
این دستور، قالب آغازین یک کتابخانه‌ی جدید TypeScript ای را به پروژه‌ی Angular ما با نام ts-math-example اضافه می‌کند. اکنون می‌توانیم از این قالب جهت توسعه‌ی کتابخانه‌ی مدرن جاوا اسکریپتی خود استفاده کنیم.


تکمیل کتابخانه‌ی جاوا اسکریپتی

اکنون که به لطف Angular CLI، قالب آغازین ساخت یک کتابخانه‌ی TypeScript ای را داریم، می‌توانیم شروع به تکمیل آن کنیم. برای این منظور به پوشه‌ی my-math-app\projects\ts-math-example\src\lib مراجعه کرده و تمام فایل‌های پیش‌فرض آن‌را حذف کنید. این‌ها قالب‌های ساخت کتابخانه‌های Angularای هستند که ما در اینجا کاری به آن‌ها نداریم:


همچنین می‌توان به فایل my-math-app\projects\ts-math-example\package.json نیز مراجعه کرد (فایل package.json پروژه‌ی کتابخانه) و قسمت peerDependencies آن را که به Angular اشاره می‌کند نیز حذف نمود.

سپس یک فایل خالی math.ts را به پوشه‌ی یاد شده اضافه می‌کنیم:


با این محتوا:
export function add(num1: number, num2: number) {
    return num1 + num2;
}
کتابخانه‌ی ما کار ساده‌ی جمع زدن اعداد را انجام می‌دهد.

در ادامه نیاز است این ماژول را به فایل my-math-app\projects\ts-math-example\src\public-api.ts معرفی کرد تا به عنوان API قابل دسترسی کتابخانه، در دسترس قرار گیرد:
/*
* Public API Surface of ts-math-example
*/
export * from './lib/math';
هر فایلی که قرار است توسط کتابخانه‌ی ما در معرض دید عموم قرارگیرد، باید در فایل public_api.ts عمومی شود.

در حین توسعه‌ی کتابخانه خود،‌جهت اطمینان از صحت کامپایل برنامه، دستور ng build ts-math-example --watch را در پوشه‌ی my-math-app صادر کنید. کار آن کامپایل مداوم پروژه‌ی کتابخانه بر اساس تغییرات داده شده‌است. حاصل این کامپایل نیز در پوشه‌ی my-math-app\dist\ts-math-example قرار می‌گیرد:


این همان خروجی مدرنی است که در ابتدای بحث از آن صحبت کردیم و شامل کتابخانه‌های ES5 و ES2015 به بعد و همچنین ارائه‌ی متادیتای مخصوص TypeScript نیز هست.


کامپایل و انتشار نهایی کتابخانه

پس از تکمیل کتابخانه‌ی خود، اکنون می‌توانیم آن‌را به سایت npm، برای استفاده‌ی سایرین ارسال کنیم. برای این منظور باید مراحل زیر طی شوند:
ابتدا فایل package.json واقع در ریشه‌ی پوشه‌ی ts-math-example را جهت تعریف اطلاعات این کتابخانه، تکمیل کنید. سپس دستورات زیر را در ریشه‌ی پروژه‌ی اصلی صادر کنید:
ng build ts-math-example --prod
cd dist/ts-math-example
npm publish
دستور اول کتابخانه را در حالت production تولید می‌کند که حداکثر بهینه سازی‌ها را به همراه دارد.
با دستور دوم به پوشه‌ی خروجی کتابخانه وارد شده و دستور سوم، آن‌را به سایت npm ارسال می‌کند.

استفاده کننده‌ی از کتابخانه‌ی ما (این استفاده کننده می‌تواند هر نوع پروژه‌ی جاوا اسکریپتی اعم از Angular ،React ،Vue ،ES6 ،TypeScript و غیره باشد) ابتدا با دستور npm install ts-math-example --save آن‌را نصب و به پروژه‌ی خود اضافه کرده و سپس به نحو زیر می‌تواند از آن استفاده کند:
 import { add } from '@myuser/ts-math-example';
مطالب
ساخت تم سفارشی در انگیولار متریال ۲ - بخش دوم

در قسمت قبل با نحوه ساخت تم سفارشی در انگیولار متریال ۲، آشنا شدیم. در این قسمت نحوه ساخت چند تم دیگر در کنار تم اصلی، ساخت تم به ازای هر کامپوننت و نحوه تعویض تم از طریق کد را دنبال خواهیم کرد.

ساخت تم اضافی در کنار تم اصلی

ساخت تم اضافی در انگیولار متریال ۲ بسیار ساده است. شما می‌توانید با استفاده مجدد از تابع angular-material-theme داخل یک کلاس CSS، صاحب یک تم اضافی دیگر شوید. برای نمونه در اینجا فایل my-custom-theme.scss را باز کرده و به شکل زیر تغییر می‌دهیم.

@import '~@angular/material/theming';
@include mat-core();
$my-app-primary: mat-palette($mat-teal);
$my-app-accent: mat-palette($mat-amber, 500, A100, A400);
$my-app-warn: mat-palette($mat-deep-orange);

$my-app-theme: mat-light-theme($my-app-primary, $my-app-accent, $my-app-warn);

@include angular-material-theme($my-app-theme);

/*تعریف تم اضافی در کنار تم اصلی*/
$alternate-primary: mat-palette($mat-light-blue);
$alternate-accent: mat-palette($mat-yellow, 500);
$alternate-warn: mat-palette($mat-red, 500);
$alternate-theme: mat-dark-theme($alternate-primary, $alternate-accent);
.alternate-theme {
  @include angular-material-theme($alternate-theme);
}

با این‌کار در کنار تم روشن اصلی، یک تم مشکی به صورت اضافی داخل کلاس CSS به نام alternate-theme تعریف کرده‌ایم. در این حالت تمامی کامپوننتهایی که داخل المنت با کلاس alternate-theme قرار گرفته‌اند، از تم مشکی تعریف شده استفاده خواهند کرد.

با تغییر فایل app.component.html به شکل زیر: 

<md-card>
  <md-card-header>
    <md-card-title>تم اصلی</md-card-title>
  </md-card-header>
  <button md-raised-button color="primary">
    Primary
  </button>
  <button md-raised-button color="accent">
    Accent
  </button>
  <button md-raised-button color="warn">
    Warning
  </button>
</md-card>

<div>
  <md-card>
    <md-card-header>
      <md-card-title>تم اضافی</md-card-title>
    </md-card-header>
    <md-card-content>
      <button md-raised-button color="primary">
        Primary
      </button>
      <button md-raised-button color="accent">
        Accent
      </button>
      <button md-raised-button color="warn">
        Warning
      </button>
    </md-card-content>
  </md-card>
</div>

تصویر زیر را در خروجی خواهید داشت.

 

به همین روش می‌توانید تعداد دلخواهی از تم‌ها را بسازید. همچنین می‌توانید هر تم اضافی را در یک فایل Sass تعریف کنید و از این طریق تم‌های مختلف را از هم جدا کنید. در این حالت به این نکته توجه داشته باشید که نباید mat-core@ در سرتاسر برنامه بیش از یکبار بارگذاری شده باشد. 

ساخت تم به ازای هر کامپوننت

با استفاده از mixin به نام angular-material-theme خروجی تولید شده بر روی تمامی کامپوننت‌های انگیولار متریال ۲ اعمال خواهد شد. اگر از تمامی کامپوننت‌های انگیولار متریال ۲ استفاده نمی‌کنید، می‌توانید برای کاهش حجم فایل CSS تولید شده از mixin مخصوص به هر کامپوننت استفاده کنید. همچنین برای ساخت تم‌های متفاوت به ازای هر کامپوننت نیز می‌توانید از این روش استفاده کنید. 

برای این کار تمامی مراحلی که برای ساخت تم مورد نیاز بود، باید طی شود. فقط به جای استفاده از mixin به نام angular-material-theme بایستی به طریق زیر عمل شود. 

اول: بارگذاری mixin با نام mat-core-them. این mixin تمامی استایل‌های مشترک رفتاری (مانند موج (ripple) در هنگام کلیک) برای کامپوننت‌ها را در بر دارد. این mixin خروجی تابع mat-light-theme یا mat-dark-theme را به عنوان ورودی دریافت می‌کند.

دوم: بارگذاری mixin مربوط به هر کامپوننت. برای مثال برای دکمه از mixin به نام mat-button-theme و برای checkbox از mixin به نام mat-checkbox-theme می‌توانید استفاده کنید. در زیر لیست mixin‌ها به ازای کامپوننت‌های مختلف ذکر شده است. 

mat-autocomplete-theme
mat-button-theme
mat-button-toggle-theme
mat-card-theme
mat-checkbox-theme
mat-chips-theme
mat-datepicker-theme
mat-dialog-theme
mat-grid-list-theme
mat-icon-theme
mat-input-theme
mat-list-theme
mat-menu-theme
mat-progress-bar-theme
mat-progress-spinner-theme
mat-radio-theme
mat-select-theme
mat-sidenav-theme
mat-slide-toggle-theme
mat-slider-theme
mat-tabs-theme
mat-toolbar-theme
mat-tooltip-theme

در مثال زیر می‌خواهیم تمامی کامپوننت‌ها به جز کامپوننت دکمه، تم سبز(در گروه Primary) و دکمه‌ها نیز تم آبی داشته باشند. کافی است کدهای زیر را در فایل Sass خود وارد کنید. 

@import '~@angular/material/theming';
@include mat-core();
$my-app-primary: mat-palette($mat-teal);
$my-app-accent: mat-palette($mat-amber, 500, A100, A400);
$my-app-warn: mat-palette($mat-deep-orange);
$my-app-theme: mat-light-theme($my-app-primary, $my-app-accent, $my-app-warn);
@include mat-material-theme($my-app-theme);

/* تعریف تم برای دکمه */
$button-primary: mat-palette($mat-light-blue);
$button-accent: mat-palette($mat-yellow, 500);
$button-warn: mat-palette($mat-red, 500);
$button-theme: mat-light-theme($button-primary, $button-accent);
@include mat-button-theme($button-theme);

با توجه به اینکه mat-material-theme در داخل خود mat-button-theme را بارگذاری می‌کند دو نتیجه زیر را می‌توان گرفت.

اول: اگر mat-material-theme بعد از هر کدام از mixinهای مربوط به کامپوننت‌ها نوشته شود، تمامی Cssهای تولید شده به ازای کامپوننت را دوباره نویسی کرده و عملا هیچ کدام کارایی نخواهند داشت. برای مثال کافی است فایل Sass خود را به شکل زیر تغییر دهید. در این صورت تم مربوط به دکمه کاریی نخواهد داشت. 

@import '~@angular/material/theming';
@include mat-core();

/* تعریف تم برای دکمه */
$button-primary: mat-palette($mat-light-blue);
$button-accent: mat-palette($mat-yellow, 500);
$button-warn: mat-palette($mat-red, 500);
$button-theme: mat-ligth-theme($button-primary, $button-accent);
@include mat-button-theme($button-theme);

$my-app-primary: mat-palette($mat-teal);
$my-app-accent: mat-palette($mat-amber, 500, A100, A400);
$my-app-warn: mat-palette($mat-deep-orange);
$my-app-theme: mat-light-theme($my-app-primary, $my-app-accent, $my-app-warn);
@include mat-material-theme($my-app-theme);

دوم: همانطور که گفتیم mat-button-theme در mat-material-theme قبلا بارگذاری شده است. با بارگذاری دوباره توسط mat-button-theme کدهای CSS که قبلا برای دکمه تولید شده‌اند را از نو دوباره می‌نویسد و این باعث بزرگ شدن حجم فایل Css تولید شده خواهد شد. پس بهتر است هنگام استفاده از mixinهای مختص کامپوننت‌ها از mat-material-theme استفاده نکنیم. 

  جهت بهبود کدهای قبلی بهتر است از کدهای زیر استفاده کنیم. 
@import '~@angular/material/theming';
@include mat-core();
$my-app-primary: mat-palette($mat-teal);
$my-app-accent: mat-palette($mat-amber, 500, A100, A400);
$my-app-warn: mat-palette($mat-deep-orange);
$my-app-theme: mat-light-theme($my-app-primary, $my-app-accent, $my-app-warn);

$button-primary: mat-palette($mat-light-blue);
$button-accent: mat-palette($mat-yellow, 500);
$button-warn: mat-palette($mat-red, 500);
$button-theme: mat-light-theme($button-primary, $button-accent);

@include mat-core-theme($my-app-theme);
@include mat-autocomplete-theme($my-app-theme);

@include mat-button-theme($button-theme);

@include mat-button-toggle-theme($my-app-theme);
@include mat-card-theme($my-app-theme);
@include mat-checkbox-theme($my-app-theme);
@include mat-chips-theme($my-app-theme);
@include mat-datepicker-theme($my-app-theme);
@include mat-dialog-theme($my-app-theme);
@include mat-grid-list-theme($my-app-theme);
@include mat-icon-theme($my-app-theme);
@include mat-input-theme($my-app-theme);
@include mat-list-theme($my-app-theme);
@include mat-menu-theme($my-app-theme);
@include mat-progress-bar-theme($my-app-theme);
@include mat-progress-spinner-theme($my-app-theme);
@include mat-radio-theme($my-app-theme);
@include mat-select-theme($my-app-theme);
@include mat-sidenav-theme($my-app-theme);
@include mat-slide-toggle-theme($my-app-theme);
@include mat-slider-theme($my-app-theme);
@include mat-tabs-theme($my-app-theme);
@include mat-toolbar-theme($my-app-theme);
@include mat-tooltip-theme($my-app-theme);

تعویض تم از طریق کد

فرض کنید یک تم پیش فرض و یک تم اضافی به نام alternate-theme دارید. برای تعویض تم از طریق کد کافی است کلاس المنت پدر در صفحه html خود را از طریق [ngClass] با نام تم، مقدار دهی کنید. کدهای داخل app.component.ts را به شکل زیر تغییر می‌دهیم. 

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  themes = [
    {value: 'alternate-theme', text: 'تم مشکی'},
    {value: '', text: 'تم سفید'},
  ];
  activeTheme = '';
}

آرایه‌ای جهت نمایش در کامپوننت md-select با دو مقدار تم پیش فرض و تم با نام 'alternate-theme تعریف می‌کنیم. همچنین متغیری با نام activeTheme را تعریف می‌کنیم. این متغیر در هر لحظه نام تم اعمال شده را در خود نگهداری می‌کند. مقدار اولیه این متغیر تم اصلی است.

کامپوننت md-select را به شکل زیر به فایل app.component.html به تگ main اضافه می‌کنیم. 

<md-select dir="rtl" [(ngModel)]="activeTheme" placeholder="تعویض تم">
    <md-option *ngFor="let theme of themes" [value]="theme.value">
            {{ theme.text }}
    </md-option>
</md-select>
حالا کافی است کل تگهای موجود در app.component.html را داخل یک تگ div به شکل زیر قرار دهیم. 
<div [ngClass]="activeTheme">
بعد از اجرای برنامه می‌توانید با تعویض تم از طریق کامپوننت md-select، تم صفحه را تعویض کنید. 

کدهای این قسمت را، از اینجا دریافت کنید: ساخت تم سفارشی در انگولار متریال ۲ - بخش دوم
نظرات مطالب
مایکرو سرویس‌ها - قسمت 1 - معرفی
سلام.
مایکرو سرویس یکی از مشتقات SOA می‌باشد. اما تفاوت هایی هم تو این دیدگاه نسبت به SOA وجود دارد
  1. اندازه سرویسها در این دو متفاوت می‌باشد در مایکرو سرویسها اندازه یک سرویسها کوچکتر از اندازه سرویسها در SOA می‌باشد
  2. سرویها در مایکرو سرویس می‌توانند کاملا مستقل از هم قابل استقرار باشندو مکانیزم استقار بصورت خودکار می‌باشد درحالی که در SOA  این امکان وجود ندارد
  3. در SOA  برای بررسی قوانین کسب و کار  و ادغام سیستم‌های یکپارچه  تمرکزبر روی ESB  می باشد ، در حالی که این امر در مایکرو سرویس‌ها یک anti pattern  به شمار می‌آید و دیدگاه  آنها عدم انتقال قوانین کسب کار به مکانیزم ارتباطی می‌باشد (smart endpoints and dumb pipe)
در مورد معایب این روش در آینده صحبت خواهیم کرد اما میتوان گفت که پیاده سازی این معماری هزینه زیادی دارد هم از لحاظ زمانی و هم از لحاظ پیچیدگی در پیاده سازی.
مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت ششم - کامپوننت‌های تو در تو
گاهی از اوقات جهت refactoring یک template بزرگ، بهتر است آن‌را به چند template کوچک خرد کرد و سپس از جمع آن‌ها به صورت یک template اصلی استفاده نمود. در این حالت نیاز است بین این زیر کامپوننت‌ها و کامپوننت‌های دربرگیرنده‌ی آن‌ها ارتباطات لازم را برقرار کرد.
تا اینجا در قسمت سوم، نحوه‌ی قراردادن یک کامپوننت را در کامپوننتی دیگر، توسط مقدار دهی خاصیت directives مزین کننده‌ی Component بررسی کردیم. همینقدر که یک کامپوننت دارای selector باشد، قابلیت قرارگرفتن در یک کامپوننت دیگر را دارد. اما چگونه باید بین این کامپوننت‌ها ارتباط برقرار کرد؟


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

مثال نمایش لیست محصولات سری جاری، دارای ستون «5Star Rating» است. در این قسمت می‌خواهیم بجای نمایش عددی این امتیازها، کامپوننتی را طراحی کنیم که نماش ستاره‌ای آن‌ها را سبب شود. این کامپوننت باید بتواند یک مقدار ورودی، یا همان عدد امتیاز محصول را از کامپوننت دربرگیرنده‌ی آن دریافت کند. همچنین می‌خواهیم اگر کاربر بر روی این ستاره‌ها کلیک کرد، کامپوننت در برگیرنده را نیز مطلع سازیم.
در این مثال در فایل product-list.component.html چنین سطری تعریف شده‌است:
 <td>{{ product.starRating }}</td>
البته می‌توان در همینجا کدهای نمایش ستاره‌ای را بجای درج مقدار عددی آن قرار داد، اما ... قالب جاری را بیش از اندازه شلوغ خواهد کرد. به همین دلیل بهتر است نمایش آن‌را تبدیل به یک کامپوننت مجزا کرد. به علاوه در این حالت، قابلیت استفاده‌ی مجدد از آن در سایر کامپوننت‌ها نیز وجود خواهد داشت.
با توجه به اینکه کامپوننت نمایش ستاره‌ای امتیازها، قابلیت استفاده‌ی مجدد را دارد و الزامی ندارد که حتما در لیست محصولات، بکار گرفته شود، بهتر است محل تعریف آن‌را به خارج از پوشه‌ی products فعلی منتقل کنیم. برای مثال می‌توان پوشه‌ی app\shared را برای آن و تمامی کامپوننت‌های با قابلیت استفاده‌ی مجدد ایجاد کرد.


برای شروع، فایل جدید App\shared\star.component.ts را اضافه کنید؛ با کدهای کامل ذیل:
import { Component, OnChanges, Input, Output, EventEmitter } from 'angular2/core';
 
@Component({
    selector: 'ai-star',
    templateUrl: 'app/shared/star.component.html',
    styleUrls: ['app/shared/star.component.css']
})
export class StarComponent implements OnChanges {
    @Input() rating: number;
    starWidth: number;
    @Output() ratingClicked: EventEmitter<string> = new EventEmitter<string>();
 
    ngOnChanges(): void {
        this.starWidth = this.rating * 86 / 5;
    }
 
    onClick() {
        this.ratingClicked.emit(`The rating ${this.rating} was clicked!`);
    }
}
روش ساخت این کامپوننت نیز همانند سایر کامپوننت‌ها است و اصول کلی آن تفاوتی نمی‌کند. در اینجا نیز کلاسی وجود دارد که export شده و همچنین به Component مزین است. مقدار selector آن نیز به ai-star تنظیم شده‌است.
سپس مسیر template و مسیر فایل css ویژه‌ی آن، در تزئین کننده‌ی Component مشخص شده‌اند. محتوای کامل این دو فایل را در ذیل مشاهده می‌کنید:
الف) محتوای فایل App\shared\star.component.html
<div class="crop"
     [style.width.px]="starWidth"
     [title]="rating"
     (click)='onClick()'>
    <div style="width: 86px">
        <span class="glyphicon glyphicon-star"></span>
        <span class="glyphicon glyphicon-star"></span>
        <span class="glyphicon glyphicon-star"></span>
        <span class="glyphicon glyphicon-star"></span>
        <span class="glyphicon glyphicon-star"></span>
    </div>
</div>
ب) محتوای فایل App\shared\star.component.css
.crop {
    overflow: hidden;
}
 
div {
    cursor: pointer;
}
قالب star.component.html کار نمایش پنج ستاره را انجام می‌دهد. عرض کلی آن بر اساس مقدار خاصیت starWidth مشخص می‌شود و بر همین اساس، تعداد نمایان ستاره‌ها، مشخص خواهند شد. خاصیت starWidth به width این div بر حسب px، متصل شده‌است (property binding). همچنین خاصیت title این div نیز به مقدار rating متصل شده‌است و اگر بر روی آن کلیک شود، متد onClick را در کلاس متناظر با کامپوننت خود، فراخوانی خواهد کرد (event binding).

معرفی مقدماتی life cycle hooks در قسمت قبل صورت گرفت. در اینجا چون نیاز است به ازای هر بار رندر شدن این کامپوننت، عرض آن متفاوت باشد، بنابراین نیاز است راهی را پیدا کنیم تا بتوان مقدار خاصیت starWidth را متغیر کرد. به همین منظور از hook مخصوص این تغییرات یا همان OnChanges استفاده می‌شود. بنابراین باید کلاس این کامپوننت، اینترفیس OnChanges را پیاده سازی کند. پس از آن، importهای لازم جهت تعریف OnChanges به ابتدای فایل اضافه شده و همچنین متد ngOnChanges نیز جهت تکمیل کار پیاده سازی اینترفیس OnChanges، به کلاس جاری اضافه می‌شود.
کار متد ngOnChanges، تبدیل عدد امتیاز یک محصول، به عرض div نمایش ستاره‌ها است.


مکانیزم کار رخداد ngOnChanges و دریافت اطلاعات از والد

متد ngOnChanges، تنها به خواص ویژه‌ای به نام «input properties» واکنش نشان می‌دهد. اگر یک کامپوننت تو در توی قرار گرفته‌ی در یک کامپوننت دیگر، بخواهد اطلاعاتی را از والد خود دریافت کند، باید خاصیتی را در معرض دید آن دربرگیرنده قرار دهد. این کار توسط decorator ویژه‌ای به نام ()Input@ انجام می‌شود.
به همین جهت است که پیش از خاصیت rating در کلاس  StarComponent، شاهد درج مزین کننده‌ی ویژه‌ی ()Input@ هستیم:
export class StarComponent implements OnChanges {
      @Input() rating: number;
مزین کننده‌ی ()Input@ را به هر خاصیتی با هر نوعی، می‌توان انتساب داد. در اینجا نیاز است کامپوننت نمایش ستاره‌ای امتیازها، عدد امتیاز را از والد خود دریافت کنند. به همین جهت خاصیت امتیاز را از نوع «خاصیت ورودی» مشخص کرده‌ایم.
پس از آن، کامپوننت دربرگیرنده یا والد، این خاصیت ورودی ویژه را از طریق روش property binding متداول، مقدار دهی می‌کند:
 [rating]='product.starRating'

بدیهی است در اینجا چون خاصیت starWidth از نوع ورودی تعریف نشده‌است، قابلیت property binging فوق را در کامپوننت والد، ندارد.

اکنون به ازای هر بار نمایش این کامپوننت فرزند، خاصیت rating ورودی آن مقدار دهی شده و مقدار آن در رخداد ngOnChanges قابل دسترسی و استفاده خواهد بود. اینجا است که می‌توان از این مقدار تغییر یافته، جهت ترجمه‌ی آن به عرض div نمایش ستاره‌ها، استفاده کرد.


ارسال داده‌ها از کامپوننت فرزند به کامپوننت والد

تا اینجا با استفاده از «خواص ورودی» امکان دسترسی به مقادیر ارسالی از طرف والد را در کامپوننت فرزند، پیدا کردیم. عکس آن نیز امکان پذیر است؛ اما توسط رخدادها.
کامپوننت فرزند، با استفاده از decorator ویژه‌ی دیگری به نام ()Output@ امکان ارسال رخدادها را به کامپوننت والد پیدا می‌کند:
export class StarComponent implements OnChanges {
    @Input() rating: number;
    starWidth: number;
    @Output() ratingClicked: EventEmitter<string> = new EventEmitter<string>();
در اینجا خاصیت ratingClicked را که با ()Output@ مزین شده‌است، مشاهده می‌کنید. نوع این خاصیت باید از نوع رخدادها باشد که در AngularJS 2.0 توسط شیء EventEmitter جنریک، تعریف شده‌است. در این مثال، نوع رخداد و مقداری که توسط آن ارسال می‌شود، رشته‌ای درنظر گرفته شده‌است. اگر نیاز به ارسال چندین مقدار بود، می‌توان یک شیء را در اینجا مشخص کرد.
در مثال جاری اگر کاربر بر روی div ستاره‌های نمایش داده شده کلیک کند، اتصال به آن از طریق event binging متداول انجام می‌شود (متد جدید onClick به رخداد click متصل شده‌است):
<div class="crop"
     [style.width.px]="starWidth"
     [title]="rating"
     (click)='onClick()'>
و سپس این رخداد کلیک، در کلاس StarComponent به نحو ذیل به والد خود منتقل خواهد شد:
onClick() {
     this.ratingClicked.emit(`The rating ${this.rating} was clicked!`);
 }
در اینجا متد emit است که اطلاعات را به کامپوننت دربرگیرنده، ارسال می‌کند. نحوه‌ی تعریف این رشته هم توسط back tick مربوط به ES 6 صورت گرفته‌است.

تا اینجا مرحله‌ی تنظیمات رخدادها در کامپوننت فرزند صورت گرفت. ابتدا خاصیتی از نوع Output تعریف شد. سپس در کدهای قالب این کامپوننت جدید، متد onClick به رخداد click متصل گردید و سپس در کدهای مدیریت کننده‌ی این متد، متد ratingClicked.emit جهت ارسال اطلاعات نهایی به والد، فراخوانی گردید.

اکنون در کامپوننت والد، باید این مراحل برای دریافت اطلاعات از کامپوننت فرزند خود، طی شوند:
الف) ابتدا نام خاصیت مزین شده‌ی با Output، به عنوان مقصد event binding مشخص می‌شود و سپس متدی در کلاس کامپوننت والد، به آن متصل می‌گردد:
 (ratingClicked)='onRatingClicked($event)'
event$ مقدار ارسالی از کامپوننت فرزند را به کامپوننت والد منتقل می‌کند.
ب) در ادامه، تعریف این متد جدید متصل شده را به کلاس ProductListComponent اضافه می‌کنیم:
onRatingClicked(message: string): void {
    this.pageTitle = 'Product List: ' + message;
}
نوع پارامتر ورودی این متد را با توجه به نوع رشته‌ای <EventEmitter<string در کلاس StarComponent  تعریف کردیم.
به این ترتیب با کلیک بر روی div هر کامپوننت نمایش ستاره‌ای امتیازها، خاصیت pageTitle درج شده‌ی در صفحه تغییر می‌کند.


استفاده از کامپوننت نمایش ستاره‌ای امتیازها

نکات کلی افزودن این کامپوننت جدید، تفاوتی با مطالب عنوان شده‌ی در قسمت سوم، در حین بررسی مراحل افزودن دایرکتیو نمایش لیست محصولات، به کامپوننت ریشه‌ی سایت ندارد و یکی هستند.
برای افزودن و استفاده از این کامپوننت جدید، ابتدا قالب product-list.component.html را گشوده و سپس سطر نمایش عددی امتیاز یک محصول را به نحو ذیل تغییر می‌دهیم:
<td>
    <ai-star [rating]='product.starRating'
             (ratingClicked)='onRatingClicked($event)'>
    </ai-star>
</td>
همانطور که ملاحظه می‌کنید، ابتدا selector این کامپوننت جدید، به صورت یک المان جدید و سفارشی HTML، در قالب کامپوننت لیست محصولات درج می‌شود. همچنین خاصیت rating ورودی آن به عدد امتیاز محصول جاری در حال رندر، متصل شده‌است. به علاوه توسط event binding به خاصیت ratingClicked که از نوع ()Output@ تعریف شده‌است، متد onRatingClicked متصل گردیده‌است.

سپس باید به کلاس کامپوننت لیست محصولات (کامپوننت در برگیرنده) اعلام کرد که این کامپوننت جدید را باید از کجا پیدا کند. برای این منظور فایل product-list.component.ts را گشوده و خاصیت directives این کامپوننت را مقدار دهی می‌کنیم:
import { Component, OnInit } from 'angular2/core';
import { IProduct } from './product';
import { ProductFilterPipe } from './product-filter.pipe';
import { StarComponent } from '../shared/star.component';
 
@Component({
    selector: 'pm-products',
    templateUrl: 'app/products/product-list.component.html',
    styleUrls: ['app/products/product-list.component.css'],
    pipes: [ProductFilterPipe],
    directives: [StarComponent]
})
در اینجا دو کار جدید انجام شده‌است. ابتدا خاصیت directives، به نام کلاس این کامپوننت جدید (StarComponent)، تنظیم شده‌است. سپس تعریف import ماژول این کامپوننت، به ابتدای فایل جاری اضافه شده‌است.

نمونه‌ای از اجرای برنامه را در تصویر ذیل مشاهده می‌کنید:

در اینجا ستون امتیازهای محصولات با کامپوننت نمایش ستاره‌ای این امتیازها جایگزین شده‌است و همچنین با کلیک بر روی یکی از آن‌ها، عنوان panel جاری تغییر کرده‌است.


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



خلاصه‌ی بحث

در اینجا نحوه‌ی طراحی API عمومی یک کامپوننت را بررسی کردیم. تا زمانیکه خواص کلاس یک کامپوننت به نحو متداولی تعریف می‌شوند، میدان دید آن‌ها محدود است به قالب تعریف شده‌ی متناظر با آن‌ها. اگر نیاز است خاصیتی خارج از این قالب و به صورت عمومی در کامپوننت دربرگیرنده‌ی دیگری در دسترس قرار گیرد، آن‌را با مزین کننده‌ی ()Input@ مشخص می‌کنیم و اگر قرار است این کامپوننت فرزند، اطلاعاتی را به کامپوننت والد ارسال کند، این‌کار را توسط رخدادها و با تعریف ویژگی ()Output@ و EventEmitter انجام می‌دهد. نوع آرگومان جنریک EventEmitter، تعیین کننده‌ی نوع اطلاعاتی است که قرار است به کامپوننت دربرگیرنده ارسال شوند.
پس از تعریف کامپوننت فرزند، برای تعریف آن در کامپوننت والد، از نام selector آن به عنوان یک المان جدید HTML استفاده می‌شود و سپس با استفاده از property binding، اطلاعات لازم، به خاصیت از نوع ()Input@ کامپوننت فرزند ارسال می‌گردد. از event binding برای دریافت رخدادها از کامپوننت فرزند استفاده می‌شود. در اینجا هر رخدادی که توسط مزین کننده‌ی ()Output@ تعریف شده باشد، می‌تواند به عنوان مقصد event binding تعریف شود و اگر نیاز است به رخدادهای property binding از والد به فرزند، گوش فرا داد، می‌توان اینترفیس OnChanges را در کلاس کامپوننت فرزند پیاده سازی کرد.
نظرات مطالب
پیاده سازی JSON Web Token با ASP.NET Web API 2.x
با سلام؛ کد رو به این شکل تغییر  دادم این دفعه خطای {"error":"unsupported_grant_type"}   میگیرم. درخواست به متد ValidateClientAuthentication در سمت سرور می‌رسه. بعد از بازگشت همچین خطایی نشون میده.
 private refreshToken(isAuthUserLoggedIn: boolean) {
      
        const headers = new HttpHeaders({ "Content-Type": "application/x-www-form-urlencoded" });
        const model = { refreshToken: this.tokenStoreService.getRawAuthToken(AuthTokenType.RefreshToken) };
        body = new HttpParams();
        body.set('grant_type', 'refresh_token');
        body.set('refresh_token', model.refreshToken);

        return this.http    
(`${this.appConfig.basePath}${this.apiConfigService.configuration.loginPath}`,
            body.toString(), { headers: headers })
            .pipe(
            map(response => response || {}),
            catchError((error: HttpErrorResponse) => ErrorObservable.create(error)),
            finalize(() => {
                this.scheduleRefreshToken(isAuthUserLoggedIn);
            })
            )
            .subscribe(result => {
                console.log("RefreshToken Result", result);
                this.tokenStoreService.storeLoginSession(result);
            });
    }

با تشکر
اشتراک‌ها
.Net Core 5.0 Preview 1.0 منتشر شد

The following improvements are in Preview 1:

  • Regular expression performance improvements (3-6x Faster)
  • Code quality improvements in RyuJIT
  • Assembly load diagnostics added to event pipe
  • Event pipe profiler APIs
  • .... 
.Net Core 5.0 Preview 1.0 منتشر شد
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت دوازدهم - توزیع برنامه
سلام؛ بعد از توزیع تمپلت‌های برنامه با گالپ تمامی دایرکتیوهای انگولارجی اس به حروف کوچک تبدیل می‌شوند.برای مثال ngFor* به ngfor* تبدیل میشه. وقتی مرحله مینی فای کردن تمپلت‌ها رو غیرفعال کردم مشکل حل شد!
gulp.task("templates", function () {
    return gulp.src(appFolder + "/**/*.html")
        //.pipe(minifyHTML())
        .pipe(gulp.dest(outFolder + "/app"));
});
مطالب
رشته ها و پردازش متن در دات نت به زبان ساده
رشته، مجموعه‌ای از کاراکترهاست که پشت سرهم، در مکانی از حافظه قرار گرفته‌اند. هر کاراکتر حاوی یک شماره سریال در جدول یونیکد هست. به طور پیش فرض دات نت برای هر کاراکتر (نوع داده char) شانزده بیت در نظر گرفته است که برای 65536 کاراکتر کافی است.
برای نگهداری از رشته‌ها و انجام عملیات بر روی آنها در دات نت از نوع system.string استفاده می‌کنیم:
string greeting = "Hello, C#";

که در این حالت مجموعه‌ای از کاراکترها را ایجاد خواهد کرد:

اتفاقاتی که در داخل کلاس string رخ می‌دهد بسیار ساده است و ما را از تعریف []char بی‌نیاز می‌کند تا مجبور نشویم خانه‌های  آرایه را به ترتیب پر کنیم. از معایب استفاده از آرایه char میتوان موارد زیر را برشمارد:
  1. خانه‌های آن یک ضرب پر نمیشوند بلکه به ترتیب، خانه به خانه پر می‌شوند.
  2. قبل از انتساب متن باید باید از طول متن مطمئن شویم تا بتوانیم تعداد خانه‌ها را بر اساس آن ایجاد کنیم.
  3. همه عملیات آرایه‌ها از پر کردن ابتدای کار گرفته تا هر عملی، نیاز است به صورت دستی صورت بگیرد و تعداد خطوط کد برای هر کاری هم بالا می‌رود.
البته استفاده از string هم راه حل نهایی برای کار با متون نیست. در انتهای این مطلب مورد دیگری را نیز بررسی خواهیم کرد. از ویژگی دیگر رشته‌ها این است که آن‌ها شباهت زیادی به آرایه‌ای از کاراکتر‌ها دارند؛ ولی اصلا شبیه آن‌ها نیستند و نمی‌توانید به صورت یک آرایه آن‌ها را مقداردهی کنید. البته کلاس string امکاناتی را با استفاده از indexer [] مهیا کرده است که میتوانید بر اساس اندیس‌ها به کاراکترها به صورت جداگانه دسترسی داشته باشید ولی نمی‌توانید آن‌ها را مقدار دهی کنید. این اندیس‌ها از 0 تا طول آن length-1 ادامه دارند.
string str = "abcde";
char ch = str[1]; // ch == 'b'
str[1] = 'a'; // Compilation error!
ch = str[50]; // IndexOutOfRangeException
همانطور که میدانیم برای مقداردهی رشته‌ها از علامت‌های نقل قول "" استفاده میکنیم که باعث میشود اگر بخواهیم علامت " را در رشته‌ها داشته باشیم نتوانیم. برای حل این مشکل از علامت \ استفاده میکنیم که البته باعث استفاده از بعضی کاراکترهای خاص دیگر هم می‌شود:
string a="Hello \"C#\"";
string b="Hello \r\n C#"; //مساوی با اینتر
string c="C:\\a.jpg"; //چاپ خود علامت  \ -مسیردهی
البته اگر از علامت @ در قبل از رشته استفاده شود علامت \ بی اثر خواهد شد.
string c=@"C:\a.jpg";// == "C:\\a.jpg"

مقداردهی رشته‌ها و پایدار (تغییر ناپذیر) بودن آنها Immutable
رشته‌ها ساختاری پایدار هستند؛ به این معنی که به صورت reference مقداردهی می‌شوند. موقعی که شما مقداری را به یک رشته انتساب می‌دهید، مقدار متغیر در  String pool یا لینک در Heap ذخیره می‌شوند و اگر همین متغیر را به یک متغیر دیگر انتساب دهیم، متغیر جدید مقدار آن را دیگر در حافظه پویا (داینامیک) Heap به عنوان مقدار جدید ذخیره نخواهد کرد؛ بلکه تنها یک pointer خواهد بود که به آدرس حافظه متغیر اولی اشاره می‌کند. به مثال زیر دقت کنید. متغیر source مقدار some source را ذخیره می‌کند و بعد همین متغیر، به متغیر assigned انتساب داده میشود؛ ولی مقداری جابجا نمی‌شود. بلکه متغیر assign به آدرسی در حافظه اشاره می‌کند که متغیر source اشاره می‌کند. هرگاه که در یکی از متغیرها، تغییری رخ دهد، همان متغیری که تغییر کرده است، به آدرس جدید با محتوای تغییر داده شده اشاره می‌کند.
string source = "Some source";
string assigned = source;

این ویژگی نوع reference فقط برای ساختارهای Immutable به معنی پایدار رخ می‌دهد و نه برای ساختار‌های ناپایدار (تغییر پذیر)  mutable؛ به این خاطر که آن‌ها مقادیرشان را مستقیما تغییر میدهند و اشاره‌ای در حافظه صورت نمی‌گیرد. 
string hel = "Hel";
string hello = "Hello";
string copy = hel + "lo";

string hello = "Hello";
string same = "Hello";

برای اطلاعات بیشتر در این زمینه این لینک را مطالعه نمایید.


مقایسه رشته‌ها
برای مقایسه دو رشته میتوان از علامت == یا از متد Equals استفاده نماییم. در این حالت به خاطر اینکه کد حروف کوچک و بزرگ متفاوت است، مقایسه حروف هم متفاوت خواهد بود. برای اینکه حروف کوچک و بزرگ تاثیری بر مقایسه ما نگذارند و #c را با #C برابر بدانند باید از متد Equals به شکل زیر استفاده کنیم:
Console.WriteLine(word1.Equals(word2,
    StringComparison.CurrentCultureIgnoreCase));
برای اینکه بزرگی و کوچکی اعداد را مشخص کنیم از علامت‌های < و > استفاده میکنیم ولی برای رشته‌ها از متد CompareTo بهره می‌بریم که چینش قرارگیری آن‌ها را بر اساس حروف الفبا مقایسه می‌کند و سه عدد، می‌تواند خروجی آن باشند. اگر 0 باشد یعنی برابر هستند، اگر -1 باشد رشته اولی قبل از رشته دومی است و اگر 1 باشد رشته دومی قبل از رشته اولی است.
string score = "sCore";
string scary = "scary";
 
Console.WriteLine(score.CompareTo(scary));
Console.WriteLine(scary.CompareTo(score));
Console.WriteLine(scary.CompareTo(scary));
 
// Console output:
// 1
// -1
// 0
 اینبار هم برای اینکه حروف کوچک و بزرگ، دخالتی در کار نداشته باشند، میتوانید از داده شمارشی StringComparison در متد ایستای (string.Compare(s1,s2,StringComparison استفاده نمایید؛ یا از نوع داده‌ای boolean برای تعیین نوع مقایسه استفاده کنید.
string alpha = "alpha";
string score1 = "sCorE";
string score2 = "score";
 
Console.WriteLine(string.Compare(alpha, score1, false));
Console.WriteLine(string.Compare(score1, score2, false));
Console.WriteLine(string.Compare(score1, score2, true));
Console.WriteLine(string.Compare(score1, score2,
    StringComparison.CurrentCultureIgnoreCase));
// Console output:
// -1
// 1
// 0
// 0
نکته : برای مقایسه برابری  دو رشته از متد Equals یا == استفاده کنید و فقط برای تعیین کوچک یا بزرگ بودن از compare‌ها استفاده نمایید. دلیل آن هم این است که برای مقایسه از فرهنگ culture فعلی سیستم استفاده میشود و نظم جدول یونیکد را رعایت نمی‌کنند و ممکن است بعضی رشته‌های نابرابر با یکدیگر برابر باشند. برای مثال در زبان آلمانی دو رشته "SS" و "ß " با یکدیگر برابر هستند.

عبارات با قاعده Regular Expression
این عبارات الگوهایی هستند که قرار است عبارات مشابه الگویی را در رشته‌ها پیدا کنند. برای مثال الگوی +[A-Z0-9] مشخص می‌کند که رشته مورد نظر نباید خالی باشد و حداقل با یکی از حروف بزرگ یا اعداد پرشده باشد. این الگوها میتوانند برای واکشی داده‌ها یا قالب‌های خاص در رشته‌ها به کار بروند. برای مثال شماره تماس‌ها ، پست الکترونیکی و ...
در اینجا میتواند نحوه‌ی الگوسازی را بیاموزید. کد زیر بر اساس یک الگو، شماره تماس‌های مورد نظر را یافته و البته با فیلتر گذاری آن‌ها را نمایش می‌دهد:
string doc = "Smith's number: 0898880022\nFranky can be " +
    "found at 0888445566.\nSteven's mobile number: 0887654321";
string replacedDoc = Regex.Replace(
    doc, "(08)[0-9]{8}", "$1********");
Console.WriteLine(replacedDoc);
// Console output:
// Smith's number: 08********
// Franky can be found at 08********.
// Steven' mobile number: 08********
سه شماره تماس در رشته‌ی بالا با الگوی ما همخوانی دارند که بعد با استفاده از متد replace در شی Regex عبارات دلخواه خودمان را جایگزین شماره تماس‌ها خواهیم کرد. الگوی بالا شماره تماس‌هایی را میابد که با 08 آغاز شده‌اند و بعد از آن 8 عدد دیگر از 0 تا 9 قرار گرفته‌اند. بعد از اینکه متن مطابق الگو یافت شد، ما آن را با الگوی ********1$ جایگزین می‌کنیم که علامت $ یک placeholder برای یک گروه است. هر عبارت () در عبارات با قاعده یک گروه حساب میشود و اولین پرانتر 1$ و دومین پرانتز یا گروه میشود 2$ که در عبارت بالا (08) میشود 1$ و به جای مابقی الگو، 8 علامت ستاره نمایش داده میشود.

اتصال رشته‌ها در Loop
برای اتصال رشته‌ها ما از علامت + یا متد ایستای string.concat استفاده می‌کنیم ولی استفاده‌ی از آن در داخل یک حلقه باعث کاهش کارآیی برنامه خواهد شد. برای همین بیایید ببینم در حین اتتقال رشته‌ها در حافظه چه اتفاقی رخ میدهد. ما در اینجا دو رشته str1 و str2 داریم که عبارات "super" و "star" را نگه داری می‌کنند و در واقع دو متغیر هستند که به حافظه‌ی پویای Heap اشاره می‌کنند. اگر این دو را با هم جمع کنیم و نتیجه را در متغیر result قرار دهیم، سه متغیر میشوند که هر کدام به حافظه‌ای جداگانه در heap اشاره می‌کنند. در واقع برای این اتصال، قسمت جدیدی از حافظه تخصصیص داده شده و مقدار جدید در آن نشسته‌است. در این حالت یک متغیر جدید ساخته شد که به آدرس آن اشاره می‌کند. کل این فرآیند یک فرآیند کاملا زمانبر است که با تکرار این عمل موجب از دست دادن کارآیی برنامه می‌شود؛ به خصوص اگر در یک حلقه این کار صورت بگیرد.
سیستم دات نت همانطور که میدانید شامل GC یا سیستم خودکار پاکسازی حافظه است که برنامه نویس را از dispose کردن بسیاری از اشیاء بی نیاز می‌کند. موقعی‌که متغیری به قسمتی از حافظه اشاره می‌کند که دیگر بلا استفاده است، سیستم GC به صورت خودکار آنها را پاکسازی می‌کند که این عمل زمان بر هم خودش موجب کاهش کارآیی می‌شود. همچنین انتقال رشته‌ها از یک مکان حافظه به مکانی دیگر، باز خودش یک فرآیند زمانبر است؛ به خصوص اگر رشته مورد نظر طولانی هم باشد.
مثال عملی: در تکه کد زیر قصد داریم اعداد 1 تا 20000 را در یک رشته الحاق کنیم:
 DateTime dt = DateTime.Now;
            string s = "";
        for (int index = 1; index <= 20000; index++)
        {
            s += index.ToString();
        }
            Console.WriteLine(s);
            Console.WriteLine(dt);
            Console.WriteLine(DateTime.Now);
            Console.ReadKey();
کد بالا تاز زمان نمایش کامل، بسته به قدرت سیستم ممکن است یکی دو ثانیه طول بکشد. حالا عدد را به 200000 تغییر دهید (یک صفر اضافه تر). برنامه را اجرا کنید و مجددا تست بزنید. در این حالت چند دقیقه ای بسته به قدرت سیستم زمان خواهد برد؛ مثلا دو دقیقه یا سه دقیقه یا کمتر و بیشتر.
عملیاتی که در حافظه صورت میگیرد این چند گام را طی میکند:
  • قسمتی از حافظه به طور موقت برای این دور جدید حلقه، گرفته میشود که به آن بافر میگوییم.
  • رشته قبلی به بافر انتقال میابد که بسته به مقدار آن زمان بر و کند است؛ 5 کیلو یا 5 مگابایت یا 50 مگابایت و ...
  • شماره تولید شده جدید به بافر چسبانده میشود.
  • بافر به یک رشته تبدیل میشود وجایی برای خود در حافظه Heap میگیرد.
  • حافظه رشته قدیمی و بافر دیگر بلا استفاده شده‌اند و توسط GC پاکسازی میشوند که ممکن است عملیاتی زمان بر باشد.

String Builder
این کلاس ناپایدار و تغییر پذیر است. به کد و شکل زیر دقت کنید:
string declared = "Intern pool";
string built = new StringBuilder("Intern pool").ToString();

این کلاس دیگر مشکل الحاق رشته‌ها یا دیگر عملیات پردازشی را ندارد. بیایید مثال قبل را برای این کلاس هم بررسی نماییم:
 StringBuilder sb = new StringBuilder();
      sb.Append("Numbers: ");

            DateTime dt = DateTime.Now;
        for (int index = 1; index <= 200000; index++)
        {
            sb.Append(index);
        }
            Console.WriteLine(sb.ToString());
            Console.WriteLine(dt);
            Console.WriteLine(DateTime.Now);
            Console.ReadKey();
اکنون همین عملیات چند دقیقه‌ای قبل، در زمانی کمتر، مثلا دو ثانیه انجام میشود.
حال این سوال پیش می‌آید مگر کلاس stringbuilder چه میکند که زمان پردازش آن قدر کوتاه است؟
همانطور که گفتیم این کلاس mutable یا تغییر پذیر است و برای انجام عملیات‌های ویرایشی نیازی به ایجاد شیء جدید در حافظه ندارد؛ در نتیجه باعث کاهش انتقال غیرضروری داده‌ها برای عملیات پایه‌ای چون الحاق رشته‌ها میگردد.
stringbuilder شامل یک بافر با ظرفیتی مشخص است (به طور پیش فرض 16 کاراکتر). این کلاس آرایه‌هایی از کاراکترها را پیاده سازی میکند که برای عملیات و پردازش‌هایش  از یک رابط کاربرپسند برای برنامه نویسان استفاده می‌کند. اگر تعداد کاراکترها کمتر از 16 باشد مثلا 5 ، فقط 5 خانه آرایه استفاده میشود و مابقی خانه‌ها خالی میماند و با اضافه شدن یک کاراکتر جدید، دیگر شیء جدیدی در حافظه درست نمی‌شود؛ بلکه در خانه ششم قرار می‌گیرد و اگر تعداد کاراکترهایی که اضافه می‌شوند باعث شود از 16 کاراکتر رد شود، مقدار خانه‌ها دو برابر میشوند؛ هر چند این عملیات دو برابر شدن resizing عملیاتی کند است ولی این اتفاق به ندرت رخ می‌دهد.
کد زیر یک آرایه 15 کاراکتری ایجاد می‌کند و عبارت #Hello C را در آن قرار می‌دهد.
StringBuilder sb = new StringBuilder(15);
sb.Append("Hello, C#!");

در شکل بالا خانه هایی خالی مانده است Unused و  جا برای کاراکترهای جدید به اندازه خانه‌های unused هست و اگر بیشتر شود همانطور که گفتیم تعداد خانه‌ها 2 برابر می‌شوند که در اینجا میشود 30.

استفاده از متد ایستای string.Format
از این متد برای نوشتن یک متن به صورت قالب و سپس جایگزینی مقادیر استفاده می‌شود:
DateTime date = DateTime.Now;
string name = "David Scott";
string task = "Introduction to C# book";
string location = "his office";
 
string formattedText = String.Format(
    "Today is {0:MM/dd/yyyy} and {1} is working on {2} in {3}.",
    date, name, task, location);
Console.WriteLine(formattedText);
در کد بالا ابتدا ساختار قرار گرفتن تاریخ را بر اساس الگو بین {} مشخص می‌کنیم و متغیر date در آن قرار می‌گیرد و سپس برای {1},{2},{3} به ترتیب قرار گیری آن‌ها متغیرهای name,last,location قرار میگیرند.
از ()ToString. هم می‌توان برای فرمت بندی خروجی استفاده کرد؛ مثل همین عبارت MM/dd/yyyy در خروجی نوع داده تاریخ و زمان.
مطالب
احراز هویت و اعتبارسنجی کاربران در برنامه‌های Angular - قسمت پنجم - محافظت از مسیرها
در قسمت سوم، کار ورود به سیستم و امکان مشاهده‌ی صفحه‌ی محافظت شده‌ی پس از لاگین را پیاده سازی کردیم. در این قسمت می‌خواهیم امکان دسترسی به مسیر http://localhost:4200/protectedPage را کنترل کنیم. تا اینجا اگر کاربر بدون لاگین کردن نیز این مسیر را درخواست کند، می‌تواند حداقل ساختار ابتدایی آن‌را مشاهده کند که باید مدیریت شود و این مدیریت دسترسی می‌تواند بر اساس وضعیت لاگین کاربر و همچنین نقش‌های او در سیستم باشد:


طراحی بخش‌هایی از این قسمت، از پروژه‌ی «کنترل دسترسی‌ها در Angular با استفاده از Ng2Permission» ایده گرفته شده‌اند.


استخراج اطلاعات کاربر وارد شده‌ی به سیستم از توکن دسترسی او

یکی از روش‌های دسترسی به اطلاعات کاربر در سمت کلاینت، مانند نقش‌های او، تدارک متدی در سمت سرور و بازگشت Claims او به سمت کلاینت است:
public IActionResult Get()
{ 
    var user = this.User.Identity as ClaimsIdentity; 
    var config = new 
    { 
        userName = user.Name, 
        roles = user.Claims.Where(x => x.Type == ClaimTypes.Role).Select(x => x.Value).ToList() 
    }; 
    return Ok(config); 
}
 اما با توجه به اینکه در زمان لاگین، نقش‌های او (و سایر Claims دلخواه) نیز به توکن دسترسی نهایی اضافه می‌شوند، می‌توان این کوئری گرفتن مدام از سرور را تبدیل به روش بسیار سریعتر استخراج آن‌ها از توکنی که هم اکنون در کش مرورگر ذخیره شده‌است، کرد.
همچنین باید دقت داشت چون این توکن دارای امضای دیجیتال است، کوچکترین تغییری در آن در سمت کاربر، سبب برگشت خوردن خودکار درخواست ارسالی به سمت سرور می‌شود (یکی از مراحل اعتبارسنجی کاربر در سمت سرور، اعتبارسنجی توکن دریافتی (قسمت cfg.TokenValidationParameters) و همچنین بررسی خودکار امضای دیجیتال آن است). بنابراین نگرانی از این بابت وجود ندارد.
اگر اطلاعات کاربر در سمت سرور تغییر کنند، با اولین درخواست ارسالی به سمت سرور، رخ‌داد OnTokenValidated وارد عمل شده و درخواست ارسالی را برگشت می‌زند (در مورد پیاده سازی سمت سرور این مورد، در مطلب «اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity» بیشتر بحث شده‌است). در این حالت کاربر مجبور به لاگین مجدد خواهد شد که این مورد سبب به روز رسانی خودکار اطلاعات توکن‌های ذخیره شده‌ی او در مرورگر نیز می‌شود.

اگر از قسمت دوم این سری به‌خاطر داشته باشید، توکن decode شده‌ی برنامه، چنین شکلی را دارد:
{
  "jti": "d1272eb5-1061-45bd-9209-3ccbc6ddcf0a",
  "iss": "http://localhost/",
  "iat": 1513070340,
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "1",
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Vahid",
  "DisplayName": "وحید",
  "http://schemas.microsoft.com/ws/2008/06/identity/claims/serialnumber": "709b64868a1d4d108ee58369f5c3c1f3",
  "http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata": "1",
  "http://schemas.microsoft.com/ws/2008/06/identity/claims/role": [
    "Admin",
    "User"
  ],
  "nbf": 1513070340,
  "exp": 1513070460,
  "aud": "Any"
}
به همین جهت متدی را برای تبدیل این اطلاعات به شیء کاربر، ایجاد خواهیم کرد و در سراسر برنامه از این اطلاعات آماده، برای تعیین دسترسی او به قسمت‌های مختلف برنامه‌ی سمت کلاینت، استفاده می‌کنیم.
برای این منظور اینترفیس src\app\core\models\auth-user.ts را به صورت ذیل ایجاد می‌کنیم:
export interface AuthUser {
  userId: string;
  userName: string;
  displayName: string;
  roles: string[];
}
پس از آن متد getAuthUser را جهت استخراج خواص ویژه‌ی توکن دسترسی decode شده‌ی فوق، به صورت ذیل به سرویس Auth اضافه می‌کنیم:
  getAuthUser(): AuthUser {
    if (!this.isLoggedIn()) {
      return null;
    }

    const decodedToken = this.getDecodedAccessToken();
    let roles = decodedToken["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"];
    if (roles) {
      roles = roles.map(role => role.toLowerCase());
    }
    return Object.freeze({
      userId: decodedToken["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"],
      userName: decodedToken["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],
      displayName: decodedToken["DisplayName"],
      roles: roles
    });
  }
کار با این متد بسیار سریع است و نیازی به رفت و برگشتی به سمت سرور ندارد؛ چون تمام اطلاعات استخراجی توسط آن هم اکنون در کش سریع مرورگر کلاینت موجود هستند. استفاده‌ی از متد Object.freeze هم سبب read-only شدن این خروجی می‌شود.
همچنین در اینجا تمام نقش‌های دریافتی، تبدیل به LowerCase شده‌اند. با اینکار مقایسه‌ی بعدی آن‌ها با نقش‌های درخواستی، حساس به بزرگی و کوچکی حروف نخواهد بود.


تعریف نقش‌های دسترسی به مسیرهای مختلف سمت کلاینت

مرسوم است اطلاعات اضافی مرتبط با هر مسیر را به خاصیت data آن route انتساب می‌دهند. به همین جهت به فایل dashboard-routing.module.ts مراجعه کرده و نقش‌های مجاز به دسترسی به مسیر protectedPage را به خاصیت data آن به صورت ذیل اضافه می‌کنیم:
import { ProtectedPageComponent } from "./protected-page/protected-page.component";
import { AuthGuardPermission } from "../core/models/auth-guard-permission";

const routes: Routes = [
  {
    path: "protectedPage",
    component: ProtectedPageComponent,
    data: {
      permission: {
        permittedRoles: ["Admin"],
        deniedRoles: null
      } as AuthGuardPermission
    }
  }
];
که در اینجا ساختار اینترفیس AuthGuardPermission، در فایل جدید app\core\models\auth-guard-permission.ts به صورت ذیل تعریف شده‌است:
export interface AuthGuardPermission {
  permittedRoles?: string[];
  deniedRoles?: string[];
}
به این ترتیب هر قسمتی از برنامه که نیاز به اطلاعات سطوح دسترسی مسیری را داشت، ابتدا به دنبال route.data["permission"] خواهد گشت (کلیدی به نام permission در خاصیت data یک مسیر) و سپس اطلاعات آن‌را بر اساس ساختار اینترفیس AuthGuardPermission، تحلیل می‌کند.
در اینجا تنها باید یکی از خواص permittedRoles  (نقش‌های مجاز به دسترسی/صدور دسترسی فقط برای این نقش‌های مشخص، منهای مابقی) و یا deniedRoles (نقش‌های غیرمجاز به دسترسی/دسترسی همه‌ی نقش‌های ممکن، منهای این نقش‌های تعیین شده)، مقدار دهی شوند.


افزودن کامپوننت «دسترسی ندارید» به ماژول Authentication

در ادامه می‌خواهیم اگر کاربری به مسیری دسترسی نداشت، به صورت خودکار به صفحه‌ی «دسترسی ندارید» هدایت شود. به همین جهت این کامپوننت را به صورت ذیل به ماژول authentication اضافه می‌کنیم:
>ng g c Authentication/AccessDenied
با این خروجی که سبب درج خودکار آن در قسمت declaration فایل authentication.module نیز می‌شود:
 AccessDenied
  create src/app/Authentication/access-denied/access-denied.component.html (32 bytes)
  create src/app/Authentication/access-denied/access-denied.component.ts (296 bytes)
  create src/app/Authentication/access-denied/access-denied.component.css (0 bytes)
  update src/app/Authentication/authentication.module.ts (550 bytes)
سپس به فایل authentication-routing.module.ts مراجعه کرده و مسیریابی آن‌را نیز اضافه می‌کنیم:
import { LoginComponent } from "./login/login.component";
import { AccessDeniedComponent } from "./access-denied/access-denied.component";

const routes: Routes = [
  { path: "login", component: LoginComponent },
  { path: "accessDenied", component: AccessDeniedComponent }
];
قالب access-denied.component.html را نیز به صورت ذیل تکمیل می‌کنیم:
<h1 class="text-danger">
  <span class="glyphicon glyphicon-ban-circle"></span> Access Denied
</h1>
<p>Sorry! You don't have access to this page.</p>
<button class="btn btn-default" (click)="goBack()">
  <span class="glyphicon glyphicon-arrow-left"></span> Back
</button>

<button *ngIf="!isAuthenticated" class="btn btn-success" [routerLink]="['/login']"
  queryParamsHandling="merge">
  Login
</button>
که دکمه‌ی Back آن به کمک سرویس Location، صورت ذیل پیاده سازی شده‌است:
import { Component, OnInit } from "@angular/core";
import { Location } from "@angular/common";

import { AuthService } from "../../core/services/auth.service";

@Component({
  selector: "app-access-denied",
  templateUrl: "./access-denied.component.html",
  styleUrls: ["./access-denied.component.css"]
})
export class AccessDeniedComponent implements OnInit {

  isAuthenticated = false;

  constructor(
    private location: Location,
    private authService: AuthService
  ) {
  }

  ngOnInit() {
    this.isAuthenticated = this.authService.isLoggedIn();
  }

  goBack() {
    this.location.back(); // <-- go back to previous location on cancel
  }
}


در اینجا اگر کاربر به سیستم وارد نشده باشد، دکمه‌ی لاگین نیز به او نمایش داده می‌شود. همچنین وجود "queryParamsHandling="merge در لینک مراجعه‌ی به صفحه‌ی لاگین، سبب خواهد شد تا query string موجود در صفحه نیز حفظ شود و به صفحه‌ی لاگین انتقال پیدا کند. در صفحه‌ی لاگین نیز جهت پردازش این نوع کوئری استرینگ‌ها، تمهیدات لازم درنظر گرفته شده‌اند.
دکمه‌ی back آن نیز توسط سرویس توکار Location واقع در مسیر angular/common@ پیاده سازی شده‌است.


ایجاد یک محافظ مسیر سمت کلاینت برای بررسی وضعیت کاربر جاری و همچنین نقش‌های او

پس از تعریف متد getAuthUser و استخراج اطلاعات کاربر از توکن دسترسی دریافتی که شامل نقش‌های او نیز می‌شود، اکنون می‌توان متد بررسی این نقش‌ها را نیز به سرویس Auth اضافه کرد:
  isAuthUserInRoles(requiredRoles: string[]): boolean {
    const user = this.getAuthUser();
    if (!user || !user.roles) {
      return false;
    }
    return requiredRoles.some(requiredRole => user.roles.indexOf(requiredRole.toLowerCase()) >= 0);
  }

  isAuthUserInRole(requiredRole: string): boolean {
    return this.isAuthUserInRoles([requiredRole]);
  }
متد some در جاوا اسکریپت شبیه به متد Any در C# LINQ عمل می‌کند. همچنین در مقایسه‌ی صورت گرفته، با توجه به اینکه user.roles را پیشتر به LowerCase تبدیل کرد‌یم، حساسیتی بین نقش Admin و admin و کلا کوچکی و بزرگی نام نقش‌ها وجود ندارد.
اکنون در هر قسمتی از برنامه که نیاز به بررسی امکان دسترسی یک کاربر به نقش یا نقش‌هایی خاص وجود داشته باشد، می‌توان AuthService را به سازنده‌ی ‌آن تزریق و سپس از متد فوق جهت بررسی نهایی، استفاده کرد.

در ادامه یک Route Guard جدید را در مسیر app\core\services\auth.guard.ts ایجاد می‌کنیم. کار آن بررسی خودکار امکان دسترسی به یک مسیر درخواستی است:
import { Injectable } from "@angular/core";
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router";

import { AuthService } from "./auth.service";
import { AuthGuardPermission } from "../models/auth-guard-permission";

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {

    if (!this.authService.isLoggedIn()) {
      this.showAccessDenied(state);
      return false;
    }

    const permissionData = route.data["permission"] as AuthGuardPermission;
    if (!permissionData) {
      return true;
    }

    if (Array.isArray(permissionData.deniedRoles) && Array.isArray(permissionData.permittedRoles)) {
      throw new Error("Don't set both 'deniedRoles' and 'permittedRoles' in route data.");
    }

    if (Array.isArray(permissionData.permittedRoles)) {
      const isInRole = this.authService.isAuthUserInRoles(permissionData.permittedRoles);
      if (isInRole) {
        return true;
      }

      this.showAccessDenied(state);
      return false;
    }

    if (Array.isArray(permissionData.deniedRoles)) {
      const isInRole = this.authService.isAuthUserInRoles(permissionData.deniedRoles);
      if (!isInRole) {
        return true;
      }

      this.showAccessDenied(state);
      return false;
    }
  }

  private showAccessDenied(state: RouterStateSnapshot) {
    this.router.navigate(["/accessDenied"], { queryParams: { returnUrl: state.url } });
  }
}
در اینجا در ابتدا وضعیت لاگین کاربر بررسی می‌گردد. این وضعیت نیز از طریق سرویس Auth که به سازنده‌ی کلاس تزریق شده‌است، تامین می‌شود. اگر کاربر هنوز لاگین نکرده باشد، به صفحه‌ی عدم دسترسی هدایت خواهد شد.
سپس خاصیت permission اطلاعات مسیر استخراج می‌شود. اگر چنین مقداری وجود نداشت، همینجا کار با موفقیت خاتمه پیدا می‌کند.
در آخر وضعیت دسترسی به نقش‌های استخراجی deniedRoles و permittedRoles که از اطلاعات مسیر دریافت شدند، توسط متد isAuthUserInRoles سرویس Auth بررسی می‌شوند.

در متد showAccessDenied کار ارسال آدرس درخواستی (state.url) به صورت یک کوئری استرینگ (queryParams) با کلید returnUrl به صفحه‌ی accessDenied صورت می‌گیرد. در این صفحه نیز دکمه‌ی لاگین به همراه "queryParamsHandling="merge است. یعنی کامپوننت لاگین برنامه، کوئری استرینگ returnUrl را دریافت می‌کند:
 this.returnUrl = this.route.snapshot.queryParams["returnUrl"];
 و پس از لاگین موفق، در صورت وجود این کوئری استرینگ، هدایت خودکار کاربر، به مسیر returnUrl پیشین صورت خواهد گرفت:
if (this.returnUrl) {
   this.router.navigate([this.returnUrl]);
} else {
   this.router.navigate(["/protectedPage"]);
}

محل معرفی این AuthGuard جدید که در حقیقت یک سرویس است، در ماژول Core، در قسمت providers آن، به صورت ذیل می‌باشد:
import { AuthGuard } from "./services/auth.guard";

@NgModule({
  providers: [
    AuthGuard
  ]
})
export class CoreModule {}
در آخر برای اعمال این Guard جدید، به فایل dashboard-routing.module.ts مراجعه کرده و خاصیت canActivate را مقدار دهی می‌کنیم:
import { ProtectedPageComponent } from "./protected-page/protected-page.component";
import { AuthGuardPermission } from "../core/models/auth-guard-permission";
import { AuthGuard } from "../core/services/auth.guard";

const routes: Routes = [
  {
    path: "protectedPage",
    component: ProtectedPageComponent,
    data: {
      permission: {
        permittedRoles: ["Admin"],
        deniedRoles: null
      } as AuthGuardPermission
    },
    canActivate: [AuthGuard]
  }
];
به این ترتیب با درخواست این مسیر، پیش از فعالسازی و نمایش آن، توسط AuthGuard معرفی شده‌ی به آن، کار بررسی وضعیت کاربر و نقش‌های او که از خاصیت permission خاصیت data دریافت می‌شوند، صورت گرفته و اگر عملیات تعیین اعتبار اطلاعات با موفقیت به پایان رسید، آنگاه کاربر مجوز دسترسی به این قسمت از برنامه را خواهد یافت.

اگر قصد آزمایش آن‌را داشتید، فقط کافی است بجای نقش Admin، مثلا Admin1 را در permittedRoles مقدار دهی کنید، تا صفحه‌ی access denied را در صورت درخواست مسیر protectedPage، بتوان مشاهده کرد.




کدهای کامل این سری را از اینجا می‌توانید دریافت کنید.
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کرده‌اید. سپس از طریق خط فرمان به ریشه‌ی پروژه‌ی ASPNETCore2JwtAuthentication.AngularClient وارد شده و دستور npm install را صادر کنید تا وابستگی‌های آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o، برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد (و یا همان اجرای فایل ng-serve.bat). همچنین باید به پوشه‌ی ASPNETCore2JwtAuthentication.WebApp نیز مراجعه کرده و فایل dotnet_run.bat را اجرا کنید، تا توکن سرور برنامه نیز فعال شود.
مطالب
معرفی افزونه‌های مفید VSCode جهت کار با Angular
VSCode یکی از بهترین ادیتورهایی است که از آن می‌توان برای توسعه‌ی برنامه‌های Angular استفاده کرد و در این بین افزونه‌های ویژه‌ای جهت کار با Angular برای آن تدارک دیده شده‌اند که در ادامه تعدادی از مهم‌ترین‌های آن‌ها را بررسی می‌کنیم.


Angular Essentials

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


Angular Language Service

نگارش‌های اخیر Angular به همراه یک سرویس زبان نیز می‌باشند که به ادیتورهای مختلف این امکان را می‌دهد تا توسط این ویژگی بتوانند قابلیت‌های ویرایشی بهتری را جهت برنامه‌های Angular ارائه کنند. برای مثال ویرایش مطلوب قالب‌های کامپوننت‌های Angular و استفاده‌ی از Syntax خاص آن، موردی است که توسط هیچکدام از HTML ادیتورهای موجود پشتیبانی نمی‌شود. اکنون به کمک سرویس زبان Angular و افزونه‌ی ویژه‌ی آن برای VSCode که توسط تیم اصلی Angular توسعه یافته‌است، امکان ویرایش غنی قالب‌های HTML ایی آن فراهم شده‌است. این افزونه یک چنین قابلیت‌هایی را فراهم می‌کند:
الف) AOT Diagnostic messages
اگر قالب HTML ایی مورد استفاده (چه به صورت inline و چه در یک فایل html مجزا) به خاصیتی تعریف نشده اشاره کند، بلافاصله خطای مرتبطی ظاهر خواهد شد:


ب) Completions lists یا همان Intellisense
ج) امکان Go to definition با کلیک راست بر روی خواص و متدهای ذکر شده‌ی در قالب.
د) Quick info که با نزدیک کردن اشاره‌گر ماوس به خاصیت یا متدی در صفحه، اطلاعات بیشتری را در مورد آن نمایش می‌دهد.


angular2-inline

علاوه بر افزونه‌ی سرویس زبان‌های Angular، این افزونه نیز قابلیت درک قالب‌های inline کامپوننت‌ها را داشته و به همراه syntax highlighting و همچنین Intellisense است.


Auto Import

حین کار با TypeScript، هر ماژولی که در صفحه ارجاعی داشته باشد، باید در ابتدای فایل جاری import شود. افزونه‌ی Auto Import با بررسی ماژول‌های موجود و فراهم آوردن Intellisense ایی بر اساس آن‌ها، این‌کار را ساده‌تر می‌کند:


بنابراین این افزونه صرفا مختص به Angular نیست و برای کارهای متداول TypeScript نیز بسیار مفید است.


TSLint

این افزونه ابزار TSLint را با VSCode یکپارچه می‌کند. بنابراین نیاز است پیش از نصب این افزونه، وابستگی‌های ذیل را نیز به صورت سراسری نصب کرد:
 > npm install -g tslint typescript
کار TSLint انجام static code analysis است؛ چیزی شبیه به افزونه‌هایی مانند ری‌شارپر در ویژوال استودیو که راهنماهایی را در مورد بهتر کردن کیفیت کدهای نوشته شده ارائه می‌دهد.
تعدادی از امکانات آن‌را پس از نصب، با فشردن دکمه‌ی F1 می‌توان مشاهده کرد:


برای مثال تولید فایل tslint.json، امکان سفارشی سازی موارد بررسی شونده‌ی توسط این افزونه را فراهم می‌کند و اگر برنامه‌ی خود را توسط Angular CLI ایجاد کرده‌اید، این فایل هم اکنون در ریشه‌ی پروژه قرار دارد.
در مورد TSLint در مطلب «Angular CLI - قسمت دوم - ایجاد یک برنامه‌ی جدید» بیشتر توضیح داده شده‌است و اینبار به کمک این افزونه، خطاهای یاد شده را دقیقا درون محیط ادیتور و به صورت خودکار و یکپارچه‌ای مشاهده خواهید کرد.



Angular v4 TypeScript Snippets

سیستم کار VSCode مبتنی بر ایجاد فایل‌های خالی است و مفهوم قالب‌های از پیش آماده‌ی فایل‌ها در آن وجود ندارد. اما با کمک Code Snippets می‌توان این خلاء را پر کرد. افزونه‌ی Angular v4 TypeScript Snippets دقیقا به همین منظور طراحی شده‌است و زمانیکه حروف -a یا -rx را در صفحه تایپ می‌کنید، منویی ظاهر خواهد شد که توسط آن می‌توان قالب ابتدایی شروع به کار با انواع و اقسام جزئیات پروژه‌های Angular را تهیه کرد.



Path Intellisense

این افزونه مسیر فایل‌های موجود را به صورت یک Intellisense ارائه می‌کند و به این صورت به سادگی می‌توان مسیرهای اسکریپت‌ها و یا شیوه‌نامه‌ها را در ادیتور انتخاب و وارد کرد.