نظرات مطالب
Angular Material 6x - قسمت اول - افزودن آن به برنامه
جهت اطلاع
- این پروژه به Angular Material 9x ارتقاء داده شده؛ جزئیات بیشتر
- کلیات نهایی آن با مطالب این سری یکی است. فقط روش importهای آن تغییر کرده که در سورس نهایی قابل مشاهده‌است.
مطالب
امکان یافتن پیش از موعد مشکلات قالب‌های Angular در نگارش 5 آن
مشکلات کامپوننت‌های Angular را چون با زبان TypeScript تهیه می‌شوند، می‌توان بلافاصله در ادیتور مورد استفاده و یا در حین کامپایل برنامه مشاهده کرد؛ اما یک چنین بررسی در مورد قالب‌های HTML ایی آن در زمان کامپایل انجام نمی‌شود و اگر مشکلی وجود داشته باشد، این مشکلات را صرفا در زمان اجرای برنامه در مرورگر می‌توان مشاهده کرد. برای رفع این مشکل و بهبود این وضعیت، در نگارش 5.2.0 فریم ورک Angular (و همچنین Angular CLI 1.7 به بعد)، پرچم جدیدی به تنظیمات کامپایلر آن اضافه شده‌است که با فعالسازی آن، مشکلات binding احتمالی در قالب‌های کامپوننت‌ها را می‌توان یافت. زمانیکه توسط Angular CLI یک برنامه‌ی Angular را در حالت AoT کامپایل می‌کنیم، کامپایلر مراحلی را طی می‌کند که توسط آن کدهای یک قالب کامپوننت، تبدیل به دستور العمل‌هایی قابل اجرای در مرورگر می‌شوند. در طی یکی از این مراحل، کامپایلر قالب‌های Angular، از کامپایلر TypeScript برای اعتبارسنجی عبارت‌های binding استفاده می‌کند. اکنون می‌توان خروجی این مرحله را نیز در حین کار با Angular CLI، مشاهده و مشکلات گزارش شده‌ی توسط آن‌را برطرف کرد.


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

برای فعالسازی بررسی مشکلات قالب‌های کامپوننت‌ها، نیاز است به فایل تنظیمات کامپایلر TypeScript و یا همان tsconfig.json مراجعه کرد و سپس قسمت جدیدی را به آن به نام angularCompilerOptions، افزود:
{
  "compilerOptions": {
    "experimentalDecorators": true,
    ...
   },
   "angularCompilerOptions": {
     "fullTemplateTypeCheck": true,
     "preserveWhiteSpace": false,
     ...
   }
 }
- در اینجا با معرفی خاصیت fullTemplateTypeCheck و تنظیم آن به true، مشکلات موجود در قالب‌ها را در زمان کامپایل برنامه می‌توانید مشاهده کنید.
- البته این خاصیت در حین استفاده‌ی از یکی از دستورات ng serve --aot  و یا  ng build --prod انتخاب می‌شود.
- مقدار این پرچم در نگارش‌های 5x به صورت پیش‌فرض به false تنظیم شده‌است؛ اما در نگارش 6 آن به true تنظیم خواهد شد. بنابراین بهتر است از هم اکنون کار با آن‌را شروع کنید.


یک مثال: بررسی خاصیت fullTemplateTypeCheck

فرض کنید اینترفیس یک مدل را به صورت زیر تعریف کرده‌اید که فقط دارای خاصیت name است:
export interface PonyModel {
   name: string;
}
سپس یک خاصیت عمومی را بر همین مبنا در کامپوننتی، تعریف و مقدار دهی اولیه کرده‌اید:
import { PonyModel } from "./pony";

@Component({
  selector: "app-detect-common-errors-test",
  templateUrl: "./detect-common-errors-test.component.html",
  styleUrls: ["./detect-common-errors-test.component.css"]
})
export class DetectCommonErrorsTestComponent implements OnInit {

  ponyModel: PonyModel = { name: "Pony1" };
اکنون در قالب این کامپوننت، به شکل زیر از این وهله استفاده شده‌است:
 <p>Hello {{ponyModel.age}}

در این حالت اگر fullTemplateTypeCheck فعال شده باشد و دستور ng build --prod را صادر کنیم، به خروجی ذیل خواهیم رسید:
 \detect-common-errors-test.component.html(5,4): : Property 'age' does not exist on type 'PonyModel'.
همانطور که ملاحظه می‌کنید اینبار خطاهای کامپایل فایل html نیز در خروجی کامپایلر ظاهر شده‌است و عنوان می‌کند خاصیت age در اینترفیس PonyModel وجود خارجی ندارد.

برای اینکه بتوانید به حداکثر کارآیی این قابلیت برسید، بهتر است گزینه‌ی strict را در تنظیمات کامپایلر TypeScript روشن کنید و خودتان را به کار با نوع‌های نال نپذیر عادت دهید. به این ترتیب می‌توانید تعداد خطاهای احتمالی بیشتری را پیش از موعد و پیش از وقوع آن‌ها در زمان اجرا، در زمان کامپایل، پیدا و رفع کنید.


یک نکته‌ی تکمیلی
افزونه‌ی Angular Language service نیز یک چنین قابلیتی را به همراه دارد (و حتی در نگارش‌های پیش از 5 نیز قابل استفاده است).
مطالب
طراحی یک گرید با Angular و ASP.NET Core - قسمت سوم - قالب پذیر ساختن گرید
در قسمت دوم، قالب نمایش ردیف‌های جدول، ثابت است و درون جدول به صورت مستقیمی درج و تعریف شده‌است. در ادامه می‌خواهیم این گرید را به نحوی تغییر دهیم که به ازای حالت‌های مختلفی مانند نمایش اطلاعات و یا ویرایش اطلاعات هر ردیف، از قالب‌های خاص آن‌ها استفاده شود.
قابلیتی که در ادامه از آن برای «قالب پذیر ساختن گرید» استفاده خواهیم کرد، همان نکته‌ی «امکان تعویض پویای قالب‌های یک دربرگیرنده» است که در مطلب «امکان تعریف قالب‌ها در Angular با دایرکتیو ng-template» به آن پرداختیم.


تعریف قالب‌های نمایش و ویرایش اطلاعات یک ردیف در گرید طراحی شده

پس از آشنایی با دایرکتیوهای تعریف و کار با قالب‌ها در Angular، اکنون تبدیل بدنه‌ی ثابت جدول، به دو قالب نمایش و ویرایش، ساده‌است.
در قسمت دوم این سری، کار رندر بدنه‌ی اصلی گرید توسط همین چند سطر، در قالب آن مدیریت می‌شود:
    <tbody>
      <tr *ngFor="let item of queryResult.items; let i = index">
        <td class="text-center">{{ itemsPerPage * (currentPage - 1) + i + 1 }}</td>
        <td class="text-center">{{ item.productId }}</td>
        <td class="text-center">{{ item.productName }}</td>
        <td class="text-center">{{ item.price | number:'.0' }}</td>
        <td class="text-center">
          <input id="item-{{ item.productId }}" type="checkbox" [checked]="item.isAvailable"
            disabled="disabled" />
        </td>
      </tr>
    </tbody>
  </table>

در ادامه قسمت داخلی ngFor را تبدیل به یک ng-container می‌کنیم تا قالب پذیر شود:
    <tbody>
      <tr *ngFor="let item of queryResult.items; let i = index">
        <ng-container [ngTemplateOutlet]="loadTemplate(item)"
                 [ngOutletContext]="{ $implicit: item, idx: i }"></ng-container>
      </tr>
    </tbody>
کار دایرکتیو ngOutletContext، تنظیم شیء context هر قالب است. به این ترتیب شیء متناظر با هر ردیف و همچنین ایندکس آن‌را به هر قالب ارجاع می‌دهیم. خاصیت implicit$ به این معنا است که اگر منبع داده‌ی متغیر ورودی مشخص نشد، از مقدار item استفاده شود.
در اینجا ngTemplateOutlet این امکان را می‌دهد تا بتوان توسط کدهای برنامه، قالب هر ردیف را مشخص کرد. متد loadTemplate در کدهای کامپوننت متناظر فراخوانی شده و بر اساس وضعیت هر ردیف، یکی از دو قالب ذیل را بازگشت می‌دهد:

الف) قالب نمایش معمولی و فقط خواندنی رکوردها


<!--The Html Template for Read-Only Rows-->
<ng-template #readOnlyTemplate let-item let-i="idx">
  <td class="text-center">{{ itemsPerPage * (currentPage - 1) + i + 1 }}</td>
  <td class="text-center">{{ item.productId }}</td>
  <td class="text-center">{{ item.productName }}</td>
  <td class="text-center">{{ item.price | number:'.0' }}</td>
  <td class="text-center">
    <input id="item-{{ item.productId }}" type="checkbox" [checked]="item.isAvailable"
      disabled="disabled" />
  </td>
  <td>
    <input type="button" value="Edit" class="btn btn-default btn-xs" (click)="editItem(item)"
    />
  </td>
  <td>
    <input type="button" value="Delete" (click)="deleteItem(item)" class="btn btn-danger btn-xs"
    />
  </td>
</ng-template>
همانطور که ملاحظه می‌کنید، در اینجا بدنه‌ی ngFor را به یک ng-template مشخص شده‌ی با readOnlyTemplate# انتقال داده‌ایم. همچنین دو متغیر ورودی item و i را توسط -let تعریف کرده‌ایم. چون عبارت منبع داده item مشخص نشده‌است، از همان خاصیت implicit$ شیء context استفاده می‌کند.
این قالب در کدهای کامپوننت آن به صورت ذیل قابل دسترسی و انتخاب شده‌است:
 @ViewChild("readOnlyTemplate") readOnlyTemplate: TemplateRef<any>;

ب) قالب ویرایش اطلاعات هر ردیف که از آن برای افزودن یک ردیف جدید هم می‌توان استفاده کرد



شبیه به همان کاری را که برای نمایش ردیف‌های فقط خواندنی انجام دادیم، در مورد قالب ویرایش هر ردیف نیز تکرار می‌کنیم. در اینجا فقط امکان ویرایش نام محصول، قیمت آن و موجود بودن آن‌را توسط یک‌سری input box مهیا کرده‌ایم:
<!--The Html Template for Editable Rows-->
<ng-template #editTemplate let-item let-i="idx">
  <td class="text-center">{{ itemsPerPage * (currentPage - 1) + i + 1 }}</td>
  <td class="text-center">{{ item.productId }}</td>
  <td class="text-center">
    <input type="text" [(ngModel)]="selectedItem.productName" class="form-control" />
  </td>
  <td class="text-center">
    <input type="text" [(ngModel)]="selectedItem.price" class="form-control" />
  </td>
  <td class="text-center">
    <input id="item-{{ item.productId }}" type="checkbox" [checked]="item.isAvailable"
      [(ngModel)]="selectedItem.isAvailable" />
  </td>
  <td>
    <input type="button" value="Save" (click)="saveItem()" class="btn btn-success btn-xs"
    />
  </td>
  <td>
    <input type="button" value="Cancel" (click)="cancel()" class="btn btn-warning btn-xs"
    />
  </td>
</ng-template>
به این قالب نیز با توجه به template reference variable آن که editTemplate# نام دارد، به صورت ذیل در کامپوننت متناظر دسترسی خواهیم یافت.
 @ViewChild("editTemplate") editTemplate: TemplateRef<any>;

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


خواص عمومی مورد نیاز جهت کار با قالب‌ها و ویرایش‌های درون ردیفی

@ViewChild("readOnlyTemplate") readOnlyTemplate: TemplateRef<any>;
@ViewChild("editTemplate") editTemplate: TemplateRef<any>;
selectedItem: AppProduct;
isNewRecord: boolean;
برای اینکه بتوانیم قالب‌ها را به صورت پویا تعویض کنیم، نیاز است در کدهای کامپوننت، به آن‌ها دسترسی داشت. اینکار را توسط تعریف ViewChildهایی با همان نام template reference variable قالب‌ها انجام داده‌ایم.
به علاوه اگر به قالب editTemplate دقت کنید، مقدار ویرایش شده به [(ngModel)]="selectedItem.productName" انتساب داده می‌شود. به همین جهت شیء selectedItem نیز تعریف شده‌است.
همچنین نیاز است بدانیم اکنون در حال ویرایش یک ردیف هستیم یا این ردیف، کاملا ردیف جدیدی است. به همین جهت پرچم isNewRecord نیز تعریف شده‌است.


فعالسازی قالب ویرایش هر ردیف

در انتهای هر ردیف، دکمه‌ی ویرایش نیز قرار دارد که به (click) آن، رخداد editItem متصل است:
editItem(item: AppProduct) {
  this.selectedItem = item;
}
در اینجا Item انتخابی را به selectedItem انتساب می‌دهیم. همین مساله سبب محاسبه‌ی مجدد ردیف می‌شود. یعنی متد loadTemplate داخل حلقه‌ی ngFor مجددا فراخوانی می‌شود:
  loadTemplate(item: AppProduct) {
    if (this.selectedItem && this.selectedItem.productId === item.productId) {
      return this.editTemplate;
    } else {
      return this.readOnlyTemplate;
    }
  }
در اینجا بررسی می‌کنیم که آیا در حال ویرایش اطلاعات هستیم؟ آیا selectedItem  مقدار دهی شده‌است و نال نیست؟ اگر بله، قالب editTemplate را بازگشت می‌دهیم. اگر خیر، قالب نمایش ردیف‌های فقط خواندنی بازگشت داده می‌شود. به این ترتیب می‌توان در کدهای برنامه به صورت پویا، در مورد نمایش قالبی خاص تصمیم‌گیری کرد.


مدیریت افزودن یک ردیف جدید

دکمه‌ی افزودن یک ردیف جدید به صورت ذیل به قالب اضافه شده‌است:
<div class="panel">
  <input type="button" value="Add new product" class="btn btn-primary" (click)="addItem()"
  />
</div>
بنابراین نیاز است رخ‌داد addItem آن‌را به صورت ذیل تعریف کرد:
  addItem() {
    this.selectedItem = new AppProduct(0, "", 0, false);
    this.isNewRecord = true;

    this.queryResult.items.push(this.selectedItem);
    this.queryResult.totalItems++;
  }
در اینجا برخلاف حالت ویرایش که selectedItem را به item انتخابی ردیف جاری تنظیم کردیم، آن‌را به یک شیء جدید و تازه تنظیم می‌کنیم. همچنین پرچم isNewRecord  را نیز true خواهیم کرد. سپس این آیتم را به لیست رکوردهای موجود گرید نیز اضافه می‌کنیم. همینقدر تغییر، سبب محاسبه‌ی مجدد loadTemplate و بارگذاری قالب ویرایشی آن می‌شود.


مدیریت لغو ویرایش هر ردیف

برای اینکه ویرایش هر ردیف را لغو کنیم و قالب آن‌‌را به حالت فقط خواندنی بازگشت دهیم، فقط کافی است selectedItem را به نال تنظیم کنیم:
cancel() {
  this.selectedItem = null;
}
با این تنظیم و محاسبه‌ی خودکار و مجدد متد loadTemplate، قسمت return this.readOnlyTemplate فعال می‌شود که سبب نمایش عادی یک ردیف خواهد شد.


مدیریت حذف هر ردیف

در اینجا با پیاده سازی متد رخ‌دادگردان deleteItem و ارسال id هر ردیف به سرور، کار حذف هر ردیف را انجام خواهیم داد:
  deleteItem(item: AppProduct) {
    this.productsService
      .deleteAppProduct(item.productId)
      .subscribe((resp: Response) => {
        this.getPagedProductsList();
      });
  }


مدیریت ثبت و یا به روز رسانی هر ردیف

آخرین عملیاتی که باید مدیریت شود، بررسی پرچم isNewRecord است. اگر true بود، کار افزودن یک ردیف جدید صورت گرفته و سپس این پرچم false می‌شود. اگر false بود، به معنای درخواست به روز رسانی ردیفی مشخص است. در پایان هر دو عملیات selectedItem را نیز true می‌کنیم و این پایان عملیات باید داخل قسمت دریافت پاسخ از سرور مدیریت شود و نه پس از فراخوانی این متدها؛ چون متدهای subscribe غیرهمزمان بوده و ردیف‌های پس از آن‌ها بلافاصله اجرا می‌شوند.
  saveItem() {
    if (this.isNewRecord) {
      this.productsService
        .addAppProduct(this.selectedItem)
        .subscribe((resp: AppProduct) => {
          this.selectedItem.productId = resp.productId;
          this.isNewRecord = false;
          this.selectedItem = null;
        });
    } else {
      this.productsService
        .updateAppProduct(this.selectedItem.productId, this.selectedItem)
        .subscribe((resp: AppProduct) => {
          this.selectedItem = null;
        });
    }
  }


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
مسیرراه‌ها
WPF
          مطالب
          ایجاد سرویس چندلایه‎ی WCF با Entity Framework در قالب پروژه - 5
          پس از ایجاد متدها، نوبت به تغییرات App.Config می‎رسد. هرچند خود Visual Studio برای کلاس پیش‌گزیده‌ی خود تنظیماتی را در App.Config افزوده است ولی چنان‎چه در در خاطر دارید ما آن فایل‎ها را حذف کردیم و فایل‎های جدیدی به جای آن افزودیم. از این رو مراحل زیر را انجام دهید:
          1- فایل App.Config را از Solution Explorer باز کنید.
          2- به جای عبارت MyNewsWCFLibrary.Service1 در قسمت Service Name این عبارت را بنویسید: MyNewsWCFLibrary.MyNewsService
          3- در قسمت BaseAddress عبارت Design_Time_Addresses را حذف کنید.
          4- در قسمت BaseAddress شماره پورت را به 8080 تغییر دهید.
          5- در قسمت BaseAddress به جای Service1 بنویسید: MyNewsService
          6- در قسمت endpoint به جای عبارت MyNewsWCFLibrary.IService1 بنویسید: MyNewsWCFLibrary.IMyNewsService 
          در پایان تگ Service در App.Config باید همانند کد زیر باشد:
             <services>
                <service name="MyNewsWCFLibrary.MyNewsService">
                  <host>
                    <baseAddresses>
                      <add baseAddress="http://localhost:8080/MyNewsWCFLibrary/MyNewsService/" />
                    </baseAddresses>
                  </host>
                  <!-- Service Endpoints -->
                  <!-- Unless fully qualified, address is relative to base address supplied above -->
                  <endpoint address="" binding="basicHttpBinding" contract="MyNewsWCFLibrary.IMyNewsService">
                    <!-- 
                        Upon deployment, the following identity element should be removed or replaced to reflect the 
                        identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
                        automatically.
                    -->
                    <identity>
                      <dns value="localhost" />
                    </identity>
                  </endpoint>
                  <!-- Metadata Endpoints -->
                  <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. -->
                  <!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
                  <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
                </service>
              </services>
          تغییرات را ذخیره کنید و پروژه را اجرا کنید. باید پنجره‌ای شبیه به پنجره‌ی زیر نشان داده شود:

          در صورت مشاهده پیام خطا، ویژوال استودیو را ببندید و این‌بار به صورت Run as administrator باز کنید.

          برای نمونه روی متد AddCategory کلیک کنید. در پنجره نشان داده شده همانند شکل در برابر فیلد CatName مقداری وارد کنید و روی دکمه Invoke کلیک کنید. متد مورد نظر اجرا شده و مقداری که وارد کرده ایم در پایگاه داده‌ها ذخیره می‌شود. مقداری که در قسمت پایین دیده می‌شود خروجی متد است که در اینجا شناسه رکورد درج‌شده است.

          بار دیگر برای مشاهده رکورد درج‌شده روی متد GetAllCategory کلیک کنید. به علت این‌که این متد ورودی ندارد در قسمت بالا چیزی نشان داده نمی‌شود. روی دکمه Invoke کلیک کنید. با پیغام خطای زیر روبه‌رو خواهید شد:

          افزودن ویژگی Virtual به tblNews و tblCategory در بخش دوم  خواندید؛ باعث می‌شود که Entity Framework در هنگام اجرا کلاس‌هایی با عنوان "پروکسی‌های پویا" به کلاس‌های Address و Customer بیفزاید و بنابراین قابلیت Lazy Loading برای این کلاس‌ها در زمان اجرای برنامه فراهم می‌گردد. 

          ولی با افزودن پروکسی‌های پویا به کلاس‌های ما، این کلاس‌ها قابلیت انتقال خود از طریق سرویس‌های WCF را از دست می‌دهند زیرا پروکسی‌های پویا به طور پیش‌گزیده قابلیت سریالایز و دیسریالایز شدن را ندارند!

          خوشبختانه می‌توانیم این ویژگی را در کلاس DBContext غیرفعال کنیم. برای این منظور قالب سازنده‌ی آن یا MyNewsModel.Context.tt را از Solution Explorer باز کنید و کد زیر را در آن پیدا کنید:

          <#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
          {
              public <#=code.Escape(container)#>()
                  : base("name=<#=container.Name#>")
              {

          سپس در ادامه‌ی آن کدغیرفعال‌کردن پروکسی پویا را به این شکل بنویسید:

          <#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
          {
              public <#=code.Escape(container)#>()
                  : base("name=<#=container.Name#>")
              {
                Configuration.ProxyCreationEnabled = false;

          اکنون اگر فایل را ذخیره کنیم سپس فایل MyNewsModel.Context.cs را از Solution Explorer باز کنید؛ خواهید دید که این خط کد در جای خود قرارگرفته است.

          بار دیگر پروژه را اجرا کنید روی متد GetAllCategory کلیک کنید. این بار اگر دکمه Invoke را بفشارید با همانند شکل زیر را خواهید دید:

          در بخش ششم پیرامون ارتباط جدول‌های tblNews و tblCategory و نمایش محتویات وابسته جدول خبر به دسته و تنظیمات آن در t4 و کلاس Service

          در بخش هفتم پیرامون میزبانی WCFLibrary در یک Web Application

          و در بخش هشتم پیرامون ایجاد یک برنامه‌ی ویندوزی جهت استفاده از سرویس‌های WCF خواهم نوشت. 

          مطالب
          استفاده از کتابخانه‌های ثالث جاوا اسکریپتی در برنامه‌های AngularJS 2.0
          هزاران کتابخانه‌ی جاوا اسکریپتی مستقل از AngularJS 2.0 وجود دارند که نیاز است بتوانیم از آن‌ها در برنامه‌های مختلفی استفاده کنیم. در این مطلب، دو روش بارگذاری آن‌ها را بررسی خواهیم کرد.


          هدف: استفاده از کتابخانه‌ی jsSHA

          می‌خواهیم در یک برنامه‌ی AngularJS 2.0، از کتابخانه‌ی jsSHA استفاده کرده و هش SHA512 یک رشته را محاسبه کنیم.


          تامین پیشنیازهای اولیه

          می‌توان فایل‌های این کتابخانه را مستقیما از GitHub دریافت و به پروژه اضافه کرد. اما بهتر است این‌کار را توسط npm مدیریت کنیم. به همین جهت فایل package.json آن‌را گشوده و سپس مدخل متناظری را به آن اضافه کنید:
          "dependencies": {
              // ...
              "jssha": "^2.1.0",
              // ...
            },
          به این ترتیب با اجرای دستور npm install بر روی پوشه‌ی جاری و یا ذخیره‌ی فایل در ویژوال استودیو، کار دریافت خودکار این کتابخانه صورت گرفته و در مسیر node_modules\jssha\src ذخیره می‌شود.


          بارگذاری فایل‌های کتابخانه به صورت پویا

          یک روش استفاده از این کتابخانه یا هر کتابخانه‌ی جاوا اسکریپتی، افزودن مدخل تعریف آن به صفحه‌ی index.html است:
           <script src="node_modules/jssha/src/sha512.js"></script>
          این روش هر چند کار می‌کند، اما با توجه به اینکه AngularJS 2.0 از System.JS برای مدیریت ماژول‌های خود کمک می‌گیرد، می‌تواند با روش پویای ذیل جایگزین شود. برای این منظور ابتدا فایل systemjs.config.js را باز کنید و سپس دو تغییر ذیل را به آن اعمال نمائید:
          // map tells the System loader where to look for things
          var map = {
              // ...
              'jssha': 'node_modules/jssha/src'
          };
           
          // packages tells the System loader how to load when no filename and/or no extension
          var packages = {
              // ...
              'jssha': { main: 'sha512.js', defaultExtension: 'js' }
          };
          در اینجا به اشیاء map و packages آن فایل که کار بارگذاری ماژول‌ها را به صورت خودکار انجام می‌دهد، تعاریف جدید jssha را اضافه کرده‌ایم. در قسمت map، مسیر پوشه‌ی فایل‌های js این کتابخانه مشخص شده‌اند و در قسمت packages، نام فایل اصلی مدنظر و پسوندهای آن‌ها ذکر گردید‌ه‌اند.
          به این ترتیب هر زمانیکه کار import این کتابخانه صورت گیرد، بارگذاری پویای آن انجام خواهد شد. به علاوه ابزارهای بسته بندی و deploy پروژه هم این فایل را پردازش کرده و به صورت خودکار، کار bundling، فشرده سازی و یکی سازی اسکریپت‌ها را انجام می‌دهند.


          استفاده از jsSHA به صورت untyped

          پس از دریافت بسته‌های این کتابخانه و مشخص سازی نحوه‌ی بارگذاری پویای آن، اکنون نوبت به استفاده‌ی از آن است. در اینجا منظور از untyped این است که فرض کنیم برای این کتابخانه، فایل‌های typings مخصوص TypeScript وجود ندارند و پس از جستجوی در مخزن کد https://github.com/DefinitelyTyped/DefinitelyTyped نتوانسته‌ایم معادلی را برای آن پیدا کنیم. بنابراین فایل جدید untyped-sha.component.ts را با محتوای ذیل به پروژه اضافه کنید:
          import { Component, OnInit } from '@angular/core';
           
          var jsSHA = require("jssha"); // ==> loads `sha512.js` file dynamically using `systemjs.config.js` file definitions
          //declare var jsSHA: any; // ==> this requires adding <script src="node_modules/jssha/src/sha512.js"></script> to the first page manually.
           
          @Component({
              templateUrl: 'app/using-third-party-libraries/untyped-sha.component.html'
          })
          export class UnTypedShaComponent implements OnInit {
              hash: String;
           
              ngOnInit(): void {
                  let shaObj = new jsSHA("SHA-512", "TEXT");
                  shaObj.update("This is a test");
                  this.hash = shaObj.getHash("HEX");
              }
          }
          با این قالب untyped-sha.component.html
          <h1>SHA-512 Hash / UnTyped</h1>
           
          <p>String: This is a test</p>
          <p>HEX: {{hash}}</p>
          توضیحات
          هر زمانیکه فایل‌های typing یک کتابخانه‌ی جاوا اسکریپتی در دسترس نبودند، فقط کافی است از روش declare var jsSHA: any استفاده کنید. در اینجا any به همان حالت استاندارد و بی‌نوع جاوا اسکریپت اشاره می‌کند. در این حالت برنامه بدون مشکل کامپایل خواهد شد؛ اما از تمام مزایای TypeScript مانند بررسی نوع‌ها و همچنین intellisense محروم می‌شویم.
          در این مثال در hook ویژه‌ای به نام OnInit، کار ساخت شیء SHA را انجام داده و سپس هش عبارت This is a test محاسبه شده و به خاصیت عمومی hash انتساب داده می‌شود. سپس این خاصیت عمومی، در قالب این کامپوننت از طریق روش interpolation نمایش داده شده‌است.

          دو نکته‌ی مهم
          الف) اگر از روش declare var jsSHA: any استفاده کردید، کار بارگذاری فایل sha512.js به صورت خودکار رخ نخواهد داد؛ چون ماژولی را import نمی‌کند. بنابراین تعاریف systemjs.config.js ندید گرفته خواهد شد. در این حالت باید از همان روش متداول افزودن تگ script این کتابخانه به فایل index.html استفاده کرد.
          ب) برای بارگذاری پویای کتابخانه‌ی jsSHA بر اساس تعاریف فایل systemjs.config.js از متد require کمک بگیرید:
           var jsSHA = require("jssha");
          در این حالت باز هم متغیر jsSHA تعریف شده از نوع any است؛ اما اینبار متد require کار بارگذاری خودکار ماژولی را به نام jssha، انجام می‌دهد. این بارگذاری هم بر اساس تعاریف قسمت «بارگذاری فایل‌های کتابخانه به صورت پویا» ابتدای بحث کار می‌کند.


          استفاده از jsSHA به صورت typed

          کتابخانه‌ی jsSHA در مخزن کد https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/jssha دارای فایل d.ts. مخصوص خود است. برای نصب آن از یکی از دو روش ذیل استفاده کنید:
          الف) نصب دستی فایل‌های typings
           npm install -g typings
          typings install jssha --save --ambient
          توسط خط فرمان، به پوشه‌ی ریشه‌ی پروژه وارد شده و دو دستور فوق را صادر کنید. به این ترتیب فایل‌های d.ts. لازم، به پوشه‌ی typings پروژه اضافه می‌شوند.
          ب) تکمیل فایل typings.ts
          {
              "ambientDependencies": {
                   // ...
                  "jssha": "registry:dt/jssha#2.1.0+20160317120654"
              }
          }
          برای این منظور فایل typings.json را گشوده و سپس سطر جدید فوق را به آن اضافه کنید. اکنون اگر فایل package.json را یکبار دیگر ذخیره کنید و یا دستور npm install را صادر کنید، همان مراحل قسمت الف تکرار خواهند شد.

          پس از نصب فایل‌های typings این پروژه، به فایل main.ts مراجعه کرده و مدخل ذیل را به ابتدای آن اضافه کنید:
          /// <reference path="../typings/browser/ambient/jssha/index.d.ts" />
          اینکار سبب خواهد شد تا intellisense درون ویژوال استودیو بتواند مداخل متناظر را یافته و راهنمای مناسبی را ارائه دهد.

          در ادامه فایل جدید typed-sha.component.ts را با محتوای ذیل به پروژه اضافه کنید:
          import { Component, OnInit } from '@angular/core';
          //import { jsSHA } from "jssha";
          import * as jsSHA from "jssha"; // ===> var jsSHA = require("jssha"); // ===> loads `sha512.js` file dynamically using `systemjs.config.js` file definitions
           
          @Component({
              templateUrl: 'app/using-third-party-libraries/typed-sha.component.html'
          })
          export class TypedShaComponent implements OnInit{
              hash: String;
           
              ngOnInit(): void {
                  let shaObj = new jsSHA("SHA-512", "TEXT");
                  shaObj.update("This is a test");
                  this.hash = shaObj.getHash("HEX");
              }
          }
          محتویات فایل typed-sha.component.html با محتویات فایل untyped-sha.component.html که پیشتر عنوان شد، یکی است.
          در اینجا تنها نکته‌ی مهم و جدید نسبت به روش قبل (استفاده از jsSHA به صورت untyped)، روش import این کتابخانه است. روش "import * as jsSHA from "jssha به عبارت var jsSHA = require("jssha") ترجمه می‌شود که در نهایت سبب بارگذاری خودکار فایل‌های jssha بر اساس تعاریف مداخل آن در فایل systemjs.config.js می‌گردد.


          کدهای کامل این پروژه را از اینجا می‌توانید دریافت کنید.
          مطالب
          چگونه پروژه‌های Angular ی سبکی داشته باشیم - قسمت دوم
          همانطور که در مطلب قبل دیدیم، با اضافه کردن style ها  و فایل‌های Javascript ای حجم صفحات خروجی رو به افزایش بودند. اولین راه بهینه سازی، استفاده از feature module است. می‌خواهیم هر زمان که به ماژولی نیاز داریم، آن را import و استفاده کنیم.
          در ادامه دو فایل زیر را برای استفاده از ماژول‌های Angular Material و Kendo Angular UI در مسیر app\modules تعریف می‌کنیم.
          // angular-material-feature.module.ts
          
          import { NgModule } from '@angular/core';
          
          // Import angular kendo angular
          import {
            MatFormFieldModule, MatInputModule,
            MatButtonModule, MatButtonToggleModule,
            MatDialogModule, MatIconModule,
            MatSelectModule, MatToolbarModule,
            MatDatepickerModule,
            DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE, MatTableModule, MatCheckboxModule, MatRadioModule, MatCardModule, fadeInContent,
            MatListModule, MatProgressBarModule, MatTabsModule
          } from '@angular/material';
          
          import {
            MatSidenavModule,
            MatSlideToggleModule,
          } from '@angular/material';
          
          import { MatMenuModule } from '@angular/material/menu';
          
          @NgModule({
            declarations: [
             // KendoGridPaginationComponent
            ],
            imports: [
          
              MatSidenavModule,
              MatSlideToggleModule,
              MatInputModule,
              MatFormFieldModule,
              MatButtonModule, MatButtonToggleModule,
              MatDialogModule, MatIconModule,
              MatSelectModule, MatToolbarModule,
              MatDatepickerModule,
              MatCheckboxModule,
              MatRadioModule,
              MatCardModule,
              MatMenuModule,
              MatListModule,
              MatProgressBarModule,
              MatTabsModule
            ],
          
          
            exports: [
          
          
              MatSidenavModule,
              MatSlideToggleModule,
              MatInputModule,
              MatFormFieldModule,
              MatButtonModule, MatButtonToggleModule,
              MatDialogModule, MatIconModule,
              MatSelectModule, MatToolbarModule,
              MatDatepickerModule,
              MatCheckboxModule,
              MatRadioModule,
              MatCardModule,
              MatMenuModule,
              MatListModule,
              MatProgressBarModule,
              MatTabsModule
          
          
            ],
            providers: [
          
          
            ]
          
          })
          export class AngularMaterialFeatureModule {
          }
          و 
          // kendo-feature.module.ts
          
          import { NgModule } from '@angular/core';
          
          // Import kendo angular ui
          import { ButtonsModule } from '@progress/kendo-angular-buttons';
          import { GridModule, ExcelModule } from '@progress/kendo-angular-grid';
          import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
          import { InputsModule } from '@progress/kendo-angular-inputs';
          import { DateInputsModule } from '@progress/kendo-angular-dateinputs';
          import { DialogsModule } from '@progress/kendo-angular-dialog';
          import { RTL } from '@progress/kendo-angular-l10n';
          import { LayoutModule } from '@progress/kendo-angular-layout';
          
          import { WindowService, WindowRef, WindowCloseResult, DialogService, DialogRef, DialogCloseResult } from '@progress/kendo-angular-dialog';
          import { SnotifyModule, SnotifyService, SnotifyPosition, SnotifyToastConfig, ToastDefaults } from 'ng-snotify';
          
          
          @NgModule({
            declarations: [
             
            ],
            imports: [
          
          
              ButtonsModule,
              GridModule,
              ExcelModule,
              DropDownsModule,
              InputsModule,
              DateInputsModule,
              DialogsModule,
              LayoutModule,
          
              SnotifyModule,
              
          
          
            ],
            exports: [
          
              ButtonsModule,
              GridModule,
              ExcelModule,
              DropDownsModule,
              InputsModule,
              DateInputsModule,
              DialogsModule,
              LayoutModule,
          
            ],
            providers: [
          
              
              { provide: 'SnotifyToastConfig', useValue: ToastDefaults },
              SnotifyService,
          
          
              // Enable Right-to-Left mode for Kendo UI components
              { provide: RTL, useValue: true },
            ]
          
          })
          export class KendoFeatureModule {
          }

           از این پس، هر زمانیکه به ماژولی نیاز داریم، feature module آن را import می‌کنیم.

          در ادامه‌ی بهینه سازی، از قابلیت Lazy-Loading استفاده می‌کنیم و فایل ‌های زیر را جهت پیاده سازی lazy-loading تغییر می‌دهیم.
          محتویات فایل styles.scss را پاک می‌کنیم. 
          فایل app.module.ts : 
          // app.module.ts
          
          import { BrowserModule } from '@angular/platform-browser';
          import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
          import { NgModule } from '@angular/core';
          import { RouterModule } from '@angular/router';
          
          import { AppRoutingModule } from './app-routing.module';
          import { AppComponent } from './app.component';
          
          @NgModule({
            declarations: [
              AppComponent
            ],
            imports: [
              BrowserAnimationsModule,
              RouterModule,
              AppRoutingModule,
          
            ],
            providers: [],
            bootstrap: [AppComponent]
          })
          export class AppModule { }
           و فایل app.component.ts : 
          // app.component.ts
          
          import { Component, ViewEncapsulation } from '@angular/core';
          
          @Component({
            selector: 'app-root',
            templateUrl: './app.component.html',
            styleUrls: ['./app.component.scss'],
            encapsulation: ViewEncapsulation.None,
          })
          export class AppComponent {
            title = 'HowToKeepAngularDeploymentSizeSmall';
          
            constructor() {
          
            }
          
          }
           و  محتویات فایل app.component.scss  را پاک می‌کنیم. 
          و فایل app.component.html : 
          // app.component.html
          
          <router-outlet></router-outlet>
          و فایل app-routing.module.ts 
          // app-routing.module.ts
          
          import { NgModule } from '@angular/core';
          import { Routes, RouterModule } from '@angular/router';
          
          const routes: Routes = [
          
            { path: '', loadChildren: './projects/home/home.module#HomeModule' } 
          
          ];
          
          @NgModule({
            imports: [RouterModule.forRoot(routes)],
            exports: [RouterModule]
          })
          export class AppRoutingModule { }

          نکته 1: سعی کنید کامپوننت‌های کمتری را به app.module.ts اضافه کنید.
          نکته 2: stylesheet‌ها را در ماژول‌هایی که به آنها نیاز دارند import کنید.

          تا به اینجای کار، ماژول اصلی پروژه را سبک کردیم. حال می‌خواهیم صفحه‌ی اول پروژه را به module ای به نام home، در مسیر app\projects\home مسیردهی کنیم. 
          home.module.ts :
          // home.module.ts
          
          import { NgModule } from '@angular/core';
          import { CommonModule } from '@angular/common';
          
          import {AngularMaterialFeatureModule , KendoFeatureModule} from '../../modules/index';
          import {HomeRoutingModule } from './home.routing';
          
          import { HomeComponent } from './home.component';
          import { IndexComponent } from './pages/index/index.component';
          
          import {LoginComponent } from './pages/login/login.component';
          
          @NgModule({
          
            declarations: [
              HomeComponent,
               IndexComponent,
              LoginComponent
              ],
            imports: [
              CommonModule,
              HomeRoutingModule,
              AngularMaterialFeatureModule
            ],
            providers:[
          
            ],
            entryComponents:[
              LoginComponent
          
            ]
          
          })
          export class HomeModule { }
          home.routing.ts :
          // home.routing.ts
          
          import { NgModule } from '@angular/core';
          import { Routes, RouterModule } from '@angular/router';
          
          import { HomeComponent } from './home.component';
          import { IndexComponent } from './pages/index/index.component';
          
          const routes: Routes = [
            {
              path: "",
              component: HomeComponent,
              children: [
                {
                  path: "",
                  component: IndexComponent,
                  data: {
                    breadcrumb: " Index "
                  }
                },
                {
                  path: "about",
                  loadChildren: "./pages/aboutus/aboutus.module#AboutusModule"
                },
                {
                  path: "blog",
                  loadChildren: "./pages/blog/blog.module#BlogModule"
                },
                {
                  path: "blog-detail",
                  loadChildren: "./pages/blogdetail/blogdetail.module#BlogdetailModule"
                },
                {
                  path: "pricing",
                  loadChildren: "./pages/pricing/pricing.module#PricingModule"
                },
                {
                  path: "contact",
                  loadChildren: "./pages/contact/contact.module#ContactModule"
                }
              ]
            }
          ];
          
          @NgModule({
            imports: [RouterModule.forChild(routes)],
            exports: [RouterModule],
          })
          export class HomeRoutingModule  {
          }

          و فایل home.component.ts :
          // home.component.ts
          
          import { Component, OnInit, ViewEncapsulation } from '@angular/core';
          import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
          
          import { LoginComponent } from './pages/login/login.component';
          
          @Component({
            selector: 'app-home',
            templateUrl: './home.component.html',
            styleUrls: ['./home.component.scss'],
            encapsulation: ViewEncapsulation.None
          })
          export class HomeComponent implements OnInit {
          
            constructor(
              private dialog: MatDialog) { }
          
            ngOnInit() {
          
            }
          
          
            public toggleModal(){
              let dialogRef = this.dialog.open(LoginComponent, {
              });
            }
          
          }
          و فایل  home.component.scss : 
          // home.component.scss
          
          @import '../../../assets/home/css/themify-icons.css';
          @import '../../../assets/home/css/font-awesome.min.css';
          @import '../../../assets/home/css/set1.css';
          
          @import '../../../assets/home/css/bootstrap.min.css';
          @import '../../../assets/home/css/style.css';
          @import "~@angular/material/prebuilt-themes/indigo-pink.css";
          و فایل  home.component.html :
          // home.component.html
          
          <!--============================= HEADER =============================-->
          <div>
            <div>
              <div>
                <div>
                  <div>
                    <nav>
                      <a href="index.html"><img src="../../../assets/home/images/logo.png" alt="logo"></a>
                      <button type="button" data-toggle="collapse" data-target="#navbarNavDropdown"
                        aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
                        <span></span>
                      </button>
                      <div id="navbarNavDropdown">
                        <ul>
          
                          <li>
                            <a  target="_blank" [routerLink]="['/dashboard']">Dashboard</a>
                          </li>
          
          
                          <li>
                            <a [routerLink]="['/about']">About</a>
                          </li>
          
                          <li>
                            <a [routerLink]="['/contact']">Contact</a>
                          </li>
          
                          <li>
                            <a [routerLink]="['/pricing']">Pricing</a>
                          </li>
          
                          <li>
                            <a href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                              Blog
                              <span></span>
                            </a>
                            <div>
                              <a [routerLink]="['/blog']">Blog Listing</a>
                              <a [routerLink]="['/blog-detail']">Blog Detail</a>
                            </div>
                          </li>
                          <li>
                            <a (click)='toggleModal()' style="cursor: pointer;">Login</a>
                          </li>
                          <li><a href="add-listing.html"><span></span> Add
                              Listing</a></li>
                        </ul>
                      </div>
                    </nav>
                  </div>
                </div>
              </div>
            </div>
          </div>
          
          <!--//END HEADER -->
          
          <router-outlet></router-outlet>
          
          
          <!--============================= FOOTER =============================-->
          <footer>
            <div>
              <div>
                <div>
                  <div>
                    <i aria-hidden="true"></i>
                    <p>503 Sylvan Ave, Mountain View<br> CA 94041, United States</p>
                  </div>
                </div>
                <div>
                  <div>
                    <img src="../../../assets/home/images/footer-logo.png" alt="#">
                  </div>
                </div>
                <div>
                  <ul>
                    <li><a href="#"><i aria-hidden="true"></i></a></li>
                    <li><a href="#"><i aria-hidden="true"></i></a></li>
                    <li><a href="#"><i aria-hidden="true"></i></a></li>
                    <li><a href="#"><i aria-hidden="true"></i></a></li>
                    <li><a href="#"><i aria-hidden="true"></i></a></li>
                  </ul>
                </div>
              </div>
              <div>
                <div>
                  <div>
                    <p>Copyright © 2017 Listed Inc. All rights reserved</p>
                    <a href="#">Privacy</a>
                    <a href="#">Terms</a>
                  </div>
                </div>
              </div>
            </div>
          </footer>
          <!--//END FOOTER -->

          پروژه را با دستور ng serve --open --prod  اجرا کرده و خروجی را بررسی می‌کنیم. حجم خروجی صفحه اول را همراه با قالبی از پیش طراحی شده در تصویر زیر می‌بینیم:


          سورس کامل پروژه در HowToKeepAngularDeploymentSizeSmall قرار دارد.
          مطالب
          Angular Material 6x - قسمت پنجم - کار با Data Tables
          در این قسمت قصد داریم اطلاعات یادداشت‌های کاربران را توسط کامپوننت mat-table نمایش دهیم که به همراه قابلیت‌هایی مانند صفحه بندی، مرتب سازی و فیلتر کردن داده‌ها است.


          کامپوننت mat-table

          کار کامپوننت mat-table نمایش اطلاعات در ردیف‌ها و ستون‌ها است. به همراه آن mat-paginator برای نمایش UI صفحه بندی اطلاعات، دایرکتیو matSort و mat-sort-header برای افزودن رابط کاربری مرتب سازی اطلاعات و امکان تغییر منبع داده آن برای فیلتر کردن داده‌ها، نیز وجود دارند.


          افزودن کامپوننت جدید notes برای نمایش یادداشت‌های کاربران

          برای نمایش لیست یادداشت‌های هر شخص، کامپوننت جدید Notes را به صورت زیر در پوشه‌ی components ایجاد می‌کنیم:
           ng g c contact-manager/components/notes --no-spec
          علت اینجا است که نمی‌خواهیم کامپوننت نمایش جزئیات شخص را بیش از اندازه شلوغ کنیم. بنابراین به قالب کامپوننت main-content (فایل main-content.component.html) مراجعه کرده و selector این کامپوننت را در آنجا درج می‌کنیم:
                <mat-tab-group>
                  <mat-tab label="Bio">
                    <p>
                      {{user.bio}}
                    </p>
                  </mat-tab>
                  <mat-tab label="Notes">
                    <app-notes [notes]="user.userNotes"></app-notes>
                  </mat-tab>
                </mat-tab-group>
          همانطور که ملاحظه می‌کنید app-notes در برگه‌ی دوم کامپوننت mat-tab-group درج شده‌است. همچنین قصد داریم لیست userNotes جاری را به خاصیت notes آن نیز ارسال کنیم. به همین جهت به کامپوننت notes مراجعه کرده و این ورودی را ایجاد می‌کنیم:
          import { Component, Input, OnInit } from "@angular/core";
          import { UserNote } from "../../models/user-note";
          
          @Component({
            selector: "app-notes",
            templateUrl: "./notes.component.html",
            styleUrls: ["./notes.component.css"]
          })
          export class NotesComponent implements OnInit {
          
            @Input() notes: UserNote[];
          فعلا جهت بررسی صحت عملکرد آن به قالب این کامپوننت (فایل notes.component.html) مراجعه کرده و آن‌را به صورت json نمایش می‌دهیم:
           <p>
            {{notes | json}}
          </p>



          تکمیل کامپوننت Notes توسط یک data table

          در ادامه قصد داریم این اطلاعات خام را توسط یک data table نمایش دهیم. به همین جهت ابتدا به مستندات mat-table مراجعه کرده و همانند قبل، مثالی را پیدا می‌کنیم که به منظور ما نزدیک‌تر باشد. سپس کدهای آن‌را به برنامه اضافه کرده و سفارشی سازی می‌کنیم. در ابتدا مثال basic آن‌را دقیقا به همان نحوی که هست کپی کرده و سپس آن‌را تغییر می‌دهیم:
          محتوای فایل notes.component.ts
          import { Component, Input, OnInit } from "@angular/core";
          import { MatTableDataSource } from "@angular/material";
          
          import { UserNote } from "../../models/user-note";
          
          @Component({
            selector: "app-notes",
            templateUrl: "./notes.component.html",
            styleUrls: ["./notes.component.css"]
          })
          export class NotesComponent implements OnInit {
          
            @Input() notes: UserNote[];
          
            displayedColumns = ["position", "title", "date"];
            dataSource: MatTableDataSource<UserNote>;
          
            constructor() { }
          
            ngOnInit() {
              this.dataSource = new MatTableDataSource<UserNote>(this.notes);
            }
          
          }
          در اینجا برای نمایش یک mat-table، نیاز به یک منبع داده وجود دارد که روش تعریف آن‌را توسط MatTableDataSource از نوع UserNote مشاهده می‌کنید.
          سپس این منبع داده در قسمت ngOnInit بر اساس ورودی آرایه‌ی notes که از کامپوننت main-content مقدار دهی می‌شود، تامین خواهد شد.
          displayedColumns نیز لیست ستون‌ها را مشخص می‌کند.

          محتوای فایل notes.component.html
          <div class="example-container mat-elevation-z8" fxLayout="column">
            <mat-table #table [dataSource]="dataSource">
              <ng-container matColumnDef="position">
                <mat-header-cell *matHeaderCellDef> No. </mat-header-cell>
                <mat-cell *matCellDef="let note"> {{note.id}} </mat-cell>
              </ng-container>
          
              <ng-container matColumnDef="title">
                <mat-header-cell *matHeaderCellDef> Title </mat-header-cell>
                <mat-cell *matCellDef="let note"> {{note.title}} </mat-cell>
              </ng-container>
          
              <ng-container matColumnDef="date">
                <mat-header-cell *matHeaderCellDef> Date </mat-header-cell>
                <mat-cell *matCellDef="let note"> {{note.date | date:'yyyy-MM-dd'}} </mat-cell>
              </ng-container>
          
              <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
              <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
            </mat-table>
          </div>
          در اینجا ترتیب ردیف‌ها بر اساس mat-row انتهای جدول مشخص می‌شود. بنابراین مهم نیست که ng-container matColumnDef‌ها چه ترتیبی دارند.
          سپس به ازای هر ستون، یک ng-container اضافه شده‌است. matColumnDef معادل نام‌های displayedColumns خواهد بود. matCellDef نیز بر اساس متغیر حلقه‌ای که بر روی منبع داده تشکیل می‌شود، تعریف خواهد شد. این تعریف امکان دسترسی به مقدار آن‌را در ادامه میسر می‌کند.
          در این حالت اگر برنامه را اجرا کنیم، خروجی زیر قابل مشاهده خواهد بود:


          افزودن صفحه بندی به mat-table یادداشت‌های یک کاربر

          اگر مجددا به مستندات mat-table مراجعه کنیم، مثالی در مورد mat-paginator نیز دارد که جهت نمایش رابط کاربری صفحه بندی مورد استفاده قرار می‌گیرد. بنابراین از مثال آن جهت تکمیل این قسمت ایده می‌گیریم:
            </mat-table>
          
            <mat-paginator #paginator [pageSize]="2" [pageSizeOptions]="[2, 4, 6]">
            </mat-paginator>
          </div>
          پس از بسته شدن تگ mat-table، کامپوننت mat-paginator به صفحه اضافه می‌شود که pageSize آن تعداد ردیف‌های در هر صفحه را مشخص می‌کند و pageSizeOptions سبب نمایش یک دراپ داون برای انتخاب تعداد ردیف‌های هر صفحه توسط کاربر خواهد شد.
          در ادامه به کدهای کامپوننت مراجعه کرده و توسط ViewChild به template reference variable ایی به نام paginator دسترسی پیدا می‌کنیم:
          export class NotesComponent implements OnInit, AfterViewInit {
          
            dataSource: MatTableDataSource<UserNote>;
            
            @ViewChild(MatPaginator) paginator: MatPaginator;
          
            ngAfterViewInit() {
              this.dataSource.paginator = this.paginator;
            }
          
          }
          سپس مطابق مستندات آن، این کامپوننت باید به خاصیت paginator منبع داده‌ی data table در رخ‌داد ngAfterViewInit، متصل شود.
          اکنون اگر برنامه را اجرا کنیم، صفحه بندی فعال شده‌است:



          افزودن جستجو و فیلتر کردن اطلاعات به mat-table یادداشت‌های یک کاربر

          مستندات mat-table به همراه مثال filtering نیز هست که از آن جهت تکمیل این قسمت به نحو ذیل ایده خواهیم گرفت:
          ابتدا فیلد ورود اطلاعات جستجو، پیش از Mat-table به قالب کامپوننت اضافه می‌شود:
          <div class="example-container mat-elevation-z8" fxLayout="column">
            <div class="example-header">
              <mat-form-field>
                <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
              </mat-form-field>
            </div>
          سپس متد applyFilter که به ازای هر keyup فعال می‌شود، در کدهای کامپوننت به نحو زیر تکمیل خواهد شد:
            applyFilter(filterValue: string) {
              this.dataSource.filter = filterValue.trim().toLowerCase(); // MatTableDataSource defaults to lowercase matches
            }
          همین اندازه تنظیم سبب فعالسازی جستجو بر روی جدول می‌شود:



          افزودن مرتب سازی اطلاعات به mat-table یادداشت‌های یک کاربر

          مستندات mat-table به همراه مثال sorting نیز هست که از آن جهت تکمیل این قسمت به نحو ذیل ایده خواهیم گرفت:
          برای فعالسازی مرتب سازی اطلاعات، در قالب کامپوننت، به mat-table، دایرکتیو matSort و به هر ستونی که نیاز است مرتب سازی شود، دایرکتیو mat-sort-header را به mat-header‌ها اضافه می‌کنیم:
            <mat-table #table [dataSource]="dataSource" matSort>
              <ng-container matColumnDef="position">
                <mat-header-cell *matHeaderCellDef mat-sort-header> No. </mat-header-cell>
          در کدهای کامپوننت نیز ابتدا توسط ViewChild به matSort دسترسی پیدا می‌کنیم و سپس آن‌را به خاصیت sort منبع داده در رخ‌داد ngAfterViewInit، متصل خواهیم کرد:
          export class NotesComponent implements OnInit, AfterViewInit {
          
            dataSource: MatTableDataSource<UserNote>;
            
            @ViewChild(MatSort) sort: MatSort;
          
            ngAfterViewInit() {
              this.dataSource.sort = this.sort;
            }
          }
          نتیجه‌ی این تغییرات را در تصویر زیر با فعالسازی مرتب سازی بر روی ستون Title مشاهده می‌کنید:



          کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: MaterialAngularClient-04.zip
          برای اجرای آن:
          الف) ابتدا به پوشه‌ی src\MaterialAngularClient وارد شده و فایل‌های restore.bat و ng-build-dev.bat را اجرا کنید.
          ب) سپس به پوشه‌ی src\MaterialAspNetCoreBackend\MaterialAspNetCoreBackend.WebApp وارد شده و فایل‌های restore.bat و dotnet_run.bat را اجرا کنید.
          اکنون برنامه در آدرس https://localhost:5001 قابل دسترسی است.