مسیرراه‌ها
Entity framework code-first
شروع به کار با EF Code first

برای تکمیل بحث نیاز است تغییرات انجام شده از نگارش 4 به 6 را نیز مد نظر داشته باشید:


آشنایی با مباحث Migrations



آشنایی با تنظیمات نگاشت‌ها به دو روش استفاده از ویژگی‌ها و Fluent API



اعتبارسنجی و بررسی استثناءها



ردیابی تغییرات



استفاده از SQL خام و بانک‌های اطلاعاتی متفاوت

      نکات مهم کوئری نویسی در EF



      استفاده از EF در WPF


      لایه بندی پروژه‌های EF Code first



      پروژ‌ه‌های انجام شده با EF Code first

       
      مطالب
      شروع به کار با AngularJS 2.0 و TypeScript - قسمت دهم - کار با فرم‌ها - قسمت اول
      هر برنامه‌ی وبی، نیاز به کار با فرم‌های وب را دارد و به همین جهت، AngularJS 2.0 به همراه دو نوع از فرم‌ها است: فرم‌های مبتنی بر قالب‌ها و فرم‌های مبتنی بر مدل‌ها.
      کار با فرم‌های مبتنی بر قالب‌ها ساده‌تر است؛ اما کنترل کمتری را بر روی مباحث اعتبارسنجی داده‌های ورودی توسط کاربر، در اختیار ما قرار می‌دهند. اما فرم‌های مبتنی بر مدل‌ها هر چند به همراه اندکی کدنویسی بیشتر هستند، اما کنترل کاملی را جهت اعتبارسنجی ورودی‌های کاربران، ارائه می‌دهند. در این قسمت فرم‌های مبتنی بر قالب‌ها (Template-driven forms) را بررسی می‌کنیم.


      ساخت فرم مبتنی بر قالب‌های ثبت یک محصول جدید

      در ادامه‌ی مثال این سری، می‌خواهیم به کاربران، امکان ثبت اطلاعات یک محصول جدید را نیز بدهیم. به همین جهت فایل‌های جدید product-form.component.ts و product-form.component.html را به پوشه‌ی App\products برنامه اضافه می‌کنیم (جهت تعریف کامپوننت فرم جدید به همراه قالب HTML آن).
      الف) محتوای کامل product-form.component.html
      <form #f="ngForm" (ngSubmit)="onSubmit(f.form)">
          <div class="panel panel-default">
              <div class="panel-heading">
                  <h3 class="panel-title">
                      Add Product
                  </h3>
              </div>
              <div class="panel-body form-horizontal">
                  <div class="form-group">
                      <label for="productName" class="col col-md-2 control-label">Name</label>
                      <div class="controls col col-md-10">
                          <input ngControl="productName" id="productName" required
                                 #productName="ngForm"
                                 (change)="log(productName)"
                                 minlength="3"
                                 type="text" class="form-control"
                                 [(ngModel)]="productModel.productName"/>
                          <div *ngIf="productName.touched && productName.errors">
                              <label class="text-danger" *ngIf="productName.errors.required">
                                  Name is required.
                              </label>
                              <label class="text-danger" *ngIf="productName.errors.minlength">
                                  Name should be minimum {{ productName.errors.minlength.requiredLength }} characters.
                              </label>
                          </div>
                      </div>
                  </div>
                  <div class="form-group">
                      <label for="productCode" class="col col-md-2 control-label">Code</label>
                      <div class="controls col col-md-10">
                          <input ngControl="productCode" id="productCode" required
                                 #productCode="ngForm"
                                 type="text" class="form-control"
                                 [(ngModel)]="productModel.productCode"/>
                          <label class="text-danger" *ngIf="productCode.touched && !productCode.valid">
                              Code is required.
                          </label>
                      </div>
                  </div>
                  <div class="form-group">
                      <label for="releaseDate" class="col col-md-2 control-label">Release Date</label>
                      <div class="controls col col-md-10">
                          <input ngControl="releaseDate" id="releaseDate" required
                                 #releaseDate="ngForm"
                                 type="text" class="form-control"
                                 [(ngModel)]="productModel.releaseDate"/>
                          <label class="text-danger" *ngIf="releaseDate.touched && !releaseDate.valid">
                              Release Date is required.
                          </label>
                      </div>
                  </div>
                  <div class="form-group">
                      <label for="price" class="col col-md-2 control-label">Price</label>
                      <div class="controls col col-md-10">
                          <input ngControl="price" id="price" required
                                 #price="ngForm"
                                 type="text" class="form-control"
                                 [(ngModel)]="productModel.price"/>
                          <label class="text-danger" *ngIf="price.touched && !price.valid">
                              Price is required.
                          </label>
                      </div>
                  </div>
                  <div class="form-group">
                      <label for="description" class="col col-md-2 control-label">Description</label>
                      <div class="controls col col-md-10">
                          <textarea ngControl="description" id="description" required
                                    #description="ngForm"
                                    rows="10" type="text" class="form-control"
                                    [(ngModel)]="productModel.description"></textarea>
                          <label class="text-danger" *ngIf="description.touched && !description.valid">
                              Description is required.
                          </label>
                      </div>
                  </div>
                  <div class="form-group">
                      <label for="imageUrl" class="col col-md-2 control-label">Image</label>
                      <div class="controls col col-md-10">
                          <input ngControl="imageUrl" id="imageUrl" required
                                 #imageUrl="ngForm"
                                 type="text" class="form-control"
                                 [(ngModel)]="productModel.imageUrl"/>
                          <label class="text-danger" *ngIf="imageUrl.touched && !imageUrl.valid">
                              Image is required.
                          </label>
                      </div>
                  </div>
              </div>
              <footer class="panel-footer">
                  <button [disabled]="!f.valid"
                          type="submit" class="btn btn-primary">
                      Submit
                  </button>
              </footer>
          </div>
      </form>

      ب) محتوای کامل product-form.component.ts
      import { Component } from 'angular2/core';
      import { Router } from 'angular2/router';
      import { IProduct } from './product';
      import { ProductService } from './product.service';
       
      @Component({
          //selector: 'product-form',
          templateUrl: 'app/products/product-form.component.html'
      })
      export class ProductFormComponent {
       
          productModel = <IProduct>{}; // creates an empty object of an interface
       
          constructor(private _productService: ProductService, private _router: Router) { }
       
          log(productName): void {
              console.log(productName);
          }
       
          onSubmit(form): void {
              console.log(form);
              console.log(this.productModel);
       
              this._productService.addProduct(this.productModel)
                  .subscribe((product: IProduct) => {
                      console.log(`ID: ${product.productId}`);
                      this._router.navigate(['Products']);
                  });
          }
      }

      اکنون ریز جزئیات و تغییرات این دو فایل را قدم به قدم بررسی خواهیم کرد.

      تا اینجا در فایل product-form.component.html یک فرم ساده‌ی HTML ایی مبتنی بر بوت استرپ 3 را تهیه کرده‌ایم. نکات ابتدایی آن، دقیقا مطابق است با مستندات بوت استرپ 3؛ از لحاظ تعریف form-horizontal و سپس ایجاد یک div با کلاس form-group و قرار دادن المان‌هایی با کلاس‌های form-control در آن. همچنین برچسب‌های تعریف شده‌ی با ویژگی for، در این المان‌ها، جهت بالارفتن دسترسی پذیری به عناصر فرم، اضافه شده‌اند. این مراحل در مورد تمام فرم‌های استاندارد وب صادق هستند و نکته‌ی جدیدی ندارند.

      در ادامه تعاریف AngularJS 2.0 را به این فرم اضافه کرد‌ه‌ایم. در اینجا هر کدام از المان‌های ورودی، تبدیل به Controlهای AngularJS 2.0 شده‌اند. کلاس Control، خواص ویژه‌ای را در اختیار ما قرار می‌دهد. برای مثال value یا مقدار این المان چیست؟ وضعیت touched و untouched آن چیست؟ (آیا کاربر فوکوس را به آن منتقل کرده‌است یا خیر؟) آیا dirty است؟ (مقدار آن تغییر کرده‌است؟) و یا شاید هم pristine است؟ (مقدار آن تغییری نکرده‌است). علاوه بر این‌ها دارای خاصیت valid نیز می‌باشد (آیا اعتبارسنجی آن موفقیت آمیز است؟)؛ به همراه خاصیت errors که مشکلات اعتبارسنجی موجود را باز می‌گرداند.
      <div class="form-group">
          <label for="description" class="col col-md-2 control-label">Description</label>
          <div class="controls col col-md-10">
              <textarea ngControl="description" id="description" required
                        #description="ngForm"
                        rows="10" type="text" class="form-control"
                        [(ngModel)]="productModel.description"></textarea>
              <label class="text-danger" *ngIf="description.touched && !description.valid">
                  Description is required.
              </label>
          </div>
      </div>
      در اینجا کلاس مفید دیگری به نام ControlGroup نیز درنظر گرفته شده‌است. برای مثال هر فرم، یک ControlGroup است (گروهی متشکل از کنترل‌ها، در صفحه). البته می‌توان یک فرم بزرگ را به چندین ControlGroup نیز تقسیم کرد. تمام خواصی که برای کلاس Control ذکر شدند، در مورد کلاس ControlGroup نیز صادق هستند. با این تفاوت که این‌بار اگر به خاصیت valid آن مراجعه کردیم، یعنی تمام کنترل‌های قرار گرفته‌ی در آن گروه معتبر هستند و نه صرفا یک تک کنترل خاص. به همین ترتیب خاصیت errors نیز تمام خطاهای اعتبارسنجی یک گروه را باز می‌گرداند.
      هر دو کلاس Control و ControlGroup از کلاس پایه‌ای به نام AbstractControl مشتق شده‌اند و این کلاس پایه است که خواص مشترک یاد شده را به همراه دارد.

      بنابراین برای کار ساده‌تر با یک فرم AngularJS 2.0، کل فرم را تبدیل به یک ControlGroup کرده و سپس هر کدام از المان‌های ورودی را تبدیل به یک Control مجزا می‌کنیم. کار برقراری این ارتباط، با استفاده از دایرکتیو ویژه‌ای به نام ngControl انجام می‌شود. بنابراین دایرکتیو ngControl، با نامی دلخواه و معین، به تمام المان‌های ورودی، انتساب داده شده‌است.
      هرچند در این مثال نام ngControl‌ها با مقدار id هر کنترل یکسان درنظر گرفته شده‌است، اما ارتباطی بین این دو نیست. مقدار id جهت استفاده‌ی در DOM کاربرد دارد و مقدار ngControl توسط AngularJS 2.0 استفاده می‌شود. جهت رسیدن به کدهایی یکدست، بهتر است این نام‌ها را یکسان درنظر گرفت؛ اما هیچ الزامی هم ندارد.

      برای بررسی جزئیات این اشیاء کنترل، در المان productName، یک متغیر محلی را به نام productName# تعریف کرده‌ایم و آن‌را به دایرکتیو ngControl انتساب داده‌ایم. این انتساب توسط ngForm انجام شده‌است. زمانیکه AngularJS 2.0 یک متغیر محلی تنظیم شده‌ی به ngForm را مشاهده می‌کند، آن‌را به صورت خودکار به ngControl همان المان ورودی متصل می‌کند. سپس این متغیر محلی را به متد log ارسال کرده‌ایم. این متد در کلاس کامپوننت جاری تعریف شده‌است و کار آن نمایش شیء Control جاری در کنسول developer tools مرورگر است.
      <input ngControl="productName" id="productName" required
             #productName="ngForm"
             (change)="log(productName)"
             minlength="3"
             type="text" class="form-control"
             [(ngModel)]="productModel.productName"/>


      همانطور که در تصویر مشاهده می‌کنید، عناصر یک شیء Control، در کنسول نمایش داده شده‌اند و در اینجا بهتر می‌توان خواصی مانند valid و امثال آن‌را که به همراه این کنترل وجود دارند، مشاهده کرد. برای مثال خاصیت dirty آن true است چون مقدار آن المان ورودی، تغییر کرده‌است.

      بنابراین تا اینجا با استفاده از دایرکتیو ngControl، یک المان ورودی را به یک شیء Control متصل کردیم. همچنین نحوه‌ی تعریف یک متغیر محلی را در المانی و سپس ارسال آن را به کلاس متناظر با کامپوننت فرم، نیز بررسی کردیم.


      افزودن اعتبارسنجی به فرم ثبت محصولات

      به کنترل‌هایی که به صورت فوق توسط ngControl ایجاد می‌شوند، اصطلاحا implicitly created controls می‌گویند؛ یا به عبارتی ایجاد آن‌ها به صورت «ضمنی» توسط AngularJS 2.0 انجام می‌شود که نمونه‌ای از آن‌را در تصویر فوق نیز مشاهده کردید. این نوع کنترل‌های ضمنی، امکانات اعتبارسنجی محدودی را در اختیار دارند؛ که تنها سه مورد هستند:
      الف) required
      ب) minlength
      ج) maxlength

      این‌ها ویژگی‌های استاندارد اعتبارسنجی HTML 5 نیز هستند. نمونه‌ای از اعمال این موارد را با افزودن ویژگی required، به المان‌های فرم ثبت محصولات فوق، مشاهده می‌کنید.
      سپس نیاز داریم تا خطاهای اعتبارسنجی را در مقابل هر المان ورودی نمایش دهیم.
      <textarea ngControl="description" id="description" required
                #description="ngForm"
                rows="10" type="text" class="form-control"></textarea>
      <div class="alert alert-danger" *ngIf="description.touched && !description.valid">
          Description is required.
      </div>
      پس از افزودن ویژگی required به یک المان، افزودن و نمایش خطاهای اعتبارسنجی، شامل سه مرحله‌ی زیر است:
      الف) ایجاد یک div ساده جهت نمایش پیام خطای اعتبار سنجی
      ب) افزودن یک متغیر محلی با # و تنظیم شده‌ی به ngForm، جهت دسترسی به شیء کنترل ایجاد شده
      ج) استفاده از این متغیر محلی در دایرکتیو ساختاری ngIf* جهت دسترسی به خاصیت valid آن کنترل. بر مبنای مقدار این خاصیت است که تصمیم گرفته می‌شود، پیام اجباری بودن پر کردن فیلد نمایش داده شود یا خیر.
      در اینجا یک سری کلاس بوت استرپ 3 هم جهت نمایش بهتر این پیام خطای اعتبارسنجی، اضافه شده‌اند.

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



      بهبود شیوه نامه‌ی پیش فرض المان‌های ورودی اطلاعات در AngularJS 2.0

      می‌خواهیم اگر اعتبارسنجی یک المان ورودی با شکست مواجه شد، یک حاشیه‌ی قرمز، در اطراف آن نمایش داده شود. این مورد را با توجه به اینکه AngularJS 2.0، شیوه نامه‌های ویژه‌ای را به صورت خودکار به المان‌ها اضافه می‌کند، می‌توان به صورت سراسری به تمام فرم‌ها اضافه کرد. برای این منظور فایل app.component.css واقع در ریشه‌ی پوشه‌ی app را گشوده و تنظیمات ذیل را به آن اضافه کنید:
      .ng-touched.ng-invalid{
          border: 1px solid red;
      }

      ویژگی‌های اضافه شده‌ی در حالت شکست اعتبارسنجی؛ مانند ng-invalid


      ویژگی‌های اضافه شده‌ی در حالت موفقیت اعتبارسنجی؛ مانند ng-valid



      مدیریت چندین ویژگی اعتبارسنجی یک المان با هم

      گاهی از اوقات نیاز است برای یک المان ورودی، چندین نوع اعتبارسنجی مختلف را تعریف کرد. برای مثال فرض کنید که ویژگی‌های required و همچنین minlength، برای نام محصول تنظیم شده‌اند. در این حالت ذکر productName.valid خیلی عمومی است و هر دو حالت اجباری بودن فیلد و حداقل طول آن‌را با هم به همراه دارد:
      <div class="alert alert-danger" *ngIf="productName.touched && !productName.valid">
         Name is required.
      </div>
      بنابراین در این حالت از روش ذیل استفاده می‌شود:
      <div *ngIf="productName.touched && productName.errors">
          <div class="alert alert-danger" *ngIf="productName.errors.required">
              Name is required.
          </div>
          <div class="alert alert-danger" *ngIf="productName.errors.minlength">
              Name should be minimum 3 characters.
          </div>
      </div>
      خاصیت errors نیز یکی دیگر از خواص شیء کنترل است. اگر نال بود، یعنی خطایی وجود ندارد و در غیراینصورت، به ازای هر نوع اعتبارسنجی تعریف شده، خواصی به آن اضافه می‌شوند. بنابراین ذکر productName.errors.required به این معنا است که آیا خاصیت errors، دارای کلیدی به نام required است؟ اگر بله، یعنی این فیلد هنوز پر نشده‌است.
      همچنین چون در این حالت productName.touched نیاز است چندین بار تکرار شود، می‌توان آن‌را در یک div محصور کننده‌ی دو div مورد نیاز جهت نمایش خطاهای اعتبارسنجی قرار داد. به علاوه بررسی نال نبودن productName.errors نیز در div محصور کننده صورت گرفته‌است و دیگر نیازی نیست این بررسی را به ngIfهای داخلی اضافه کرد.

      نکته 1
      اگر علاقمند بودید تا جزئیات خاصیت errors را مشاهده کنید، آن‌را می‌توان توسط pipe توکاری به نام json به صورت موقت نمایش داد و بعد آن‌را حذف کرد:
       <div *ngIf="productName.touched && productName.errors">
        {{ productName.errors | json }}

      نکته 2
      بجای ذکر مستقیم عدد سه در «minimum 3 characters»، می‌توان این عدد را مستقیما از تعریف ویژگی minlength نیز استخراج کرد:
       Name should be minimum {{ productName.errors.minlength.requiredLength }} characters.


      بررسی ngForm

      شبیه به ngControl که یک المان ورودی را به یک کنترل AngularJS 2.0 متصل می‌کند، دایرکتیو دیگری نیز به نام ngForm وجود دارد که کل فرم را به شیء ControlGroup بایند می‌کند و برخلاف ngControl، نیازی به ذکر صریح آن وجود ندارد. هر زمانیکه AngularJS 2.0، المان استاندارد فرمی را در صفحه مشاهده می‌کند، این اتصالات را به صورت خودکار برقرار خواهد کرد.
      ngForm دارای خاصیتی است به نام ngSumbit که از نوع EventEmitter است (نمونه‌ای از آن را در مبحث کامپوننت‌های تو در تو پیشتر ملاحظه کرده‌اید). بنابراین از آن می‌توان جهت اتصال رخداد submit فرم، به متدی در کلاس کامپوننت خود، استفاده کرد. متد متصل به این رخداد، زمانی فراخوانی می‌شود که کاربر بر روی دکمه‌ی submit کلیک کند:
       <form #f="ngForm" (ngSubmit)="onSubmit(f.form)">
      همچنین در اینجا متغیر محلی f جهت دسترسی به شیء ControlGroup و ارسال آن به متد onSubmit تعریف شده‌است (شبیه به متغیرهای محلی دسترسی به ngControl که پیشتر جهت نمایش خطاهای اعتبارسنجی، اضافه کردیم).

      پس از تعریف این رخداد و اتصال آن در قالب کامپوننت، اکنون می‌توان متد onSubmit را در کلاس آن نیز اضافه کرد.
      onSubmit(form): void {
         console.log(form);
      }
      فعلا هدف از این متد، نمایش جزئیات شیء form دریافتی، در کنسول developer tools است.



      غیرفعال کردن دکمه‌ی submit در صورت وجود خطاهای اعتبارسنجی

      در قسمت بررسی ngForm، یک متغیر محلی را به نام f ایجاد کردیم که به شیء ControlGroup فرم جاری اشاره می‌کند. از این متغیر و خاصیت valid آن می‌توان با کمک property binding به خاصیت disabled یک دکمه، آن‌را به صورت خودکار فعال یا غیرفعال کرد:
      <button [disabled]="!f.valid"
              type="submit" class="btn btn-primary">
          Submit
      </button>
      هر زمانیکه کل فرم از لحاظ اعتبارسنجی مشکلی نداشته باشد، دکمه‌ی submit فعال می‌شود و برعکس.



      نمایش فرم افزودن محصولات توسط سیستم Routing

      با نحوه‌ی تعریف مسیریابی‌ها در قسمت قبل آشنا شدیم. برای نمایش فرم افزودن محصولات، می‌توان تغییرات ذیل را به فایل app.component.ts اعمال کرد:
      //same as before...
      import { ProductFormComponent }  from './products/product-form.component';
       
      @Component({
          //same as before…
          template: `
                      //same as before…
                          <li><a [routerLink]="['AddProduct']">Add Product</a></li>
                     //same as before…
          `,
          //same as before…
      })
      @RouteConfig([
          //same as before…
          { path: '/addproduct', name: 'AddProduct', component: ProductFormComponent }
      ])
      //same as before...
      ابتدا به RouteConfig، مسیریابی کامپوننت فرم افزودن محصولات اضافه شده‌است. سپس ماژول این کلاس در ابتدای فایل import شده و در آخر routerLink آن به قالب سایت و منوی بالای سایت اضافه شده‌است.



      اتصال المان‌های فرم به مدلی جهت ارسال به سرور

      برای اتصال المان‌های فرم به یک مدل، این مدل را به صورت یک خاصیت عمومی، در سطح کلاس کامپوننت فرم، تعریف می‌کنیم:
       productModel = <IProduct>{}; // creates an empty object of an interface
      اگر از اینترفیسی مانند IProduct که در قسمت‌های قبل این سری تعریف شد، نیاز است شیء جدیدی ساخته شود، الزاما نیازی نیست تا یک کلاس جدید را از آن مشتق کرد و بعد متغیر new ClassName را تهیه کرد. در TypeScript می‌توان به صورت خلاصه از syntax فوق نیز استفاده کرد.
      پس از تعریف خاصیت productModel، اکنون کافی است با استفاده از two-way data binding، آن‌را به المان‌های فرم نسبت دهیم. برای مثال:
      <textarea ngControl="description" id="description" required
                #description="ngForm"
                rows="10" type="text" class="form-control"
                [(ngModel)]="productModel.description"></textarea>
      در اینجا با استفاده از ngModel و انقیاد دو طرفه، کار اتصال به خاصیت توضیحات شیء محصول انجام شده‌است. اکنون بلافاصله تغییرات اعمالی به فرم، به مدل متناظر منعکس می‌شود و برعکس. این ngModel را به تمام المان‌های ورودی فرم متصل خواهیم کرد.
      پس از تعریف یک چنین اتصالی، دیگر نیازی به مقدار دهی پارامتر onSubmit(f.form) نیست. زیرا شیء productModel، در متد onSumbit در دسترس است و این شیء همواره حاوی آخرین تغییرات اعمالی به المان‌های فرم است.

      پس از اینکه فرم را به مدل آن متصل کردیم، فایل product.service.ts را گشوده و متد جدید addProduct را به آن اضافه کنید:
      addProduct(product: IProduct): Observable<IProduct> {
          let headers = new Headers({ 'Content-Type': 'application/json' }); // for ASP.NET MVC
              let options = new RequestOptions({ headers: headers });
       
          return this._http.post(this._addProductUrl, JSON.stringify(product), options)
              .map((response: Response) => <IProduct>response.json())
              .do(data => console.log("Product: " + JSON.stringify(data)))
              .catch(this.handleError);
      }
      کار این متد، ارسال شیء محصول به یک اکشن متد برنامه‌ی ASP.NET MVC جاری است. با جزئیات کار با obsevables درمطلب «دریافت اطلاعات از سرور» پیشتر آشنا شده‌ایم.
      نکته‌ی مهم اینجا است که content type پیش فرض ارسالی متد post آن، plain text است و در این حالت ASP.NET MVC شیء JSON دریافتی از کلاینت را پردازش نخواهد کرد. بنابراین نیاز است تا هدر content type را به صورت صریحی در اینجا ذکر نمود؛ در غیراینصورت در سمت سرور، شاهد نال بودن مقادیر دریافتی از کاربران خواهیم بود.
      امضای سمت سرور متد دریافت اطلاعات از کاربر، چنین شکلی را دارد (تعریف شده در فایل Controllers\HomeController.cs):
       [HttpPost]
      public ActionResult AddProduct(Product product)
      {

      اشیاء هدرها و تنظیمات درخواست، در متد addProduct سرویس ProductService، در ماژول‌های ذیل تعریف شده‌اند که باید به ابتدای فایل product.service.ts اضافه شوند:
       import { Headers, RequestOptions } from 'angular2/http';

      پس از تعریف متد addProduct در سرویس ProductService، اکنون با استفاده از ترزیق این سرویس به سازنده‌ی کلاس فرم ثبت یک محصول جدید، می‌توان متد this._productService.addProduct را جهت ارسال productModel به سمت سرور، در متد onSubmit فراخوانی کرد:
      //Same as before…
      import { IProduct } from './product';
      import { ProductService } from './product.service';
       
      @Component({
      //Same as before…
      })
      export class ProductFormComponent {
       
          productModel = <IProduct>{}; // creates an empty object of an interface
       
          constructor(private _productService: ProductService, private _router: Router) { }
       
          //Same as before… 
      
          onSubmit(form): void {
              console.log(form);
              console.log(this.productModel);
       
              this._productService.addProduct(this.productModel)
                  .subscribe((product: IProduct) => {
                      console.log(`ID: ${product.productId}`);
                      this._router.navigate(['Products']);
                  });
          }
      }
      همانطور که ذکر شد، از آنجائیکه شیء productModel حاوی آخرین تغییرات اعمالی توسط کاربر است، اکنون می‌توان پارامتر form متد onSubmit را حذف کرد.
      در اینجا پس از فراخوانی متد addProduct، متد subscribe، در انتهای زنجیره، فراخوانی شده‌است. کار آن هدایت کاربر به صفحه‌ی نمایش لیست محصولات است. در اینجا this._router از طریق تزریق وابستگی‌های سرویس مسیریاب به سازنده‌ی کلاس، تامین شده‌است. نمونه‌ی آن‌را در قسمت «افزودن دکمه‌ی back با کدنویسی» مربوطه به مطلب آشنایی با مسیریابی، پیشتر مطالعه کرده‌اید.



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


      خلاصه‌ی بحث

      فرم‌های template driven در AngularJS 2.0 به این نحو طراحی می‌شوند:
       1) ابتدا فرم HTML را به حالت معمولی آن طراحی می‌کنیم؛ با تمام المان‌های آن.
       2) به تمام المان‌های فرم، دیراکتیو ngControl را متصل می‌کنیم، تا AngularJS 2.0 آن‌را تبدیل به یک کنترل خاص خودش کند. کنترلی که دارای خواصی مانند valid و touched است.
       3) سپس برای دسترسی به این کنترل ایجاد شده‌ی به صورت ضمنی، یک متغیر محلی آغاز شده‌ی با # را به تمام المان‌ها اضافه می‌کنیم.
       4) اعتبارسنجی‌هایی را مانند required  به المان‌های فرم اضافه می‌کنیم.
       5) از متغیر محلی تعریف شده و ngIf* برای بررسی خواصی مانند valid و touched برای نمایش خطاهای اعتبارسنجی کمک گرفته می‌شود.
       6) پس از تعریف فرم، تعریف ngControlها، تعریف متغیر محلی شروع شده‌ی با # و افزودن خطاهای اعتبارسنجی، اکنون نوبت به ارسال این اطلاعات به سرور است. بنابراین رخداد ngSubmit را باید به متدی در کلاس کامپوننت جاری متصل کرد.
       7) اکنون که با کلیک بر روی دکمه‌ی submit فرم، متد onSubmit متصل به ngSubmit فراخوانی می‌شود، نیاز است بین المان‌های فرم HTML و کلاس کامپوننت، ارتباط برقرار کرد. این‌کار را توسط two-way data binding و تعریف ngModel بر روی تمام المان‌های فرم، انجام می‌دهیم. این ngModelها، به یک خاصیت عمومی که متناظر است با وهله‌ای از شیء مدل فرم، متصل هستند. بنابراین این مدل، در هر لحظه، بیانگر آخرین تغییرات کاربر است و از آن می‌توان برای ارسال اطلاعات به سرور استفاده کرد.
       8) پس از اتصال فرم به کلاس متناظر با آن، اکنون سرویس محصولات را تکمیل کرده و به آن متد HTTP Post را جهت ارسال اطلاعات سمت کاربر، به سرور، اضافه می‌کنیم. در اینجا نکته‌ی مهم، تنظیم content type ارسالی به سمت سرور است. در غیراینصورت فریم ورک سمت سرور قادر به تشخیص JSON بودن این اطلاعات نخواهد شد.
      مطالب
      React 16x - قسمت 17 - مسیریابی - بخش 3 - یک تمرین
      به عنوان تمرین، همان برنامه‌ی طراحی گریدی را که تا قسمت 14 تکمیل کردیم، با معرفی مسیریابی بهبود خواهیم بخشید. برای این منظور یک NavBar بوت استرپی را به بالای صفحه اضافه می‌کنیم که دارای سه لینک movies ،customers و rentals است. به همین جهت نیاز به دو کامپوننت مقدماتی customers و rentals نیز وجود دارد که تنها یک h1 را نمایش می‌دهند. به علاوه منوی راهبری برنامه نیز باید بر اساس مسیر فعال جاری، با رنگ مشخصی، فعال بودن مسیریابی گزینه‌ی انتخابی را مشخص کند. در این برنامه اگر کاربر، آدرس نامعتبری را وارد کرد، باید به صفحه‌ی not-found هدایت شود. همچنین می‌خواهیم تمام عناوین فیلم‌های نمایش داده شده‌ی در جدول، تبدیل به لینک‌هایی به صفحه‌ی جدید جزئیات آن‌ها شوند. در این صفحه باید یک دکمه‌ی Save هم وجود داشته باشد تا با کلیک بر روی آن، به صورت خودکار به صفحه‌ی movies هدایت شویم.


      برپایی پیش‌نیازها

      ابتدا کتابخانه‌ی react-router-dom را نصب می‌کنیم:
       npm i react-router-dom --save
      سپس کامپوننت App را با BrowserRouter آن در فایل index.js محصور می‌کنیم؛ تا کار انتقال مدیریت تاریخچه‌ی مرور صفحات در مرورگر، به درخت کامپوننت‌های React انجام شود:
      import { BrowserRouter } from "react-router-dom";
      
      //...
      
      ReactDOM.render(
        <BrowserRouter>
          <App />
        </BrowserRouter>,
        document.getElementById("root")
      );


      ایجاد کامپوننت‌های جدید مورد نیاز

      برای تکمیل نیازمندی‌هایی که در مقدمه عنوان شد، این کامپوننت‌های جدید را ایجاد می‌کنیم:
      کامپوننت بدون حالت تابعی src\components\customers.jsx با این محتوا:
      import React from "react";
      
      const Customers = () => {
        return <h1>Customers</h1>;
      };
      
      export default Customers;

      کامپوننت بدون حالت تابعی src\components\rentals.jsx با این محتوا:
      import React from "react";
      
      const Rentals = () => {
        return <h1>Rentals</h1>;
      };
      
      export default Rentals;

      کامپوننت بدون حالت تابعی src\components\notFound.jsx با این محتوا:
      import React from "react";
      
      const NotFound = () => {
        return <h1>Not Found</h1>;
      };
      
      export default NotFound;

      کامپوننت بدون حالت تابعی src\components\movieForm.jsx با این محتوا:
      import React from "react";
      
      const MovieForm = () => {
        return (
          <div>
            <h1>Movie Form</h1>
            <button className="btn btn-primary">Save</button>
          </div>
        );
      };
      
      export default MovieForm;


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

      پس از نصب کتابخانه‌ی مسیریابی و راه اندازی آن، اکنون نوبت به تعریف مسیریابی‌های مورد نیاز برنامه در فایل app.js است:
      import "./App.css";
      
      import React from "react";
      import { Redirect, Route, Switch } from "react-router-dom";
      
      import Customers from "./components/customers";
      import Movies from "./components/movies";
      import NotFound from "./components/notFound";
      import Rentals from "./components/rentals";
      
      function App() {
        return (
          <main className="container">
            <Switch>
              <Route path="/movies" component={Movies} />
              <Route path="/customers" component={Customers} />
              <Route path="/rentals" component={Rentals} />
              <Route path="/not-found" component={NotFound} />
              <Redirect to="/not-found" />
            </Switch>
          </main>
        );
      }
      
      export default App;
      - در اینجا ابتدا چهار مسیریابی جدید را جهت نمایش صفحات کامپوننت‌هایی که ایجاد کردیم، تعریف و سپس نکته‌ی «مدیریت مسیرهای نامعتبر درخواستی» قسمت قبل را نیز با افزودن کامپوننت Redirect، پیاده سازی کرده‌ایم. به علاوه پیشتر نمایش کامپوننت Movies را داخل container تعریف شده داشتیم که اکنون با وجود این مسیریابی‌ها، نیازی به تعریف المان آن نیست و از return تعریف شده، حذف شده‌است.
      تا اینجا اگر برنامه را اجرا کنیم، بلافاصله به http://localhost:3000/not-found هدایت می‌شویم. از این جهت که هنوز مسیریابی را برای / یا ریشه‌ی سایت که در ابتدا نمایش داده می‌شود، تنظیم نکرده‌ایم. به همین جهت Redirect زیر را پیش از آخرین Redirect تعریف شده اضافه می‌کنیم تا با درخواست ریشه‌ی سایت، به آدرس /movies هدایت شویم:
      <Redirect from="/" to="/movies" />
      و هانطور که در بخش 1 این قسمت بررسی کردیم، چون این مسیریابی با تمام آدرس‌های شروع شده‌ی با / تطابق پیدا می‌کند، وجود Switch در اینجا ضروری است؛ تا پس از انطباق با اولین مسیر ممکن، کار مسیریابی به پایان برسد. به علاوه با تعریف این Redirect، اگر مثلا آدرس نامعتبر http://localhost:3000/xyz را درخواست کنیم، به آدرس movies/ هدایت می‌شویم؛ چون / با xyz/ تطابق پیدا کرده و کار در همینجا به پایان می‌رسد. به همین جهت ذکر ویژگی exact در تعریف این Redirect ویژه ضروری است؛ تا صرفا به ریشه‌ی سایت پاسخ دهد:
      <Redirect from="/" exact to="/movies" />


      افزودن منوی راهبری به برنامه

      ابتدا فایل جدید src\components\navBar.jsx را ایجاد می‌کنیم؛ با این محتوا:
      import React from "react";
      import { Link, NavLink } from "react-router-dom";
      
      const NavBar = () => {
        return (
          <nav className="navbar navbar-expand-lg navbar-light bg-light">
            <Link className="navbar-brand" to="/">
              Home
            </Link>
            <button
              className="navbar-toggler"
              type="button"
              data-toggle="collapse"
              data-target="#navbarNavAltMarkup"
              aria-controls="navbarNavAltMarkup"
              aria-expanded="false"
              aria-label="Toggle navigation"
            >
              <span className="navbar-toggler-icon" />
            </button>
            <div className="collapse navbar-collapse" id="navbarNavAltMarkup">
              <div className="navbar-nav">
                <NavLink className="nav-item nav-link" to="/movies">
                  Movies
                </NavLink>
                <NavLink className="nav-item nav-link" to="/customers">
                  Customers
                </NavLink>
                <NavLink className="nav-item nav-link" to="/rentals">
                  Rentals
                </NavLink>
              </div>
            </div>
          </nav>
        );
      };
      
      export default NavBar;
      توضیحات:
      - ساختار کلی NavBar ای را که ملاحظه می‌کنید، دقیقا از مثال‌های رسمی مستندات بوت استرپ 4 گرفته شده‌است و تمام classهای آن با className جایگزین شده‌اند.
      - سپس تمام anchor‌های موجود در یک منوی راهبری بوت استرپ را به Link و یا NavLink تبدیل کرده‌ایم تا برنامه به صورت SPA عمل کند؛ یعنی با کلیک بر روی هر لینک، بارگذاری کامل صفحه در مرورگر صورت نگیرد و تنها محل و قسمتی که توسط کامپوننت‌های Route مشخص شده، به روز رسانی شوند. تفاوت NavLink با Link در کتابخانه‌ی react-router-dom، افزودن خودکار کلاس active به المانی است که بر روی آن کلیک شده‌است. به این ترتیب بهتر می‌توان تشخیص داد که هم اکنون در کجای منوی راهبری قرار داریم.
      - پس از تبدیل anchor‌ها به Link و یا NavLink، مرحله‌ی بعد، تبدیل href‌های لینک‌های قبلی به ویژگی to است که هر کدام باید به یکی از مسیریابی‌های تنظیم شده، مقدار دهی گردد.

      پس از تعریف کامپوننت منوی راهبری سایت، به app.js بازگشته و این کامپوننت را پیش از مسیریابی‌های تعریف شده اضافه می‌کنیم:
      import NavBar from "./components/navBar";
      // ...
      
      function App() {
        return (
          <React.Fragment>
            <NavBar />
            <main className="container">
              // ...
            </main>
          </React.Fragment>
        );
      }
      
      export default App;
      در اینجا چون نیاز به بازگشت دو المان NavBar و main وجود داشت، از React.Fragment برای محصور کردن آن‌ها استفاده کردیم.

      به علاوه به فایل index.css برنامه مراجعه کرده و padding این navBar را صفر می‌کنیم تا از بالای صفحه و بدون فاصله‌ای نمایش داده شود و container اصلی نیز اندکی از پایین آن فاصله پیدا کند:
      body {
        margin: 0;
        padding: 0 0 0 0;
        font-family: sans-serif;
      }
      
      .navbar {
        margin-bottom: 30px;
      }
      
      .clickable {
        cursor: pointer;
      }
      با این تغییر، اکنون ظاهر برنامه به صورت زیر در خواهد آمد:


      اگر دقت کنید چون آدرس http://localhost:3000/movies در حال نمایش است، در منوی راهبری، گزینه‌ی متناظر با آن، با رنگی دیگر مشخص (فعال) شده‌است.


      لینک کردن عناوین فیلم‌های نمایش داده شده به کامپوننت movieForm

      برای تبدیل عناوین نمایش داده شده‌ی در جدول فیلم‌ها به لینک، به کامپوننت src\components\moviesTable.jsx مراجعه کرده و تغییرات زیر را اعمال می‌کنیم:
      - در قدم اول باید بجای ذکر خاصیت Title در آرایه‌ی ستون‌های جدول:
      class MoviesTable extends Component {
        columns = [
          { path: "title", label: "Title" },
      یک محتوای لینک شده را نمایش دهیم:
      class MoviesTable extends Component {
        columns = [
          {
            path: "title",
            label: "Title",
            content: movie => <Link to={`/movies/${movie._id}`}>{movie.title}</Link>
          },
      در اینجا خاصیت content اضافه شده‌است تا یک المان React را مانند Link، بازگشت دهد و چون می‌خواهیم id هر فیلم نیز در اینجا ذکر شود، آن‌را به صورت arrow function تعریف کرده‌ایم تا شیء movie را گرفته و لینک به آن‌را تولید کند. در اینجا از یک template literal برای تولید پویای رشته‌ی منتسب به to استفاده کرده‌ایم.
      همچنین این Link را هم باید در بالای این ماژول import کرد:
      import { Link } from "react-router-dom";
      تا اینجا عناوین فیلم‌ها را تبدیل به لینک‌هایی کردیم:



      تعریف مسیریابی نمایش جزئیات یک فیلم انتخابی

      اگر به تصویر فوق دقت کنید، به آدرس‌هایی مانند http://localhost:3000/movies/5b21ca3eeb7f6fbccd47181a رسیده‌ایم که به همراه id هر فیلم هستند. اکنون می‌خواهیم کلیک بر روی این لینک‌ها را جهت فعالسازی صفحه‌ی نمایش جزئیات فیلم، تنظیم کنیم. به همین جهت به فایل app.js مراجعه کرده و مسیریابی زیر را به ابتدای Switch تعریف شده اضافه می‌کنیم:
      <Route path="/movies/:id" component={MovieForm} />
      که نیاز به این import را هم دارد:
      import MovieForm from "./components/movieForm";


      تکمیل کامپوننت نمایش جزئیات یک فیلم

      اکنون می‌خواهیم صفحه‌ی نمایش جزئیات فیلم، به همراه نمایش id فیلم باشد و همچنین با کلیک بر روی دکمه‌ی Save آن، کاربر را به صفحه‌ی movies هدایت کند. به همین جهت فایل src\components\movieForm.jsx را به صورت زیر ویرایش می‌کنیم:
      import React from "react";
      
      const MovieForm = ({ match, history }) => {
        return (
          <div>
            <h1>Movie Form {match.params.id} </h1>
            <button
              className="btn btn-primary"
              onClick={() => history.push("/movies")}
            >
              Save
            </button>
          </div>
        );
      };
      
      export default MovieForm;
      توضیحات:
      - چون این کامپوننت، یک کامپوننت تابعی بدون حالت است، props را باید از طریق آرگومان خود دریافت کند و البته در همینجا امکان Object Destructuring خواصی که از آن نیاز داریم، مهیا است؛ مانند { match, history } که ملاحظه می‌کنید.
      - سپس شیء match، امکان دسترسی به params ارسالی به صفحه را مانند id فیلم، میسر می‌کند.
      - با استفاده از شیء history و متد push آن می‌توان علاوه بر به روز رسانی تاریخچه‌ی مرورگر، به مسیر مشخص شده بازگشت که در همینجا و به صورت inline، تعریف شده‌است.


      کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: sample-17.zip
      مطالب
      پروسیجرها و شنود پارامترها در SQL Server - قسمت دوم
      در ادامه مطلب قبلی، آشنایی با شنود پارامترها در اس کیو ال سرور، جهت بهبود زمان پاسخگویی پروسیجرها یا کوئری‌های پارامتری الزامی است.
      اس کیو ال سرور بوسیله ایجاد پلن‌های اجرایی کامپایل شده، سعی در بهینه سازی پروسیجرها دارد. هنگامیکه اس کیو ال سرور یک پروسیجر را کامپایل می‌نماید، به پارامترهای ارسال شده توجه دارد و یک پلن اجرایی را بر اساس پارامترهای ارسالی ایجاد می‌کند. به فرآیند تماشا یا توجه به پارامترهای ارسالی به پروسیجر، شنود پارامترها گفته می‌شود. شنود پارامترها می‌تواند بعضی از اوقات به کاهش کارآیی پلن اجرایی منجر شود؛ مخصوصا زمانیکه پارامترهایی با کاردینالیتی متفاوت، فراخوانی شوند.

      شنود پارامتر چیست؟

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

      پلن‌های اجرایی بر اساس چیزهایی که از SQL Server خواسته می‌شوند، بهینه سازی می‌شوند. اس کیو ال سرور کوئری را بررسی کرده و یک استراتژی بهینه را برای اجرای آن مشخص می‌کند. به کارهایی که کوئری قرار است انجام دهد نگاه می‌کند؛ از مقادیر پارامترها برای استفاده از اطلاعات آماری استفاده کرده و محاسباتی را انجام خواهد داد.

      مثالی از مصرف شدید I/O بدلیل شنود پارامتر

      در ادامه، برای درک بهتر شنود پارامتر، با مثالی خواهید دید که پروسیجر ذیل، باعث مصرف بالای منابع، بر اساس پارامترهای ارسالی خواهد شد. در این مثال دو دسته متفاوت پارامتر برای اجرای پروسیجر ارسال خواهند شد و خواهید دید که فراخوانی دوم، منابع I/O بیشتری را نسبت به فراخوانی اول، مصرف خواهد کرد. در ادامه کدهای جدولی را که پروسیجر قرار است بر روی آن فراخوانی اطلاعات را انجام دهد، می‌بینید.

      SET NOCOUNT ON;
      DROP TABLE BillingInfo;
      CREATE TABLE BillingInfo(
      ID INT IDENTITY,
      BillingDate DATETIME,
      BillingAmt MONEY,
      BillingDesc varchar(500));
       
      DECLARE @I INT;
      DECLARE @BD INT;
      SET @I = 0;
      WHILE @I < 1000000 
      BEGIN
        SET @I = @I + 1;
        SET @BD=CAST(RAND()*10000 AS INT)%3650;
        INSERT BillingInfo (BillingDate, BillingAmt) 
        VALUES (DATEADD(DD,@BD,
          CAST('1999/01/01' AS DATETIME)),
          RAND()*5000);
      END 
       
      ALTER TABLE BillingInfo 
        ADD  CONSTRAINT [PK_BillingInfo_ID] 
        PRIMARY KEY CLUSTERED (ID);
       
      CREATE NONCLUSTERED INDEX IX_BillingDate
        ON dbo.BillingInfo(BillingDate);

      در جدول BilingInfo بالا، یک میلیون رکورد با مقادیر BilingDate و BilingAmt به صورت تصادفی ایجاد شده است. بر روی ستون ID، ایندکس خوشه‌ای و ستون ایندکس غیر خوشه‌ای بر روی ستون BilingDate ایجاد شده‌است.

      بوسیله پروسیجر زیر هم قرار است اطلاعات درخواستی فراهم شود:

      CREATE PROC [dbo].[DisplayBillingInfo]
        @BeginDate DATETIME,
        @EndDate DATETIME
      AS
      SELECT BillingDate, BillingAmt
        FROM BillingInfo
        WHERE BillingDate between @BeginDate AND @EndDate;
      سپس پروسیجر را 2 بار، با مقادیر پارامترهای متفاوتی اجرا می‌کنیم:
      SET STATISTICS IO ON;
      DBCC FREEPROCCACHE;
      EXEC dbo.DisplayBillingInfo 
        @BeginDate = '1999-01-01',  
        @EndDate  = '1999-12-31';  
        
      EXEC dbo.DisplayBillingInfo 
        @BeginDate = '2005-01-01',  
        @EndDate  = '2005-01-03';
      اطلاعات آماری I/O روشن است و اطلاعات I/O در هر بار اجرا، نمایش داده می‌شود. در خط دوم توسط DBCC FREEPROCCACHE، پلن کش خالی خواهد شد؛ جهت اطمینان از عدم وجود پلن اجرایی مشابهی.
      در فراخوانی اول، اطلاعات در بازه یک سال و در فراخوانی دوم، در بازه چند روز، درخواست شده‌اند. همانطور که گفته شد، پلن اجرایی بر اساس فراخوانی اول ایجاد خواهد شد و فراخوانی دوم نیز براساس همین پلن اجرایی ایجاد شده، اجرا می‌شود.


      همانطور که مشاهده می‌کنید عملیات Clustered Index Scan، اجرا شده و اطلاعات I/O نیز بشرح زیر است (خط اول فراخوانی اول، خط دوم فراخوانی دوم):
      Table 'BillingInfo'. Scan count 1, logical reads 3593, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
       
      Table 'BillingInfo'. Scan count 1, logical reads 3593, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
      حال ترتیب فراخوانی‌ها را به‌شرح زیر جابجا می‌کنیم:
      SET STATISTICS IO ON;
      DBCC FREEPROCCACHE;
      EXEC dbo.DisplayBillingInfo 
        @BeginDate = '2005-01-01',  
        @EndDate  = '2005-01-03';
        
      EXEC dbo.DisplayBillingInfo 
        @BeginDate = '1999-01-01',  
        @EndDate  = '1999-12-31';
      در کد بالا ابتدا فراخوانی که بازه کوچکتری دارد اجرا و پلن اجرایی بر اساس آن ایجاد خواهد شد و فراخوانی دوم از پلن کش شده استفاده می‌کند.

      اکنون عملیات Index Seek را بجای Index Scan مشاهده می‌کنید. اطلاعات I/O هم بشرح زیر است:

      Table 'BillingInfo'. Scan count 1, logical reads 2965, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
       
      Table 'BillingInfo'. Scan count 1, logical reads 337040, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
      اکنون فراخوانی اول بهینه‌تر اجرا شده و بجای Index Scan از Index Seek استفاده کرده‌است و logical reads آن کاهش یافته‌است. در حالیکه فراخوانی دوم که در بازه یکسال اجرا شده‌است، با عملیات هزینه بر I/O بیشتری نسبت به آزمایش (حدود 100 برابر) انجام شده‌است. انجام این 2 آزمایش، پلن اجرایی متفاوتی را بر اساس پارامترهای ورودی ایجاد کرد و هزینه‌های I/O آن را مشاهده کردید. اکنون درک خوبی را نسبت به این قابلیت و اثرات آن خواهید داشت و در قسمت بعدی به راه حل‌هایی جهت کاهش اثرات منفی آن اشاره خواهد شد.
      مطالب
      ویژگی های کمتر استفاده شده در NET. - بخش پنجم

      Nullable<T>.GetValueOrDefault Method

      با استفاده از متد GetValueOrDefault مقدار فعلی یک شیء Nullable و یا مقدار پیش فرض آن را می‌توان بدست آورد. این متد از عملگر ?? سریع‌تر است.
      float? yourSingle = -1.0f;
      Console.WriteLine( yourSingle.GetValueOrDefault() );
      
      yourSingle = null;
      Console.WriteLine( yourSingle.GetValueOrDefault() );
      
      // assign different default value
      Console.WriteLine( yourSingle.GetValueOrDefault( -2.4f ) );
      
      // returns the same result as the above statement
      Console.WriteLine( yourSingle ?? -2.4f );

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

      شما می‌توانید برای دیکشنری نیز یک متد Get امن ایجاد کنید (در صورت عدم وجود کلید، بجای پرتاب استثناء، مقدار پیش فرض بازگشت داده شود).

      public static class DictionaryExtensions
      {
          public static TValue GetValueOrDefault< TKey, TValue >( this Dictionary< TKey, TValue > dic,
                                                                  TKey key )
          {
              TValue result;
              return dic.TryGetValue( key,
                                      out result )
                  ? result
                  : default(TValue);
          }
      }

      و روش استفاده

      var names = new Dictionary< int, string >
                  {
                      { 0, "Vahid" }
                  };
      Console.WriteLine( names.GetValueOrDefault( 1 ) );


      ZipFile in .NET

      با استفاده از کلاس ZipFile ( رفرنس به اسمبلی System.IO.Compression.FileSystem ) می‌توان عملیات بازکردن، ایجاد و استخراج فایل‌های Zip را انجام داد.
      var startPath = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.Desktop ), "Start" );
      var resultPath = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.Desktop ), "Result" );
      var extractPath = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.Desktop ), "Extract" );
      Directory.CreateDirectory( startPath );
      Directory.CreateDirectory( resultPath );
      Directory.CreateDirectory( extractPath );
      
      var zipPath = Path.Combine( resultPath, Guid.NewGuid() + ".zip" );
      ZipFile.CreateFromDirectory( startPath, zipPath );
      ZipFile.ExtractToDirectory( zipPath, extractPath );

      C# Preprocessor Directives

      با استفاده از  warning#  می توان یک هشدار را در یک قسمت خاص از کد تولید کرد.
      #if DEBUG
      #warning DEBUG is defined
      #endif
      و خروجی آن

      با استفاده از  error#  می توان یک خطا را در یک جای خاصی از کد تولید کرد.
      #if DEBUG
      #error DEBUG is defined
      #endif
      در صورتی که کد بالا را اجرا کنید (در حال دیباگ) کامپایلر با نمایش DEBUG is defined در پنجره Error List، جلوی اجرای برنامه را می‌گیرد. اما در حالت ریلیز، برنامه بدون هیچ مشکلی اجرا می‌شود.

      با استفاده از  line#  می توانید شماره خط کامپایلر و نام فایل خروجی (اختیاری) را برای خطاها و هشدارها تغییر دهید.

      در مثال زیر، در صورتیکه در خط اول break point قرار دهید و با کلید F10 برنامه را اجرا کنید، مشاهده می‌کنید که دیباگر، خطی را که بعد از دستور line hidden# نوشته شده است، در نظر نمی‌گیرد (برای دیباگ) اما اجرا می‌شود و دیباگر بر روی دستور بعد از line default# قرار می‌گیرد.

          Console.WriteLine("Normal line #1."); // Set break point here.
      #line hidden
          Console.WriteLine("Hidden line.");
      #line default
          Console.WriteLine("Normal line #2.");


      Stackalloc

      کلمه کلیدی stackalloc برای اختصاص یک بلاک از حافظه در stack، در زمینه کد غیرامن (unsafe code) استفاده می‌شود.
      مثال زیر 20 عدد اول دنباله فیبوناچی را تولید می‌کند. هر عدد از مجموع دو عدد قبلی به دست می‌آید. در این مثال، یک بلاک از حافظه به اندازه 20 عدد از نوع int را در stack (نه heap) اختصاص می‌دهد. (تفاوت stack با heap)
      static unsafe void Fibonacci()
      {
          const int arraySize = 20;
          int* fib = stackalloc int[arraySize];
          var p = fib;
          *p++ = *p++ = 1;
      
          for ( var i = 2; i < arraySize; ++i, ++p )
          {
              *p = p[-1] + p[-2];
          }
      
          for ( var i = 0; i < arraySize; ++i )
          {
              System.Console.WriteLine( fib[i] );
          }
      }
      آدرس بلاک حافظه در اشاره گر fib ذخیره می‌شود. این متغیر توسط GC جمع آوری نمی‌شود و طول عمر آن محدود به متدی است که در آن تعریف شده است و شما نمی‌توانید قبل از بازگشت متد، حافظه را آزاد کنید.
      تنها دلیل استفاده از stackalloc، عملکرد بهتر آن است (برای محاسبات و یا ردوبدل اطلاعات). با استفاده از stackalloc به جای اختصاص دادن آرایه (heap)، فشار کمتری را بر GC وارد می‌کنید (نیاز کمتری به اجرای GC وجود دارد). در نتیجه سرعت اجرای بالاتری خواهید داشت.
      توجه: برای اجرای مثال بالا باید پنجره خصوصیات پروژه را باز کنید و در بخش Build، گزینه Allow unsafe code را تیک بزنید.
      مطالب
      روش فعالسازی SSL بر روی IIS SMTP Server
      پس از انجام «تنظیمات امنیتی SMTP Server متعلق به IIS جهت قرارگیری بر روی اینترنت» و  همچنین پیاده سازی «مراحل تنظیم Let's Encrypt در IIS»، یکی دیگر از کاربردهای مجوز SSL دریافتی، رمزنگاری تبادل اطلاعات بین SMTP Server جاری و میل سرور مقصد است. بدون انجام اینکار، ایمیل‌های دریافتی توسط GMail، یک چنین شکلی را خواهند داشت:


      همانطور که مشاهده می‌کنید، کنار ایمیل ارسالی توسط ما، یک آیکن قرمز رنگ مبتنی بر عدم رمزنگاری ارتباطات بین سرور ارسال کننده‌ی آن و جی‌میل، قابل مشاهده‌است. در ادامه قصد داریم این وضعیت را تغییر دهیم.


      نیاز به مجوز SSL معتبر نصب شده‌ی بر روی سرور

      اولین مرحله‌ی پیش از شروع به تنظیمات SSL (عبارت صحیح‌تر آن TLS) بر روی SMTP Server جاری، نیاز به یک مجوز SSL معتبر نصب شده‌ی بر روی سرور است که نمونه‌ی آن در مطلب «مراحل تنظیم Let's Encrypt در IIS» بررسی شده‌است.
      نکته‌ی مهمی که در اینجا وجود دارد این است که IIS SMTP Server، فقط مجوزهای موجود در قسمت Local_Machine\Personal certificates را مشاهده می‌کند. بنابراین ابتدا مراحل زیر را طی کنید تا مطمئن شوید که مجوز SSL شما در این قسمت نیز قرار گرفته‌است:
      در خط فرمان دستور mmc را وارد کرده و آن‌را اجرا کنید. در صفحه‌ی باز شده، گزینه‌ی File -> Add remove snap-ins را انتخاب کنید. در اینجا در لیست ظاهر شده، certificates را انتخاب و add کنید. در صفحه‌ی بعدی نیز computer account و سپس local computer را انتخاب کنید. اکنون بر روی دکمه‌ی ok کلیک کنید تا صفحه‌ی certificate مخصوص local computer ظاهر شود:


      مرحله‌ی بعد یادداشت کردن subject این مجوز است؛ از این جهت که از مقدار آن در مرحله‌ی بعد استفاده خواهیم کرد.



      مراحل تنظیم SSL در IIS SMTP Server

      1- پس از یادداشت کردن subject مجوز SSL مدنظر، به خواص SMTP Virtual Server مراجعه کنید:


      در اینجا در برگه‌ی Delivery، بر روی دکمه‌ی Advanced کلیک کرده و در قسمت fully-qualified domain name، مقدار subject مجوز را وارد کنید.
      2- پس از آن یکبار SMTP Virtual Server را متوقف کرده و سپس اجرا کنید تا مجوز متناظر با تنظیمات جدید را دریافت کند.
      3- اکنون اگر به برگه‌ی Access مراجعه کنید، قسمت Secure communication آن فعال شده‌است (پیش از تنظیم fully-qualified domain name این گزینه غیرفعال است) و می‌توان گزینه‌ی Requires TLS Encryption آن‌را انتخاب کرد. همچنین در اینجا تاریخ انقضای مجوز نیز ذکر می‌شود.


      4- در مرحله‌ی آخر، در همان برگه‌ی Delivery، بر روی دکمه‌ی outbound security کلیک کرده و گزینه‌ی TSL Encryption آن‌را نیز فعال کنید.



      تغییرات برنامه جهت ارسال ایمیل‌های امن

      اکنون اگر با همان تنظیمات قبلی برنامه‌ی خود، ایمیلی را ارسال کنید، پیام خطای زیر را دریافت خواهید کرد:
      The SMTP server requires a secure connection or the client was not authenticated.
      The server response was: 5.7.0 Must issue a STARTTLS command first
      برای رفع این مشکل، فایل web.config برنامه‌ی وب خود را گشوده و enableSsl تنظیمات smtp آن‌را به true تنظیم کنید:
      <system.net>
          <mailSettings>
            <smtp>
                <network enableSsl="true"/>
            </smtp>
          </mailSettings>
      </system.net>
      اینبار اگر ایمیلی را توسط برنامه ارسال کنید، گوگل چنین تغییراتی را نمایش می‌دهد:


      قسمت security پیام دریافتی فعال شده و TLS را تشخیص داده‌است؛ که بیانگر ارتباط امن بین SMTP Virtual Server ما و جی‌میل است.
      مطالب
      استفاده از Razor در فایل‌های JavaScript و CSS
      یکی از مشکلات سینتکس Razor سمت سرور، این است که در فایل‌های JavaScript و CSS سمت کاربر نمی‌توانیم از آن استفاده کنیم، به عنوان مثال فرض کنید در یک فایل JavaScript نیاز به مشخص سازی آدرس یک اکشن متد دارید؛ مثلاً انجام یک عملیات ای‌جکسی. در این حالت به عنوان یک Best Practice بهتر است از Url.Action استفاده کنید. اما همانطور که عنوان شد، این امکان یعنی استفاده از سینتکس Razor در فایل‌های JS و CSS مهیا نیست.
      ساده‌ترین راه‌حل، تولید ویوهای سمت سرور JavaScript ایی است. برای اینکار تنها کاری که باید انجام دهیم، تغییر مقدار Content-Type صفحه به مقدار موردنظر می‌باشد؛ مثلاً text/javascript برای فایل‌های JS و text/css برای فایل‌های CSS. به عنوان مثال برای فایل‌های CSS به این صورت عمل خواهیم کرد:
      public ActionResult Style()
      {
                  Response.ContentType = "text/css";
                  var model = new Style
                  {
                      Color = "red",
                      Background = "blue"
                  };
                  return View(model);
      }
      برای ویوی آن نیز خواهیم داشت:
      @model ExternalJavaScript.Models.Style
      @{
          Layout = null;
      }
      body {
          color : @Model.Color;
          background-color : @Model.Background;
      }
      در نهایت ویوی فوق را به عنوان فایل CSS در فایل Layout استفاده خواهیم کرد:
      <link rel="stylesheet" href="@Url.Action("Style","Home")" />
      برای حالت فوق می‌توانیم یک اکشن فیلتر به صورت زیر تهیه کنیم:
      public class ContentType : ActionFilterAttribute
          {
              private string _contentType;
              public ContentType(string ct)
              {
                  this._contentType = ct;
              }
      
              public override void OnActionExecuted(ActionExecutedContext context) { /* nada */ }
              public override void OnActionExecuting(ActionExecutingContext context)
              {
                  context.HttpContext.Response.ContentType = this._contentType;
              }
          }
      و برای استفاده از آن خواهیم داشت:
      [ContentType("text/css")]
      public ActionResult Style()
       {
           var model = new Style
            {
                      Color = "red",
                      Background = "blue"
            };
           return View(model);
      }

      برای فایل‌های JS نیز می‌توانیم از یک View به عنوان محل قرارگیری کدهای جاوا اسکریپت استفاده کنیم:
      public class JavaScriptSettingsController : Controller
      {
              public ActionResult Index()
              {
                  return PartialView();
              }
      }
      در این حالت در داخل فایل Index.cshtml کدهای جاوا اسکریپت را همراه با سینتکس Razor می‌توانیم بنویسیم:
      $(function(){
          $.post('@Url.Action("GetData", "Home")', function (data) {
              $('.notificationList').html(data);
                  if ($(data).filter("li").length != 0) {
                      $('#notificationCounter').html($(data).filter("li").length);
                  }
          });
      });
      سپس در داخل فایل Layout.cshtml_ می‌توانیم به ویوی فوق ارجاعی داشته باشیم:
      <script src="/JavaScriptSettings"></script>
      این روش به خوبی برای ویوهای JS و CSS کار خواهد کرد؛ اما از آنجائیکه ویوی ما توسط ویژوال استودیو به عنوان یک فایل JS و یا CSS معتبر شناخته نمی‌شود، Intellisense برای آن مهیا نیست. برای فعال سازی Intellisense و همچنین معتبر شناخته شدن ویوی فوق، بهترین راه‌حل قرار دادن کدهای JS درون بلاک script است (برای فایل‌های CSS نیز همینطور):
      <script>
          $(function () {
              $.post('@Url.Action("Index", "Home")', function (data) {
                  $('.notificationList').html(data);
                  if ($(data).filter("li").length != 0) {
                      $('#notificationCounter').html($(data).filter("li").length);
                  }
              });
          });
      </script>
      اما با اجرای برنامه، در کنسول مرورگر بلافاصله خطای Uncaught SyntaxError: Unexpected token < را دریافت خواهید کرد. در این حالت به روشی نیاز داریم که در زمان اجرا بلاک script را حذف نمائید. بنابراین از یک اکشن فیلتر سفارشی برای اینکار استفاده خواهیم کرد. کار این اکشن فیلتر، تغییر مقدار Content-Type و همچنین حذف بلاک مورد نظر می‌باشد:
      public class ExternalFileAttribute : ActionFilterAttribute
          {
              private readonly string _contentType;
              private readonly string _tag;
              public ExternalFileAttribute(string ct, string tag)
              {
                  this._contentType = ct;
                  _tag = tag;
              }
      
              public override void OnResultExecuted(ResultExecutedContext filterContext)
              {
                  var response = filterContext.HttpContext.Response;
                  response.Filter = new StripEnclosingTagsFilter(response.Filter, _tag);
                  response.ContentType = _contentType;
              }
      
              private class StripEnclosingTagsFilter : MemoryStream
              {
                  private static Regex _leadingOpeningScriptTag;
                  private static Regex _trailingClosingScriptTag;
      
                  //private static string Tag;
      
                  private readonly StringBuilder _output;
                  private readonly Stream _responseStream;
      
                  /*static StripEnclosingTagsFilter()
                  {
                      LeadingOpeningScriptTag = new Regex(string.Format(@"^\s*<{0}[^>]*>", Tag), RegexOptions.Compiled);
                      TrailingClosingScriptTag = new Regex(string.Format(@"</{0}>\s*$", Tag), RegexOptions.Compiled);
                  }*/
      
                  public StripEnclosingTagsFilter(Stream responseStream, string tag)
                  {
                      _leadingOpeningScriptTag = new Regex(string.Format(@"^\s*<{0}[^>]*>", tag), RegexOptions.Compiled);
                      _trailingClosingScriptTag = new Regex(string.Format(@"</{0}>\s*$", tag), RegexOptions.Compiled);
      
                      _responseStream = responseStream;
                      _output = new StringBuilder();
                  }
      
                  public override void Write(byte[] buffer, int offset, int count)
                  {
                      string response = GetStringResponse(buffer, offset, count);
                      _output.Append(response);
                  }
      
                  public override void Flush()
                  {
                      string response = _output.ToString();
      
                      if (_leadingOpeningScriptTag.IsMatch(response) && _trailingClosingScriptTag.IsMatch(response))
                      {
                          response = _leadingOpeningScriptTag.Replace(response, string.Empty);
                          response = _trailingClosingScriptTag.Replace(response, string.Empty);
                      }
      
                      WriteStringResponse(response);
                      _output.Clear();
                  }
      
                  private static string GetStringResponse(byte[] buffer, int offset, int count)
                  {
                      byte[] responseData = new byte[count];
                      Buffer.BlockCopy(buffer, offset, responseData, 0, count);
      
                      return Encoding.Default.GetString(responseData);
                  }
      
                  private void WriteStringResponse(string response)
                  {
                      byte[] outdata = Encoding.Default.GetBytes(response);
                      _responseStream.Write(outdata, 0, outdata.GetLength(0));
                  }
              }
          }
       در نهایت می‌توانیم اکشن‌متد موردنظرمان را با فیلتر سفارشی مزین کنیم:
      [ExternalFile("text/javascript", "script")]
      public ActionResult Index()
      {
            return PartialView();
      }
      برای تولید ویوهای CSS نیز کافی است مقادیر فیلتر را تغییر دهیم:
      [ExternalFile("text/css", "style")]
      public ActionResult Style()
      {
                  var model = new Style
                  {
                      Color = "red",
                      Background = "blue"
                  };
                  return View(model);
      }
      مطالب
      AngularJS #1
      پیش از اینکه آموزش AngularJs را شروع کنیم بهتر است با مفهوم برنامه‌های تک صفحه ای وب  و یا  Single Page Web Applications آشنا شویم؛ چرا که AngularJS برای توسعه هر چه ساده‌تر و قوی‌تر این گونه برنامه‌ها متولد شده است.

      Single Page Application
      برای درک چگونگی کارکرد این برنامه ها، مثالی را میزنیم که هر روزه با آن سرو کار دارید، یکی از نمونه‌های کامل و قدرتمند برنامه‌های Single Page Application و یا به اختصار SPA، سرویس پست الکترونیکی Google و یا همان Gmail است.
      اجازه بدهید تا ویژگی‌های SPA را با بررسی Gmail انجام دهم، تا به درک روشنی از آن برسید:

      Reload نشدن صفحات
      در Gmail هیچ گاه صفحه Reload و یا اصطلاحا بارگیری مجدد نمی‌شود. برای مثال، وقتی شما لیست ایمیل‌های خود را مشاهده میکنید و سپس بر روی یکی از آن‌ها کلیک میکنید، بدون اینکه به صفحه ای دیگر هدایت شوید؛ایمیل مورد نظر را میبینید. در حقیقت تمامی اطلاعات در همان صفحه نمایش داده می‌شوند و بر عکس وب سایت‌های معمول است که از صفحه ای به صفحه‌ی دیگر هدایت میشوید ، در یک صفحه تمام کارهای مورد نیاز خود را انجام می‌دهید و احتیاجی به بارگیری مجدد صفحات نیست. با توجه به این صحبت‌ها برای توسعه دهنده‌های وب آشکار است که تکنیک AJAX، نقشی اساسی در این گونه برنامه‌ها دارد، چون کلیه‌ی عملیات ارتباط با سرور در پشت زمینه انجام می‌شوند.

      تغییر URL در نوار آدرس مرورگر
      وقتی شما بر روی یک ایمیل کلیک می‌کنید و آن ایمیل را بدون Reload شدن مجدد صفحه مشاهده می‌کنید، آدرس صفحه در مرورگر نیز تغییر می‌کند. خب مزیت این ویژگی چیست؟ مزیت این ویژگی در این است که هر ایمیل شما دارای یک آدرس منحصر به فرد است و به شما امکان Bookmark کردن آن لینک، باز کردن آن در یک Tab جدید و یا حتی ارسال آن به دوستان خود را دارید. حتی اگر این مطلب را جدا از Gmail در نظر بگیریم، به موتور‌های جست و جو کمک می‌کند، تا هر صفحه را جداگانه Index کنند؛ جدا از اینکه وبسایت ما SPA است. همچنین این کار یک مزیت مهم دیگر نیز دارد؛ و آن کار کردن کلیدهای back و forward مرورگر، برای بازگشت به صفحات پیمایش شده قبلی است.
      شاید قبل از بیان این ویژگی با خود گفته باشید که پیاده سازی Reload نشدن صفحات با AJAX آن چنان کار پیچیده ای نیست. بله درست است، اما آیا شما قبل از این راه حلی برای تغییر URL اندیشیده بودید؟ مطمئنا شما هم صفحات وب زیادی را دیده اید که همه‌ی صفحات آن دارای یک URL در نوار آدرس مرورگر هستند و هیچگاه تغییر نمی‌کنند و با باز کردن یک لینک در یک Tab جدید، باز همان صفحه‌ی تکراری را مشاهده می‌کنند! و یا بدتر از همه که دکمه‌ی back مرورگر غیر عادی عمل می‌کند. بله، اینها تنها تعدادی از صدها مشکلات رایج سیستم‌های نوشته شده ای است که سعی کردند همه‌ی کارها در یک صفحه انجام شود.

      Cache شدن اطلاعات دریافتی
      شاید خیلی‌ها ویژگی‌های فوق را برای یک SPA کافی بدانند، اما تعدادی هم مانند نگارنده وجود یک کمبود را حس می‌کنند و آن کش شدن اطلاعات دریافتی در مرورگر است. Gmail این امکان را به خوبی پیاده سازی کرده است. لیست ایمیل‌های دریافتی در بار اول از سرور دریافت می‌شود، سپس شما بر روی یک ایمیل کلیک و آن را مشاهده می‌کنید. حال به لیست ایمیل‌های دریافتی بازگردید، آیا رفت و برگشتی به سرور انجام می‌شود؟ مسلما خیر. حتی اگر دوباره بر روی آن ایمیل مشاهده شده ، کلیک کنید، بدون رفت و برگشتی به سرور آن ایمیل را مشاهده می‌کنید.
      کش شدن اطلاعات سبب می‌شود که بار سرور خیلی کاهش یابد و رفت و آمدهای بیهوده صورت نگیرد. کش شدن داده‌ها یک مزیت دیگر نیز دارد و آن تبدیل برنامه‌های معمول وب stateless به برنامه‌های شبه دسکتاپ state full است. 
        
      تکنیک AJAX در پیاده سازی امکانات فوق نقشی اساسی را بازی می‌کند. کمی به عقب برمیگردیم یعنی زمانی که AJAX برای اولین بار مطرح شد و هدف اصلی به وجود آمدن آن پیاده سازی برنامه‌های وب به شکل دسکتاپ بود و این کار از طریق انجام تمامی ارتباطات سرور با  XMLHttpRequest  امکان پذیر می‌شد. شاید آن زمان با توجه به محدودیت تکنولوژی‌ها موجود این کار به صورت تمام و کمال امکان پذیر نبود، اما امروزه به بهترین شکل ممکن قابل پیاده سازی است.

       شاید اکنون این سوال پیش بیاید که چرا باید وبسایت خود را به شکل SPA طراحی کنیم؟
      برای پاسخ دادن به این سوال باید گفت که سیستم‌های وب  امروزی به دو دسته‌ی زیر تقسیم می‌شوند:
      Web Documents و یا همان وب سایت‌های معمول
      Web Applications و یا همان Single Page Web Applications
      اگر هدف شما طراحی یک وب سایت معمول است که هدف آن، نمایش یک سری اطلاعات است و به قولی دارای محتواست، مطمئنا پیاده سازی این سیستم به صورت SPA کاری بیهوده به نظر می‌آید؛ ولی اگر هدفتان نوشتن سیستم هایی مثل Gmail، Google Maps، Azure، Facebook و ... است، پیاده سازی آن‌ها به صورت وب سایت‌های معمولی، غیر معقول به نظر می‌آید. حتی بخش‌های مدیریتی یک وبسایت هم می‌تواند به خوبی توسط SPA پیاده سازی شود، چرا که واقعا برای مدیریت اطلاعات یک وب سایت احتیاجی نیست، که  از این صفحه به آن صفحه جا به جا شد.

      معرفی کتابخانه‌ی AngularJS
      AngularJS فریم ورکی متن باز و نوشته شده به زبان جاوا اسکریپت است. هدف از به وجود آمدن این فریم ورک، توسعه هر چه ساده‌تر SPA‌ها با الگوی طراحی MVC و تست پذیری هر چه آسان‌تر آن‌ها است. این فریم ورک توسط یکی از محققان Google در سال 2009 به وجود آمد. بعد‌ها این فریم ورک تحت مجوز MIT به صورت متن باز در آمد و اکنون گوگل آن را حمایت می‌کند و توسط هزاران توسعه دهنده در سرتاسر دنیا، توسعه داده می‌شود.
      قبل از اینکه به بررسی ویژگی‌های Angular بپردازم، بهتر است ابتدا مطلبی درباره‌ی به کارگیری Angular از Brad Green که کارمند گوگل است، بیان کنم.
      در سال 2009 تیمی در گوگل مشغول انجام پروژه ای به نام Google Feedback بودند. آن‌ها سعی داشتند تا در طی چند ماه، به سرعت کد‌های خوب و تست پذیر بنویسند. پس از 6 ماه کدنویسی، نتیجه‌ی کار 17000 خط کد شد. در آن موقع یکی از اعضای تیم به نام Misko Hevery، ادعا کرد که می‌تواند کل این پروژه را در دو هفته به کمک کتابخانه‌ی متن بازی که در اوقات فراغت توسعه داده است، بازنویسی کند. Misko نتوانست در دو هفته این کار را انجام دهد. اما پس از سه هفته همه‌ی اعضای تیم را شگفت زده کرد. نتیجه‌ی کار تنها 1500 خط بود! همین باعث شد که ما بفهمیم که، Misko بر روی چیزی کاری میکند که ارزش دنبال کردن دارد.
      پس از آن قضیه Misko و Brad بر روی Angular کار کردند و اکنون هم Angular توسط تیمی در گوگل و هزاران توسعه دهنده‌ی متن باز حرفه ای در سرتاسر جهان، درحال توسعه است.
      فکر کنم همین داستان ذکر شده، قدرت فوق العاده زیاد این فریم ورک را برای همگان آشکار سازد.

      ویژگی‌های AngularJS:

      قالب‌های سمت کاربر (Client Side Templates): انگولار دارای یک template engine قدرتمند برای تعریف قالب است.

      پیروی از الگوی طراحی MVC: انگولار، الگوی طراحی MVC را برای توسعه پیشنهاد می‌دهد و امکانات زیادی برای توسعه هر چه راحت‌تر با این الگو فراهم کرده است.

      Data Binding: امکان تعریف انقیاد داده دوطرفه (Two-Way Data Binding) در این فریم ورک به راحتی هرچه تمام، امکان پذیر است.

      Dependency Injection: این فریم ورک برای دریافت وابستگی‌های تعریف شده، دارای یک سیستم تزریق وابستگی توکار است.

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

      تعریف Directive‌های سفارشی: یکی از جذاب‌ترین و قدرتمند‌ترین امکانات این فریم ورک، تعریف Directive‌های سفارشی است.  Directive ها، امکان توسعه HTML را فراهم کرده اند. توسعه‌ی HTML اکنون در قالب Web Components‌ها فراهم شده است، اما هنوز هم خیلی از مرورگر‌های جدید نیز از آن پشتیبانی نمی‌کنند.

      فرمت کردن اطلاعات با استفاده از فیلترهای سفارشی: با استفاده از فیلترها میتوانید چگونگی الحاق شدن اطلاعات را برای نمایش به کاربر تایین کنید ؛ انگولار همراه با فیلترهای گوناگون مختلفی عرضه میشود که میتوان برایه مثال به فیلتر currency ، date ،uppercase کردن رشته‌ها و .... اشاره کرد همچنین شما محدود به فیلترهای تعریف شده در انگولار نیستید و آزادید که فیلترهای سفارشی خودتان را نیز تعریف کنید.

      سیستم Routing: دارا بودن سیستم Routing  قدرتمند، توسعه SPA‌ها را بسیار ساده کرده است.

      سیستم اعتبار سنجی: Angular دارای سیستم اعتبار سنجی توکار قدرتمند برای بررسی داده‌های ورودی است.

      سرویس تو کار برای ارتباط با سرور: Angular دارای سرویس پیش فرض ارتباط با سرور به صورت AJAX است.

      تست پذیری: Angular دارای بستری آماده برای تست کردن برنامه‌های نوشته شده است و از Unit Tests و Integrated End-to-End Test هم پشتیبانی می‌کند.

      جامعه‌ی متن باز بسیار قوی
         
      این‌ها فقط یک مرور کلی بر توانایی‌های این فریم ورک بود و در ادامه هر کدام از این ویژگی را به صورت دقیق بررسی خواهیم کرد.
      در مقاله‌ی بعدی، به چگونگی نصب AngularJS خواهیم پرداخت. سپس، اولین کد خود را با استفاده از آن خواهیم نوشت و مطالب Client Side Templates و MVC را دقیق‌تر بررسی خواهیم کرد.
       
      مطالب
      آموزش TypeScript #4
      در پست‌های قبل با کلیات و primitive types در زبان TypeScript آشنا شدیم:

      در این پست به مفاهیم شی گرایی در این زبان می‌پردازیم.

      ماژول ها:
      تعریف یک ماژول: برای تعریف یک ماژول باید از کلمه کلیدی module استفاده کنید. یک ماژول معادل یک ظرف است برای نگهداری کلاس‌ها و اینترفیس‌ها و سایر ماژول ها. کلاس‌ها و اینترفیس‌ها در TypeScript می‌توانند به صورت internal یا public باشند(به صورت پیش فرض internal است؛ یعنی فقط در همان ماژول قابل استفاده و فراخوانی است). هر چیزی که در داخل یک ماژول تعریف می‌شود محدوده آن در داخل آن ماژول خواهد بود. اگر قصد توسعه یک پروژه در مقیاس بزرگ را دارید می‌توانید همانند دات نت که در آن امکان تعریف فضای نام‌های تودرتو امکان پذیر است در TypeScript نیز، ماژول‌های تودرتو تعریف کنید.  برای مثال:
      module MyModule1 {
          module  MyModule2 {
           }
      }
      اما به صورت معمول سعی می‌شود هر ماژول در یک فایل جداگانه تعریف شود. استفاده از چند ماژول در یک فایل به مرور، درک پروژه را سخت خواهد کرد و در هنگام توسعه امکان برخورد با مشکل وجود خواهد داشت. برای مثال اگر یک فایل به نام MyModule.ts داشته باشیم که یک ماژول به این نام را شامل شود بعد از کامپایل یک فایل به نام  MyModule.js ایجاد خواهد شد. 

      کلاس ها:
      برای تعریف یک کلاس می‌توانیم همانند دات نت از کلمه کلیدی class استفاده کنیم. بعد از تعریف کلاس می‌توانیم متغیر‌ها و توابع مورد نظر را در این کلاس قرار داده و تعریف کنیم.  
      module Utilities {
         export class Logger {
            log(message: string): void{
             if(typeofwindow.console !== 'undefined') {
                 window.console.log(message);
              }
            }
         }    
      }
      نکته مهم و جالب قسمت بالا کلمه export است. export معادل public در دات نت است و کلاس  logger را قابل دسترس در خارج ماژول Utilities خواهد کرد. اگر از export در هنگام تعریف کلاس استفاده نکنیم این کلاس فقط در سایر کلاس‌های تعریف شده در داخل همان ماژول قابل دسترس است.
      تابع log  که در کلاس بالا تعریف کردیم به صورت پیش فرض public یا عمومی است و نیاز به استفاده export نیست.
      برای استفاده از کلاس بالا باید این کلمه کلیدی new استفاده کنیم.  
      window.onload = function() {
        varlogger = new Utilities.Logger();
        logger.log('Logger is loaded'); 
      };
      برای تعریف سازنده برای کلاس بالا باید از کلمه کلیدی constructor استفاده نماییم:
      export class Logger{
      constructor(private num: number) { 
      }
      با کمی دقت متوجه تعریف متغیر num به صورت private خواهید شد که برخلاف انتظار ما در زبان‌های دات نتی است. بر خلاف دات نت در زبان TypeScript، دسترسی به متغیر تعریف شده در سازنده با کمک اشاره گر this  در هر جای کلاس ممکن می‌باشد. در نتیجه نیازی به تعریف متغیر جدید و  پاس دادن مقادیر این متغیر‌ها به این فیلدها نمی‌باشد.
      اگر به تابع log دقت کنید خواهید دید که یک پارامتر ورودی به نام message دارد که نوع آن string است. در ضمن Typescript از پارامتر‌های اختیاری( پارامتر با مقدار پیش فرض) نیز پشتیبانی می‌کند. مثال:

      pad(num: number, len: number= 2, char: string= '0')
      استفاده از پارامترهای Rest
      منظور از پارامترهای Rest یعنی در هنگام فراخوانی توابع محدودیتی برای تعداد پارامتر‌ها نیست که معادل params در دات نت است. برای تعریف این گونه پارامترهاکافیست به جای params از ... استفاده نماییم.
      function addManyNumbers(...numbers: number[]) {
        var sum = 0;
        for(var i = 0; i < numbers.length; i++) {
          sum += numbers[i];
       }
        returnsum;
      }
      var result = addManyNumbers(1,2,3,5,6,7,8,9);
      تعریف توابع خصوصی
      در TypeScript امکان توابع خصوصی با کلمه کلیدی private امکان پذیر است. همانند دات نت با استفاده از کلمه کلیدی private می‌توانیم کلاسی تعریف کنیم که فقط برای همان کلاس قابل دسترس باشد(به صورت پیش فرض توابع به صورت عمومی هستند).
      module Utilities {
          Export class Logger {  
           log(message: string): void{
                       if(typeofwindow.console !== 'undefined') {   
                          window.console.log(this.getTimeStamp() + ' -'+ message);
                          window.console.log(this.getTimeStamp() + ' -'+ message); 
                      }
              }
        private getTimeStamp(): string{
            var now = newDate();
            return now.getHours() + ':'+
            now.getMinutes() + ':'+
            now.getSeconds() + ':'+
            now.getMilliseconds();
        }
       }
      }
      از آن جا که تابع getTimeStamp به صورت خصوصی تعریف شده است در نتیجه امکان استفاده از آن در خارج کلاس وجود ندارد. اگر سعی بر استفاده این تابع داشته باشیم توسط کامپایلر با یک warning مواجه خواهیم شد.

      یک نکته مهم این است که کلمه private فقط برای توابع و متغیر‌ها قابل استفاده است.

      تعریف توابع static:

      در TypeScript امکان تعریف توابع static وجود دارد. همانند دات نت باید از کلمه کلیدی static استفاده کنیم.

      classFormatter {
      static pad(num: number, len: number, char: string): string{
            var output = num.toString();
               while(output.length < len) {
               output = char + output;
            }
         returnoutput;
         }
        }
      }
      و استفاده از این تابع بدون وهله سازی از کلاس :
      Formatter.pad(now.getSeconds(), 2, '0') +
      Function Overload
      همان گونه که در دات نت امکان overload کردن توابع میسر است در TypeScript هم این امکان وجود دارد.
      static pad(num: number, len?: number, char?: string);
      static pad(num: string, len?: number, char?: string);
      static pad(num: any, len: number= 2, char: string= '0') {
       var output = num.toString();
       while(output.length < len) {
       output = char + output;
       }
       returnoutput;
      }

      ادامه دارد...
      مطالب
      چرا به الگوهای طراحی JavaScript نیاز داریم ؟
      امروزه اهمیت یادگیری JavaScript بر هیچ کس پوشیده نیست ، API‌های جدید HTML 5 و امکانات جدید وب مثل Geo Location ، Offline storage ، Canvas ، Offline Web applications و ... همه نیاز به دانش JavaScript دارند. در نتیجه نیاز است علاوه بر یادگیری JavaScript به تمیز کد نوشتن جهت سهولت نگهداری آگاه بود.  همانطور که در کدهای سمت سرور مثل C# و یا PHP نیاز به استفاده از الگوهای طراحی (Design Patterns)است در JavaScript هم اوضاع به همین منوال است.
      الگوی طراحی یک راه حل قابل استفاده مجدد است که برای حل مشکلات متداول در طراحی نرم افزار به کار می‌رود.
      چرا به الگوی‌های طراحی JavaScript نیازمندیم ؟ 
      1. می خواهیم کد هایی با قابلیت استفاده‌ی مجدد بنویسیم ، استفاده از عملکرد‌های مشابه در سطح صفحات یک Web application یا چند Web Application.
      2. می خواهیم کد هایی با قابلیت نگهداری بنویسیم ، هر چه قدر در فاز توسعه کد‌های با کیفیت بنویسیم در فاز نگهداری از آن بهره می‌بریم. باید کد هایی بنویسیم که قابل Debug و خواندن توسط دیگر افراد تیم باشند.
      3. کدهای ما نباید با توابع و متغیر‌های دیگر پلاگین‌ها تداخل نامگزاری داشته باشند. در برنامه‌های امروزی بسیار مرسوم است که از پلاگین‌های Third party استفاده شود. می‌خواهیم با رعایت Encapsulation and modularization در کدهایمان از این تداخل جلوگیری کنیم.
      معمولا کد‌های JavaScript که توسط اکثر ما نوشته می‌شود یک سری تابع پشت سرهم هست ، بدون هیچ کپسوله سازی : 
      function getDate() {
          var now = new Date();
          var utc = now.getTime() + (now.getTimezoneOffset() * 60000);
          var est;
          est = new Date(utc + (3600000 * -4));
          return dateFormat(est, "dddd, mmmm dS, yyyy, h:MM:ss TT") + " EST";
      
      }
      function initiate_geolocationToTextbox() {
          navigator.geolocation.getCurrentPosition(handle_geolocation_queryToTextBox);
      }
      
      function handle_geolocation_queryToTextBox(position) {
          var longitude = position.coords.longitude;
          var latitude = position.coords.latitude;
          $("#IncidentLocation").val(latitude + " " + longitude);
      }
      
      به این روش کدنویسی Function Spaghetti Code گفته می‌شود که معایبی دارد : 
      • توابع و متغیر‌ها به Global scope برنامه افزوده می‌شوند.
      • کد Modular نیست.
      • احتمال رخ دادن Conflict در اسامی متغیر‌ها و توابع بالا می‌رود.
      • نگهداری کد به مرور زمان سخت می‌شود.
      با شبیه سازی یک مثال مشکلات احتمالی را بررسی می‌کنیم : 
      // file1.js
      function saveState(obj) {
          // write code here to saveState of some object
          alert('file1 saveState');
      }
      // file2.js (remote team or some third party scripts)
      function saveState(obj, obj2) {
           // further code...
          alert('file2 saveState");
      }
      
      همانطور که می‌بینید در این مثال در 2 فایل متفاوت در برنامه مان از 2 تابع با اسامی یکسان و امضای متفاوت استفاده کرده ایم . اگر فایل‌ها را اینگونه در برنامه آدرس دهی کنیم : 
      <script src="file1.js" type="text/javascript"></script>
      <script src="file2.js" type="text/javascript"></script>
      
      متد saveState در فایلی که دیرتر آدرس داده شده (file2.js) ، متد saveState در file1.js را Override می‌کند ، در نتیجه عملکردی که از متد saveState در فایل اول انتظار داریم اتفاق نمی‌افتد.
       در پست بعدی به راه حل این مشکلات و کپسوله سازی خواهم پرداخت.
      برای مطالعه‌ی بیشتر کتاب (Learning JavaScript Design Patterns) را از دست ندهید.