مطالب
لیست شماره نگارش‌های دات نت فریم ورک تا این تاریخ

از زمانیکه دات نت فریم ورک ارائه شده (حدودا 8 سال یا بیشتر اگر بتای آن‌را هم به حساب بیاوریم به سال 2000 بر می‌گردد)، نگارش‌های متفاوتی تا به امروز در اختیار عموم قرار گرفته اند.
جدول زیر این موارد را تا این تاریخ لیست کرده و شماره نگارش دقیق آن‌ها را نیز بر می‌شمارد:

.NET version

Actual version

3.5 SP1

3.5.30729.1

3.5

3.5.21022.8

3.0 SP2

3.0.4506.2152

3.0 SP1

3.0.4506.648

3.0

3.0.4506.30

2.0 SP2

2.0.50727.3053

2.0 SP1

2.0.50727.1433

2.0

2.0.50727.42

1.1 SP1

1.1.4322.2032

1.1 SP1 (in 32 bit version of Windows 2003)

1.1.4322.2300

1.1

1.1.4322.573

1.0 SP3

1.0.3705.6018

1.0 SP2

1.0.3705.288

1.0 SP1

1.0.3705.209

1.0

1.0.3705.0


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

class NetVersion
{
public static string Version
{
get
{
return Environment.Version + "\n" +
Environment.OSVersion;
}
}
}

که خروجی آن فقط آخرین نگارش CLR را شامل می‌شود.
برای مثال روی ویندوز اکس پی سرویس پک 3 با دات نت فریم ورک سه و نیم، سرویس پک یک خواهیم داشت:
2.0.50727.3053
Microsoft Windows NT 5.1.2600 Service Pack 3
که عدد نگارش ارائه شده با دات نت فریم ورک 2 سرویس پک 2 تطابق دارد. به عبارت دیگر آخرین نگارش CLR هنوز همان 2 است و موارد دیگر (مثل wf و wcf و ...) فقط یک سری افزونه برای این هسته به شمار می‌روند.
خوبی این روش هم این است که اگر در یک هاست اینترنتی قصد داشتید شماره نگارش دات نت فریم ورک سرور را بررسی کنید، بدون مشکل پاسخ خواهد داد. برای مثال اگر به دات نت فریم ورک 2 سرویس پک 2 رسیدید، یعنی دات نت فریم ورک سه و نیم، سرویس پک یک حتما روی سرور نصب است، چون این دو با هم ارائه شده‌اند و به صورت مجزا ارائه نشده‌اند.

برای بدست‌ آوردن لیست دات نت فریم ورک‌های مختلف باید به رجیستری ویندوز مراجعه کرد. مسیرهای:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent\Post Platform


این روش یا حتی لیست کردن فولدرهای نصب شد در مسیر C:\WINDOWS\Microsoft.NET\Framework نیز شماره نگارش کامل را ارائه نمی‌دهند. تنها راه باقیمانده مراجعه به فایل mscorlib.dll هر پوشه و بررسی نگارش آن است:



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

public static string MscorlibVersion
{
get
{
//using System.Diagnostics;
FileVersionInfo myFileVersionInfo = FileVersionInfo.GetVersionInfo(
Environment.GetEnvironmentVariable("windir") +
@"\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll");
return myFileVersionInfo.ProductVersion;
}
}

مطالب
آشنایی با angular.extend در AngularJs
در این مقاله قصد دارم تکنیکی ساده ولی در عین حال موثر را معرفی کنم که در نوشتن کنترلر‌ها بسیار کارآمد است. همانطور که می‌دانید به طور معمول برای تعریف متد‌ها و پراپرتی‌های درون کنترلر و انتقال آن به View، از دو روش انتساب به scope$ و controller as استفاده می‌شود.
در AngularJs متدی به صورت built-in وجود دارد که قادر است یک شیء را درون شیء دیگر کپی کند. این متد در بسیاری از مواقع بسیار کارآمد است که به بررسی آن خواهیم پرداخت. جزئیات بیشتر و دقیق‌تر در مورد angular.extend را می‌توانید در اسناد سایت رسمی AngularJs  مشاهده کنید.
به تکه کد زیر توجه کنید. این کد نمونه‌ای از استفاده‌ی از scope$ است. مشاهده می‌کنید که متغییر‌های thingOne و thingTwo چگونه درون scope$ قرار میگیرد.
app.controller(‘ThingController’, [ ‘$scope’, function($scope) {
   $scope.thingOne = ‘one’;
   $scope.thingTwo = ‘two’;
   $scope.getThings = function() { 
       return $scope.thingOne + ‘ ‘ + $scope.thingTwo; 
   };
}]);
حال ببینیم که متد angular.extend کد فوق را چگونه ساده سازی می‌کند. همانطور که مشاهده می‌کنید توسط extend، تمامی متدها و پراپرتی‌ها به صورت یکجا به scope انتساب داده شده است.
app.controller(‘ThingController’, [ ‘$scope’, function($scope) {
    angular.extend($scope, {
        thingOne: ‘one’,
        thingTwo: ‘two’,
        getThings: function() { 
            return $scope.thingOne + ‘ ‘ + $scope.thingTwo; 
        }
    });
}]);
استفاده از angular.extend باعث می‌شود که تمامی assignmentها به صورت‌‌تر و تمیز درون scope یا this قرار بگیرند. حال اینکه نیازی نیست تا شما تمامی این assingmentها را به صورت یکجا و توسط یک بار صدا زدن extend انجام دهید. شما می‌توانید در نقاط مختلف کنترلر این متد را با مقادیر انتسابی متفاوتی فراخوانی کنید. تکه کد زیر نمونه‌ی تغییر یافته به صورت دو بار فراخوانی extend است:
app.controller(‘ThingController’, [ ‘$scope’, function($scope) {
    // models
    angular.extend($scope, {
        thingOne: ‘one’,
        thingTwo: ‘two’
    });

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

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

    // methods
    angular.extend($scope, {
       // in HTML template, something like {{ things }}
       get things() { 
            return _thingOne + ‘ ‘ + _thingTwo; 
        }
    });
}]);
و در آخر اینکه با استفاده از این متد می‌توان ارث بری در سرویس‌های Angular را پیاده سازی کرد که جزئیات آن را می‌توانید از اینجا  دریافت کنید.
اشتراک‌ها
طراحی رابط کاربری آسان با فریم ورک کوانتوم یو آی
The most powerful NATIVE AngularJS and Bootstrap CSS based UI components make developer life 
easyCompletely MVVM structure, easy, light, fast, mobile friendly 25+ components and lots of native angular services.Optimized and extended Bootstrap based CSS structure with pure CSS3 plugins. 
طراحی رابط کاربری آسان با فریم ورک کوانتوم یو آی
نظرات مطالب
AngularJS #2
خود angularjs دارای یک سری api قدرتمند برای اعتبار سنجی هستند. البته انتظار helper‌های شسته رفته asp.net mvc را نداشته باشید که بر اساس model  بتواند اعتبار سنجی سمت کلاینت کند.
باید برایش خودتان دستی کد بنویسید. البته فکر نکنم تهیه‌ی helper برای angular که بر اساس model، کدهای متناظر اعتبار سنجی را تولید کند کار مشکلی باشد.
مطالب
چگونه پروژه‌های 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 قرار دارد.
اشتراک‌ها
انتشار TypeScript 4.6

Here’s a quick list of what’s new in TypeScript 4.6!

Allowing Code in Constructors Before super()
Control Flow Analysis for Destructured Discriminated Unions
Improved Recursion Depth Checks
Indexed Access Inference Improvements
Control Flow Analysis for Dependent Parameters
--target es2022
Removed Unnecessary Arguments in react-jsx
JSDoc Name Suggestions
More Syntax and Binding Errors in JavaScript
TypeScript Trace Analyzer
Breaking Changes
 

انتشار TypeScript 4.6
اشتراک‌ها
16 Angular در کنفرانس Google I/O 2023

Learn what's new in Angular announced during Google I/O 2023 including the latest updates from Angular v16 and how to get started with the new reactivity model Angular Signals. 

16 Angular در کنفرانس Google I/O 2023
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت نهم - مسیریابی
به روز رسانی

تمام مسیریابی‌های  این سری به نگارش سوم روتر AngularJS 2.0 به روز رسانی شدند.
ریز جزئیات تغییرات

توضیحات:

ابتدا نیاز است وابستگی‌های روتر جدید را به نحو ذیل به فایل package.json اضافه کنید:
 "dependencies": {
    // ...
    "@angular/router": "^3.0.0-alpha.7",
    // ...  
},
سپس
یک فایل جدید را به نام app.routes.ts به ریشه‌ی پروژه اضافه کنید، با این محتوا
import { provideRouter, RouterConfig } from '@angular/router';

import { ProductListComponent } from './products/product-list.component';
import { WelcomeComponent } from './home/welcome.component';
import { ProductDetailComponent } from './products/product-detail.component';
import { ProductFormComponent }  from './products/product-form.component';
import { SignupFormComponent } from './users/signup-form.component';
import { TypedShaComponent } from './using-third-party-libraries/typed-sha.component';
import { UnTypedShaComponent } from './using-third-party-libraries/untyped-sha.component';
import { UsingJQueryAddonsComponent } from './using-jquery-addons/using-jquery-addons.component';

export const routes: RouterConfig = [
    { path: '', component: WelcomeComponent },
    { path: 'welcome', component: WelcomeComponent },
    { path: 'products', component: ProductListComponent },
    { path: 'product/:id', component: ProductDetailComponent },
    { path: 'addproduct', component: ProductFormComponent },
    { path: 'adduser', component: SignupFormComponent },
    { path: 'typedsha', component: TypedShaComponent },
    { path: 'untypedsha', component: UnTypedShaComponent },
    { path: 'usingjquery', component: UsingJQueryAddonsComponent }
];

export const APP_ROUTER_PROVIDERS = [
  provideRouter(routes)
];
در اینجا مسیریابی‌های قدیمی برنامه از فایل app.component.ts خارج شده و به یک فایل مستقل منتقل شده‌اند.
در سیستم مسیریابی جدید، خاصیت‌های name و useAsDefault وجود ندارند و حذف شده‌اند. همچنین مسیریابی‌ها نباید با / شروع شوند.
به علاوه در فایل index.html، مسیر ریشه به نحو ذیل مشخص می‌شود:
 <base href=".">
پس از تعریف فایل app.routes.ts، نیاز است آن‌را به main.ts معرفی کرد:
 // ...
import { APP_ROUTER_PROVIDERS } from './app.routes';
// ...
bootstrap(AppComponent, [
   // ...
   APP_ROUTER_PROVIDERS
])
.catch(err => console.error(err));
به این ترتیب کار برپایی مسیریابی اصلی سایت به پایان می‌رسد.
البته باید دقت داشت که فایل systemjs.config.js هم کمی نیاز است جهت بارگذاری این مسیریاب جدید اصلاح شود.

در ادامه، در فایل app.component.ts، دایرکتیوهای مرتبط با مسیریابی که در ROUTER_DIRECTIVES قرار دارند، اینبار از ماژول ذیل تامین می‌شوند:
 import { ROUTER_DIRECTIVES } from '@angular/router';

سپس لینک‌های مسیریابی، اینبار بجای نام مسیریابی که در نگارش سوم روتر، حذف شده‌است، به همان مسیر متناظر اشاره می‌کند:
 <a [routerLink]="['/welcome']">Home</a>
این مورد جهت متدهای navigate هم صدق می‌کند و بجای نام، به مسیر مدنظر باید ویرایش شوند:
 this._router.navigate(['/products']);
تغییر مهم دیگر رخ داده، در مورد نحوه‌ی دسترسی به پارامترهای مسیریابی است که نمونه‌ای از آن‌را در مورد product-detail.component.ts در اینجا مشاهده می‌کنید:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';

@Component({
    templateUrl: 'app/products/product-detail.component.html'
    //template: require('./product-detail.component.html')//for webpack
})
export class ProductDetailComponent implements OnInit, OnDestroy {
    private sub: any;
    pageTitle: string = 'Product Detail';
    constructor(private _route: ActivatedRoute, private _router: Router) {
    }

    ngOnInit(): void {
        this.sub = this._route.params
            .subscribe(params => {
                let id = +params['id']; // (+) converts string 'id' to a number
                this.pageTitle += `: ${id}`;
            });
    }

    ngOnDestroy(): void {
        this.sub.unsubscribe(); // we must unsubscribe before Angular destroys the component. Failure to do so could create a memory leak.
    }

    onBack(): void {
        this._router.navigate(['/products']);
    }
}
اینبار سرویس RouteParams حذف شده‌است و بجای آن ActivatedRoute را داریم که خاصیت params آن، یک observable را باز می‌گرداند. به همین جهت باید متد subscribe آن‌را جهت دسترسی به پارامترهای مسیریابی، فراخوانی کرد. این فراخوانی نیز باید در متد ngOnInit باشد و همچنین برای جلوگیری از نشتی حافظه، باید در ngOnDestroy کار unsubscribe آن انجام شود.

یک اصلاح دیگر هم در اینجا داریم. لینک به صفحه‌ی جزئیات هر محصول اینبار به صورت زیر ویرایش می‌شود (در فایل product-list.component.html):
 <a [routerLink]="['/product', product.productId]">
  {{product.productName}}
</a>
نظرات مطالب
صفحه بندی و مرتب سازی خودکار اطلاعات به کمک jqGrid در ASP.NET MVC
مطالب گروه jqGrid را بررسی کنید. الزامی به استفاده از فرم‌های پیش فرض آن نیست. امکان inline add/edit هم دارد به همراه سفارشی سازی جزئیات آن‌ها. در مورد رویدادهای آن هم در لابلای مباحث این گروه با ارائه کدهای مرتبط، بحث شده‌است.
مطالب
بررسی روش تعریف انقیاد دو طرفه‌ی سفارشی در کامپوننت‌های Angular
برخلاف AngularJS، در برنامه‌های Angular امکانات two way data binding به صورت پیش‌فرض ارائه نمی‌شوند تا از تمام مشکلات آن مانند digest cycle ،watchers و غیره خبری نباشد. اما گاهی از اوقات نیاز است انقیاد دو طرفه‌ی سفارشی را بین دو کامپوننت ایجاد کنیم. در این مطلب روش ایجاد یک چنین انقیادهایی را بررسی خواهیم کرد و در اینجا در ابتدا نیاز است دو پیشنیاز Property Binding و Event Binding را بررسی کنیم که از جمع آن‌ها two way data binding حاصل می‌شود:


البته Angular به همراه دایرکتیو ویژه‌ای به نام ngModel است که two-way data binding را با import ماژول ویژه‌ی فرم‌ها میسر می‌کند:


که آن نیز در اصل از جمع Property Binding و Event Binding تشکیل شده‌است:
<input [ngModel]="username" (ngModelChange)="username = $event">
و یا به صورت خلاصه:
<input [(ngModel)]='username' />
در اینجا می‌خواهیم یک چنین امکانی را بدون استفاده از ngModel و ماژول فرم‌ها پیاده سازی کنیم.


انقیاد به خواص یا Property binding

فرض کنید دو کامپوننت والد و فرزند را ایجاد کرده‌ایم:


در کامپوننت والد، مقداری را توسط متد deposit هربار 100 آیتم افزایش می‌دهیم:
import { Component, OnInit } from "@angular/core";

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

  amount = 500;

  constructor() { }

  ngOnInit() {
  }

  deposit() {
    this.amount += 100;
  }
}
با این قالب:
<h2>Custom two way data binding</h2>

<div class="panel panel-primary">
  <div class="panel-heading">
    <h2 class="panel-title">Parnet Component</h2>
  </div>
  <div class="panel-body">
    <label>Available amount:</label> {{amount}}
    <button (click)="deposit()" class="btn btn-success">Deposit 100</button>
    <div>
      <app-child [amount]="amount"> </app-child>
    </div>
  </div>
</div>
که در آن مقدار amount کامپوننت والد نمایش داده شده‌است و همچنین این مقدار به خاصیت ورودی کامپوننتی به نام app-child نیز نسبت داده شده‌است.

کامپوننت فرزند به صورت ذیل تعریف می‌شود:
import { Component, OnInit, Input } from "@angular/core";

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

  @Input() amount: number;

  constructor() { }

  ngOnInit() {
  }

  withdraw() {
    this.amount -= 100;
  }
}
که در آن خاصیت amount، از والد آن، توسط ویژگی Input دریافت می‌شود. سپس در متد withdraw هربار می‌توان 100 آیتم را از آن کسر کرد.
با این قالب:
<div class="panel panel-default">
  <div class="panel-heading">
    <h2 class="panel-title">Child Component</h2>
  </div>
  <div class="panel-body">
    <label>Amount available: </label> {{amount}}

    <button (click)="withdraw()" class="btn btn-danger">Withdraw 100</button>
  </div>
</div>
که در آن مقدار amount فرزند نمایش داده شده‌است و همچنین امکان فراخوانی متد withdraw وجود دارد.

در اینجا زمانیکه data binding را به صورت ذیل تعریف می‌کنیم:
<app-child [amount]="amount"> </app-child>
روش مقدار دهی خاصیت amount داخل [] ، انقیاد به خواص نامیده می‌شود و سمت راست آن نیز یک خاصیت درنظر گرفته می‌شود. یعنی مقدار خاصیت amount والد (درون "") به مقدار خاصیت amount فرزند (درون []) نسبت داده خواهد شد.
این ارتباط نیز یک طرفه‌است. برای مثال اگر بر روی دکمه‌ی Deposit والد کلیک کنیم:


مقدار افزایش یافته‌ی در والد، به فرزند نیز منتقل می‌شود و نمایش داده خواهد شد. اما اگر بر روی دکمه‌ی withdraw فرزند کلیک کنیم:


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


انقیاد به رخ‌دادها یا Event binding

یک کامپوننت می‌تواند به رخ‌دادهای صادر شده‌ی توسط کامپوننتی دیگر گوش فرا دهد:
import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core";

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

  @Input() amount: number;
  @Output() amountChange = new EventEmitter();

  constructor() { }

  ngOnInit() {
  }

  withdraw() {
    this.amount -= 100;
    this.amountChange.emit(this.amount);
  }
}
برای این منظور در کامپوننت فرزند، یک خاصیت Output را به نام amountChange از نوع EventEmitter تعریف می‌کنیم. سپس جایی که قرار است کار کاهش amount صورت گیرد، با صدور رخ‌دادی (this.amountChange.emit)، این مقدار را به والد اعلام می‌کنیم.
اکنون در قالب کامپوننت والد، این رخ‌داد را درون یک () معرفی خواهیم کرد:
<app-child [amount]="amount" (amountChange)="this.amount= $event"> </app-child>
به این ترتیب زمانیکه کامپوننت فرزند، مقدار amount را تغییر می‌دهد، این مقدار توسط this.amountChange.emit به والد منتشر خواهد شد و می‌توان در سمت والد توسط event$ به آن دسترسی یافته و آن‌را به خاصیت this.amount کامپوننت والد نسبت دهیم.
اکنون اگر برنامه را آزمایش کنیم، با کلیک بر روی دکمه‌ی withdraw فرزند، مقدار کاهش یافته به والد نیز منعکس می‌شود:



پیاده سازی syntax ویژه‌ی Banana in a box

تا اینجا پیاده سازی two way data-binding سفارشی به پایان می‌رسد. اما تعریف طولانی:
<app-child [amount]="amount" (amountChange)="this.amount= $event"> </app-child>
به صورت ذیل هم قابل نوشتن و ساده سازی است:
<app-child [(amount)]="amount"> </app-child>
که به آن syntax ویژه Banana in a box نیز گفته می‌شود.
نکته‌ی ویژه‌ی آن، وجود پسوند Change در نام رخ‌داد تعریف شده‌است:
  @Input() amount: number;
  @Output() amountChange = new EventEmitter();
 اگر نام خاصیت Input مساوی x باشد، باید جهت فعالسازی syntax ویژه Banana in a box، نام رخ‌داد متناظر با آن دقیقا مساوی xChange انتخاب شود. مانند amount ورودی در اینجا و amountChange خروجی تعریف شده.

بنابراین به صورت خلاصه جهت تعریف یک انقیاد دو طرفه سفارشی:
- ابتدا باید انقیاد به یک خاصیت ورودی x را تعریف کرد.
- سپس نیاز است انقیاد به یک رخ‌داد خروجی هم‌نام، که نام آن، پسوند Change را اضافه‌تر دارد، یعنی xChange را تعریف کرد.
- اکنون می‌توان two-way data binding syntax ویژه‌ای را به نام banana in a box بر روی این‌دو تعریف کرد[(x)].


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید.