اشتراک‌ها
کتابخانه‌ی ngx-persian

A full featured tool set for Persian Applications Created by Angular (v > 2) containing Pipes, Services, Directives and javascript Similar Date object for working with Jalali Date. 

کتابخانه‌ی ngx-persian
اشتراک‌ها
بررسی 5 ویژگی جدید Angular v4

- if...else syntax in component HTML templates
- Stand alone animation module
- TypeScript's StrictNullChecks compliancy
- Angular Universal adoption by team to live in core
- Performance boost with FESM

بررسی 5 ویژگی جدید Angular v4
اشتراک‌ها
راه اندازی Angular 2 با Webpack

setting up Angular 2 with Webpack. Webpack optimizes module loading in the browser by minimizing the number of requests, and reducing the overall size.  

راه اندازی Angular 2 با Webpack
مطالب
پیاده سازی PWA در Angular
امروزه طراحی اپلیکیشن‌های موبایل بخش زیادی از جامعه را در برگرفته است و روز به روز در حال توسعه میباشند. موازی با رشد روز افزون و نیاز بیشتر به این اپلیکیشن‌ها فریمورک‌های زیادی نیز  ابداع شده اند. از جمله این فریم ورک‌ها می‌توان به موارد زیر اشاره کرد:

Ionic  , react native , flutter , xamarin ….

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

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

1-  نوشتن کد به مراتب آسانتر است.
۲- چون اکثر فریم ورک‌ها، فریم ورک‌های جاوا اسکریپتی هستند، سرعت بالایی هنگام run کردن برنامه دارند ولی در build آخر و خروجی نهایی بر روی پلتفرم، این سرعت کندتر می‌شود.
۳- cossplatform بودن. با طراحی یک اپلیکیشن برای یک پلتفرم می‌توان همزمان برای پلتفرم‌های دیگر خروجی گرفت.


معایب:

۱- برنامه نویس را محدود  میکند.
۲- سرعت اجرای پایینی دارد.
۳- حجم برنامه به مراتب بالاتر میباشد.
۴- از منابع سخت افزاری بیشتری استفاده می‌کند.

اگر شما هم میخواهید از فریم ورک‌ها برای طراحی اپلیکیشن استفاده کنید در اینجا  می‌توانید اطلاعات بیشتری را درباره هر کدام از فریم ورک‌ها ببینید. هر کدام از این فریم ورک‌ها دارای مزایا معایب و همچنین رزومه کاری خوب می‌باشند. بیشتر فریم ورک‌های بالا در رندر آخر، همان کد native را تولید می‌کنند؛ مثلا اگر برنامه‌ای را با react native بنویسید، میتوانید همان برنامه را بر روی اندروید استودیو و کد native بالا بیارید. توضیحات  بالا مقایسه فریم ورک‌های Cross Platform با زبان‌های native بود. اما در این مقاله قصد آشنایی با تکنولوژی جدیدی را داریم که شما را قادر می‌سازد یک وب اپلیکیشن را با آن طراحی کنید.


 pwa چیست؟

pwa مخفف Progressive Web Apps است و یک تکنولوژی برای طراحی وب اپلیکیشن‌های تحت مرورگر میباشد. شما با pwa میتوانید اپلیکیشن خود را بر روی پلتفرم‌های مختلفی اجرا کنید، طوری که کاربران متوجه نشوند با یک صفحه‌ی وبی دارند کار میکنند. شاید شما هم فکر کنید این کار را هم میتوان با html ,css و responsive کردن صفحه انجام داد! ولی اگر بخواهید کاربر متوجه استفاده‌ی از یک صفحه‌ی وبی نشود، باید حتما از pwa استفاده کنید! برای مثال یک صفحه‌ی وبی معمولی حتما باید در بستر اینترنت اجرا شود، ولی با pwa با یکبار وصل شدن به اینترنت و کش کردن داده‌ها، برای بار دوم دیگر نیازی به اینترنت ندارد و میتواند به صورت offline  کار کند. شما حتی با pwa می‌توانید اپلیکیشن را در background اجرا کنید و notification ارسال کنید.


مزیت pwa

۱- یکی از مزیت‌های مهم pwa، حالت offline آن میباشد که حتی با قطع اینترنت، شما میتوانید همچنان با اپلیکیشن کار کنید.
۲- با توجه به اینکه شما در حقیقت با یک صفحه‌ی وبی کار میکنید، دیگر نیازی به دانلود و نصب ندارید.
۳- امکان به‌روز رسانی کردن، بدون اعلام کردن نسخه جدید.
۴- از سرعت بسیار زیادی برخوردار است.
۵- چون pwa از پروتکل https استفاده می‌کند، دارای امنیت بالایی میباشد.


معایب

۱- محدود به مرورگر می‌باشد.
۲- هرچند امروزه اکثر مرورگر‌ها pwa را پشتیبانی میکنند، ولی در بعضی از مرورگر‌ها و مرورگر‌های با ورژن پایین، pwa پشتیبانی نمیشود.
3- نمیتوان یک برنامه‌ی مبتنی بر os را نوشت و محدود به مرورگر میباشد


چند نکته درباره pwa

1- برای pwa  لزومی ندارد حتما از فریم ورک‌های spa استفاده کنید. شما از هر فریم ورک Client Side ای می‌توانید استفاده کنید.
۲- چون صفحات شما در پلتفرم‌های مختلف و با صفحه نمایش‌های مختلفی اجرا می‌شود، باید صفحات به صورت کاملا responsive شده طراحی شوند.
۳- باید از پروتکل https استفاده کنید.

ما در این مقاله از فریم ورک angular  استفاده  خواهیم کرد.

قبل از شروع، با شیوه کار pwa آشنا خواهیم شد. یکی از قسمت‌های مهم  Service Worker ،pwa میباشد که از جمله کش کردن، notification فرستادن و اجرای پردازش‌ها در پس زمینه را بر عهده دارد.


با توجه به شکل بالا، Service Worker مانند یک لایه‌ی نرم افزاری مابین کلاینت و سرور قرار دارد که درخواست‌های داده شده از کلاینت را در صورت اتصال به اینترنت، ارسال کرده و مجددا response را برگشت میدهد‌. اگر به هر دلیلی اینترنت قطع باشد، درخواست به صورت آفلاین به کش مرورگر که توسط proxy ساخته شده است، فرستاده میشود و response را برگشت میدهد.


چند نکته  در رابطه با Service Worker

- نباید برای  نگهداری داده global از Service Worker استفاد کرد. برای استفاده از داده‌های Global میتوان از Local Storage یا IndexedDB استفاده کرد.
- service worker  به dom دسترسی ندارند.


 سناریو

 قبل از شروع، سناریوی پروژه را تشریح خواهیم کرد. رکن اصلی یک برنامه‌ی وب، UI آن میباشد و قصد داریم کاربران را متوجه کار با یک صفحه‌ی وبی نکنیم. قالبی که ما در این مثال در نظر گرفتیم خیلی شبیه به یک اپلیکیشن پلتفرم اندرویدی میباشد. یک کاربر با کشیدن منوی کشویی میتواند گزینه‌های خود را انتخاب نمایند. اولین گزینه‌ای که قصد پیاده سازی آن را داریم، ثبت کاربران میباشد. بعد از ثبت کاربران در یک Component جدا، کاربران را در یک جدول نمایش خواهیم داد.


 قبل از شروع لازم است به چند نکته زیر توجه کرد

1-  سرویس crud را به صورت کامل در پروژه قرار خواهیم داد، ولی چون از حوصله‌ی مقاله خارج است، فقط ثبت کاربران و نمایش کاربران را پیاده سازی خواهیم کرد.
2 - هر اپلیکیشنی قطعا نیاز به یک وب سرویس دارد که واسط بین دیتابیس و اپلیکیشن باشد. ولی ما چون در این مثال به عنوان front end کار قصد طراحی  اپلیکیشن را داریم، برای حذف back end از firebase استفاده خواهیم کرد و مستقیما از انگولار به دیتابیس firebase کوئری خواهیم زد.


 شروع به کار

پیش نیاز‌های یک پروژه‌ی انگیولاری را بر روی سیستم خود فراهم کنید. ما در این مثال از یک template آماده انگیولاری استفاده خواهیم کرد. پس برای اینکه با جزئیات و طراحی ui درگیر نشویم، از لینک github پروژه را دریافت کنید.
سپس وارد root پروژه شوید و با دستور زیر پکیج‌های پروژه را نصب کنید:
 npm install
پس از نصب پکیج‌ها، با دستور ng serve، پروژه را اجرا کنید.

پس از اجرا باید خروجی بالا را داشته باشید. می‌خواهیم یکی از قابلیت‌های pwa را بررسی کنیم. بر روی صفحه کلیک راست کرده و وارد inspect شوید. وارد تب Application  شوید و Service Worker را انتخاب کنید.
 


قبل از اینکه کاری را انجام دهید، چند بار صفحه را refresh کنید! صفحه بدون هیچ تغییری refresh میشود. اینبار گزینه‌ی offline را فعال کنید و مجددا صفحه را refresh کنید.
این بار صفحه No internet به معنی قطع بودن اینترنت نمایش داده میشود و شما دیگر نمی‌توانید بر روی اپلیکیشن خود فعالیتی داشته باشید! 


نصب pwa  بر روی پروژه

برای اضافه کردن pwa به پروژه وارد ریشه‌ی پروژه شوید و دستور زیر را وارد کنید:
 ng add @angular/PWA
پس از  اجرای دستور بالا، تغییراتی در پروژه لحاظ میشود از جمله در فایل‌هایindex.html,app-module,angular.json، همچنین یک پوشه‌ی جدید به نام icons در assets و دو فایل جیسونی زیر به روت پروژه اضافه شده‌اند:
 Manifest.json : اگر محتویات فایل را مشاهده کرده باشید، شامل تنظیمات فنی وب اپلیکیشن می‌باشد؛ از جمله Home Screen Icon و نام وب اپلیکیشن و سایر تنظیمات دیگر.
 Nsgw-config.json : این فایل نسبت به فایل manifest فنی‌تر میباشد و بیشتر به کانفیگ مد آفلاین و کش کردن مرتبط میشود. در ادامه با این فایل بیشتر کار داریم.

بعد از نصب pwa  بر روی پروژه، همه تنظیمات به صورت خودکار انجام می‌شود. البته می‌توانید تنظیمات مربوط به کش کردن داده‌ها را به صورت پیشرفته‌تر کانفیگ کنید.


  اجرا کردن وب اپلیکیشن

 برای اجرا کردن و نمایش خروجی از وب اپلیکیشن، ابتدا باید از پروژه build گرفت. با استفاده از دستور زیر از پروژه خود build بگیرید:
 ng build -- prod
سپس  با دستور زیر وارد پوشه‌ای که فایل‌های Build  شده در آن قرار دارند، شوید:
 cd dist/Web Application  Pwa
بعد با دستور زیر برنامه را اجرا کنید:
 Http-server -o
اگر چنانچه با دستور بالا به خطا برخوردید، با دستور زیر پکیج npm آن را نصب کنید:
 npm i http-server
اگر تا به اینجای کار درست پیش رفته باشید، پروژه را بدون هیچ تغییری مشاهده خواهیم کرد. inspect گرفته و وارد تب Application شوید. اینبار گزینه‌ی offline را فعال کنید و مجددا صفحه را refresh کنید! اینبار برنامه بدون هیچ مشکلی کار میکند. حتی شما میتوانید در کنسول، برنامه را به صورت کامل stop کنید.


برسی فایل Nsgw-config.json 

وارد فایل Nsgw-config.json شوید: 

"$schema": "./node_modules/@angular/service-worker/config/schema.json",
  "index": "/index.html",
  "assetGroups": [
    {
      "name": "app",
      "installMode": "prefetch",
      "resources": {
        "files": [
          "/favicon.ico",
          "/index.html",
          "/*.css",
          "/*.js"
        ]
      }
    }, {
      "name": "assets",
      "installMode": "lazy",
      "updateMode": "prefetch",
      "resources": {
        "files": [
          "/assets/**",
          "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
        ]
      }

    }
  ],
  "dataGroups": [
    {
      "name": "api-performance",
      "urls": [
        "https://api/**"
      ],
      "cacheConfig": {
        "strategy": "performance",
        "maxSize": 100,
        "maxAge": "3d"
      }
    }
  ]
}
 این فایل مربوط به کش کردن داده‌ها میباشند و میتواند شامل چند object زیر باشد. مهمترین آنها را بررسی خواهیم کرد:
  ۱- assetGroups : کش کردن اطلاعات مربوط به اپلیکیشن
  ۲- Index : کش کردن  فایل مربوط به index.html
  ۳-  assetGroups : کش کردن فایل‌های مربوط به asset، شامل فایل‌های js، css  و  غیره
  ۴- dataGroups : این object مربوط به وقتی است که برنامه در حال اجرا است. میتوان داده‌های در حال اجرای اپلیکیشن را کش کرد. داده‌ی در حال اجرا میتواند شامل فراخوانی apiها باشد. برای مثال فرض کنید شما در حالت کار کردن online با اپلیکیشن، لیستی از اسامی کاربران را از api گرفته و نمایش میدهید. وقتی دفعه‌ی بعد در حالت offline  اپلیکیشن را باز کنیم، اگر api را کش کرده باشیم، اپلیکیشن داده‌ها را از کش فراخوانی میکند. این عمل درباره post کردن داده‌ها هم صدق میکند.

خود dataGroups شامل چند object زیر میباشد:
  ۱- name :  یک نام انتخابی برای Groups  میباشد.
 ۲- urls  : شامل آرایه‌ای از آدرس‌ها میباشد. میتوان آدرس یک دومین را همراه با کل apiها به صورت زیر کش کرد:
"https://api/**"
۳- cacheConfig  : که شامل
  ۱- maxSize  : حداکثر تعداد کش‌های مربوط به Groups .
  ۲- maxAge  : حداکثر  lifetime مربوط به کش.
  ۳- strategy  : که میتواند یکی از مقادیر freshness به معنی Network-First یا performance  به معنی Cache-First باشد.


 پیاده سازی پیغام نمایش به‌روزرسانی جدید وب اپلیکیشن

در اپلیکیشن‌های native  وقتی به‌روزرسانی جدیدی برای app اعلام میشود، در فروشگاهای اینترنتی پیغامی مبنی بر به‌روزرسانی جدید app برای کاربران ارسال میشود که کاربران میتوانند app خود را به‌روزرسانی کنند. ولی در pwa تنها با یک رفرش صفحه میتوان اپلیکیشن را به جدیدترین امکانات به‌روزرسانی کرد! برای اینکه بتوانیم با هر تغییر، پیغامی را جهت به‌روزرسانی نسخه یا هر  پیغامی دیگری را نمایش دهیم، از کد زیر استفاده میکنم:
if(this.swUpdate.isEnabled)
    {
      this.swUpdate.available.subscribe(()=> {

        if(confirm("New Version available.Load New Version?")){
          window.location.reload();
        }
      })
    }


وصل شدن به دیتابیس

برای اینکه بتوانیم یک وب اپلیکیشن کامل را طراحی کنیم، قطعا نیاز به دیتابیس داریم. برای دیتابیس از firebase استفاده میکنیم. قبل از شروع، وارد سایت firebase  شوید. پس از لاگین، بر روی Add Project کلیک کنید. بعد از انتخاب نام مناسب، create Project را انتخاب کنید. ساختار دیتابیس‌های firebase  شبیه nosqlها می‌باشد و بر پایه‌ی json کار میکنند. پس از ساخت پروژه و دریافت کد جاواسکریپتی زیر شروع به طراحی فیلد‌های دیتابیس خواهیم کرد.


در منوی سمت چپ، بر روی database کلیک کنید و یک دیتابیس را در حالت test mode  ایجاد نماید. سپس یک collection را به نام user ایجاد کرده و فیلد‌های زیر را به آن اضافه کنید:
Age :number
Fullname :string
Mobile : string
مرحله‌ی بعد، config پروژه‌ی انگیولاری میباشد. وارد  vscode شوید و با دستور زیر پکیج firebase را اضافه کنید:
  npm install --save firebase @angular/fire
سپس وارد appmodule شوید و آن‌را به صورت زیر کانفیگ کنید:

@NgModule({
  declarations: [AppComponent, RegisterComponent,AboutComponent, UserListComponent],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule,
    HttpClientModule,
    SharedModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(environmentFirebase.firebase),
    AngularFireDatabaseModule,
    ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
  ],
  providers: [FirebaseService],
  bootstrap: [AppComponent]
})
export class AppModule {}
خط ۱۰ مربوط به pwa است که هنگام نصب pwa اضافه شده‌است و خط ۸ و مربوط به apiKey فایربیس میباشد. می‌شود گفت environmentFirebase.firebase شبیه  connection string می‌باشد که به صورت زیر کانفیگ شده است:
export const environment ={
   production: true,
   firebase: {
     apiKey: "AIz×××××××××××××××××××××××××××××××××8",
     authDomain: "pwaangular-6c041.firebaseapp.com",
     databaseURL: "https://pwaangular-6c041.firebaseio.com",
     projectId: "pwaangular-6c041",
     storageBucket: "pwaangular-6c041.appspot.com",
     messagingSenderId: "545522081966"
   }

}

FirebaseService در قسمت providers مربوط به سرویس crud میباشد. اگر وارد فایل مربوطه شوید، چند عمل اصلی به صورت زیر در آن پیاده سازی شده است:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import * as firebase from 'firebase';
import { AngularFireDatabase } from '@angular/fire/database';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { ThrowStmt } from '@angular/compiler';
@Injectable({
  providedIn: 'root'
})
export class FirebaseService {

  ref = firebase.firestore().collection('users');
  constructor(public db: AngularFireDatabase,public _http:HttpClient) { 
 
  }

  getUsers(): Observable<any> {
    return new Observable((observer) => {
      this.ref.onSnapshot((querySnapshot) => {
        let User = [];
        querySnapshot.forEach((doc) => {
          let data = doc.data();
          User.push({
            key: doc.id,
            fullname: data.fullname,
            age: data.age,
            mobile: data.mobile
          });
        });
        observer.next(User);
      });
    });
  }

  getUser(id: string): Observable<any> {
    return new Observable((observer) => {
      this.ref.doc(id).get().then((doc) => {
        let data = doc.data();
        observer.next({
          key: doc.id,
          title: data.title,
          description: data.description,
          author: data.author
        });
      });
    });
  }

  postUser(user): Observable<any> {
    return new Observable((observer) => {
      
      this.ref.add(user).then((doc) => {
        observer.next({
          key: doc.id,
        });
      });
    });
  }

  updateUser(id: string, data): Observable<any> {
    return new Observable((observer) => {
      this.ref.doc(id).set(data).then(() => {
        observer.next();
      });
    });
  }

  deleteUser(id: string): Observable<{}> {
    return new Observable((observer) => {
      this.ref.doc(id).delete().then(() => {
        observer.next();
      });
    });
  }

getDataOnApi(){
  return this._http.get('https://site.com/api/General/Getprovince')
  .pipe(
      map((res: Response) => {
return res;

      })
  );
}

getOnApi(){
  return  this._http.get("https://site.com/api/General/Getprovince",).pipe(
    map((response:any) => {
       
return  response
    } )
    );
}
}
اگر دقت کرده باشید، خیلی شبیه به کد نویسی سمت سرور میباشد.

با دستورات زیر میتوانید مجددا پروژه را اجرا کنید:
ng build --prod
cd dist/Pwa-WepApp
Http-server -o


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

برای اینکه بتوانیم خروجی وب اپلیکیشن را بر روی پلتفرم‌های مختلفی تست کنیم، میتوانیم آن را آپلود کرده و مثل یک سایت اینترنتی، با وارد کردن دومین، وارده پروژه شد. ولی کم هزینه‌ترین راه، استفاده از ابزار ngrok میباشد. میتوانید توسط این مقاله پروژه خودتان در لوکال بر روی https سوار کنید.

نکته! توجه کنید apiهای مربوط به firebase را نمیتوان کش کرد.


کد‌های مربوط به این قسمت را میتوانید از این repository دریافت کنید .
مطالب
Embed کردن SQL Server Express 2008 در یک برنامه
مقدمه
نصب Microsoft Sql Server یکی از عملیات مشکل برای کاربر نهایی می‌باشد. برای رفع این مشکل، باید آنرا بصورت اتوماتیک و بدون درگیر کردن کاربر آن را نصب کنیم.برای اینکار دو روش موجود است:
1) استفاده از فایل Configure.ini
2) ارسال پارامتر به فایل Setup.exe از طریق Command Prompt

قابلیت‌های نسخه‌های مختلف Sql Server Express 2008
 نسخه / قابلیت Database Engine Management Studio Basic Full-Text Search Reporting Services
Management Studio Basic    X    
 Runtime Only
 X      
 with Tools   X  X    
  with Advanced Services     X  X  X  X

(SQL Server 2008 Management Studio Express (SSMSE
ابزارهای مدیریتی گرافیکی رایگان برای پیکربندی، مدیریت و اداره کردن برنامه‌های SQL Server Express 2008
استفاده برای مدیریت چندین نمونه از موتور پایگاه داده SQL Server که توسط نسخه‌های مختلف SQL Server 2008 ساخته شده اند.

(SQL Server 2008 Express (Runtime Only
موتور پایگاه داده SQL Server برای ساخت، ذخیره سازی، بروز رسانی و واکشی داده

SQL Server 2008 Express with Tools
موتور پایگاه داده SQL Server برای ساخت، ذخیره سازی، بروز رسانی و واکشی داده
SQL Server Management Studio Basic - یک ابزار مدیریتی ویژوال برای ساخت، ویرایش و مدیریت پایگاه‌های داده

SQL Server 2008 Express with Advanced Services
موتور پایگاه داده SQL Server برای ساخت، ذخیره سازی، بروز رسانی و واکشی داده
SQL Server Management Studio Basic - یک ابزار مدیریتی ویژوال برای ساخت، ویرایش و مدیریت پایگاه‌های داده
Full-Text Search - یک موتور قدرتمند و پرسرعت برای جستجوی داده‌های متن-فشرده

دو حالت برای نصب SQL Server وجود دارد:
نصب یک نمونه جدید
آپگرید

نصب SQL Server Express 2008 از طریق Command Prompt
1) پس از دانلود SQL Server Express 2008 با پارامتر X/ انرا از حالت فشرده خارج کنید.
برای این کار Command Prompt (یا همان cmd) را باز کنید و با دستور cd به مسیری که فایل  SQL Server Express هست بروید.
حالا نام فایل نصب را همراه با پارامتر X/ در cmd تایپ کنید. مثلا: SQLExpr32_x86_enu.exe /x
نکته: اگر فایل یا سی دی نصب ویژوال استودیو 2010 را دارید می‌توانید فایل نصب SQL Server Express را در مسیر WCU\SSE\ پیدا کنید.
2) همانطور که می‌بینید یک مسیر برای Extract از شما خواسته شده. یک مسیر وراد کنید.
3) در مسیر ساخته شده، یک فایل با پسوند bat. بسازید و آنرا یا یک ویرایشگر متنی باز کنید و دستورات زیر را در آن تایپ کنید:
Setup.exe /q /Action=Install /Hideconsole /Features=SQL,Tools
/InstanceName=SQLExpress /SQLSYSADMINACCOUNTS="Builtin\Administrators"
/SQLSVCACCOUNT="<DomainName\UserName>" /SQLSVCPASSWORD="<StrongPassword>

توضیح پارامترهای فوق بشرح زیر است:
q/ (اختیاری): اینکه Setup به حالت خاموش (quiet mode) و بدون رابط کاربری اجرا شود.
Action/ (الزامی): عملیاتی که باید انجام شود. این پارامتر مقدار install و upgrade را قبول می‌کند.
Features/ (الزامی): اینکه کدام قابلیت‌های SQL Server باید نصب شوند.
HideConsole/ (اختیاری): اگر از این پارامتر استفاده شود، پنجره کنسول نمایش داده نخواهد شد.
InstanceName/ (الزامی): نام نمونه ایی که باید نصب شود.
SQLSYSADMINACCOUNTS/ (الزامی): مقررات لوگین برای اعضای با نقش "مدیر سیستم".
SQLSVCACCOUNT/ (الزامی): تعیین اکانتی که سرویس SQL Server را در Startup ویندوز اجرا کند.
SQLSVCACCOUNT/ (اگر از یک اکانت لوکال یا تحت Domain استفاده کنید الزامی است): تعیین پسورد اکانت پارامتر SQLSVCACCOUNT.

باتوجه به سیاست‌ها نصب می‌توانید از پارامترها دیگری نیز استفاده کنید. بعنوان مثال پارامترهای زیر برای نصب روی سیستمی که نام کاربری و پسورد انرا نداریم مناسب است:
setup.exe /q /Action=Install /Features=SQL /InstanceName=SQLExpress /SECURITYMODE=SQL /SAPWD="1234567" 
/SQLSYSADMINACCOUNTS="Builtin\Administrators" /SQLSVCACCOUNT="NT AUTHORITY\SYSTEM" 
/SQLSVCSTARTUPTYPE="Automatic" /TCPENABLED=1 
نکته: اگر مقدار پارامتر SECURITYMODE برابر SQL باشد حتما باید پارامتر SAPWD مقداردهی شود.
نکته: اگر مقدار TCPENABLED برابر 1 باشد پروتکل TCP/IP فعال می‌شود. اگر هیچ نمونه‌ی دیگری روی سیستم نصب نباشد مقدار TCP Port برابر 1433 است، درغیر اینصورت یک مقدار تصادفی تولید می‌شود.
نکته: برای خوانایی بیشتر، پارامترهای فوق در چند خط نوشته شده اند. برای اجرای صحیح در یک فایل bat، همه انها باید در یک خط باشند.
برای اولین بهتر است از پارامتر HideConsel/ استفاده نکنید تا خطای احتمالی رو مشاهده کنید. برای آپگرید کردن نیز می‌توانید از دستور زیر استفاده کنید:
Setup.exe /q /Hideconsole /ACTION=upgrade /INSTANCENAME=SQLExpress

برای مشاهده دیگر پارامترها به مستندات MSDN مراجعه کنید. همچنین می‌توان نصب از طریق فایل Configuration را نیز انجام داد.
امیدوارم مفید واقع شده باشد.
مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت چهارم - data binding
در قسمت قبل، نگاهی مقدماتی داشتیم به مبحث data binding. در ادامه، این مبحث را به همراه pipes، جهت اعمال تغییرات بر روی اطلاعات، پیگیری خواهیم کرد.


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

قابلیت property binding این امکان را فراهم می‌کند که یکی از خواص المان‌های HTML را به مقادیر دریافتی از کلاس کامپوننت، متصل کنیم:
 <img [src]='producr.imageUrl'>
در این مثال، خاصیت src المان تصویر، به آدرس تصویر یک محصول متصل شده‌است.
در حین تعریف property binding، مقصد اتصال، داخل براکت‌ها قرار می‌گیرد و خاصیت مدنظر المان را مشخص می‌کند. منبع اتصال همیشه داخل "" در سمت راست علامت مساوی قرار می‌گیرد.
اگر اینکار را بخواهیم با interpolation معرفی شده‌ی در قسمت قبل انجام دهیم، به کد ذیل خواهیم رسید:
 <img src={{producr.imageUrl}}>
در اینجا نه از []، برای معرفی مقصد اتصال استفاده شده‌است و نه از "" برای مشخص سازی منبع اتصال. این نوع اتصال یک طرفه است (از منبع به مقصد).

خوب، در یک چنین مواردی property binding بهتر است یا interpolation؟
توصیه‌ی کلی ترجیح property binding به interpolation است. اما اگر در اینجا نیاز به انجام محاسباتی بر روی عبارت منبع وجود داشت، باید از interpolation استفاده کرد؛ مانند:
 <img src='http://www.mysite.com/images/{{producr.imageUrl}}'>


تکمیل قالب کامپوننت لیست محصولات

اگر از قسمت قبل به خاطر داشته باشید، در فایل product-list.component.html، لیست پردازش شده‌ی توسط ngFor*، فاقد ستون نمایش تصاویر محصولات است. به همین جهت فایل یاد شده را گشوده و سپس با استفاده از property binding، دو خاصیت src و title تصویر را به منبع داده‌ی آن متصل می‌کنیم:
<tbody>
    <tr *ngFor='#product of products'>
        <td>
            <img [src]='product.imageUrl'
                 [title]='product.productName'>
        </td>
        <td>{{ product.productName }}</td>
        <td>{{ product.productCode }}</td>
        <td>{{ product.releaseDate }}</td>
        <td>{{ product.price }}</td>
        <td>{{ product.starRating }}</td>
    </tr>
</tbody>
در این حالت اگر برنامه را اجرا کنیم به خروجی ذیل خواهیم رسید:


هرچند اینبار تصاویر محصولات نمایش داده شده‌اند، اما اندکی بزرگ هستند. بنابراین در ادامه با استفاده از property binding، خواص style آن‌را تنظیم خواهیم کرد. برای این منظور فایل product-list.component.ts را گشوده و به کلاس ProductListComponent، دو خاصیت imageWidth و imageMargin را اضافه می‌کنیم:
export class ProductListComponent {
    pageTitle: string = 'Product List';
    imageWidth: number = 50;
    imageMargin: number = 2;
    products: any[] = [
        // as before...
    ];
}
البته همانطور که پیشتر نیز ذکر شد، چون مقادیر پیش فرض این خواص عددی هستند، نیازی به ذکر صریح نوع number در اینجا وجود ندارد (type inference).
پس از تعریف این خواص، امکان دسترسی به آن‌ها در قالب کامپوننت وجود خواهد داشت:
<tbody>
    <tr *ngFor='#product of products'>
        <td>
            <img [src]='product.imageUrl'
                 [title]='product.productName'
                 [style.width.px]='imageWidth'
                 [style.margin.px]='imageMargin'>
        </td>
همانطور که مشاهده می‌کنید، چون خاصیت‌های جدید تعریف شده، جزئی از خواص اصلی کلاس هستند و نه خواص اشیاء لیست محصولات، دیگر به همراه .product ذکر نشده‌اند.
همچنین در اینجا نحوه‌ی style binding را نیز مشاهده می‌کنید. مقصد اتصال همیشه با [] مشخص می‌شود و سپس کار با ذکر .style شروع شده و پس از آن نام خاصیت مدنظر عنوان خواهد شد. اگر نیاز به ذکر واحدی وجود داشت، پس از درج نام خاصیت، قید خواهد شد. برای مثال [style.fontSize.em] و یا [%.style.fontSize]


یک نکته:
اگر مثال را قدم به قدم دنبال کرده باشید، با افزودن style binding و بارگذاری مجدد صفحه، احتمالا تغییراتی را مشاهده نخواهید کرد. این مورد به علت کش شدن قالب قبلی و یا فایل جاوا اسکریپتی متناظر با آن است (فایلی که خواص عرض و حاشیه‌ی تصویر به آن اضافه شده‌اند).
یک روش ساده‌ی حذف کش آن، بازکردن آدرس http://localhost:2222/app/products/product-list.component.js در مرورگر به صورت مجزا و سپس فشردن دکمه‌های ctrl+f5 بر روی آن است.


پاسخ دادن به رخدادها و یا event binding

تا اینجا تمام data binding‌های تعریف شده‌ی ما یک طرفه بودند؛ از خواص کلاس کامپوننت به اجزای قالب متناظر با آن. اما گاهی از اوقات نیاز است تا با کلیک کاربر بر روی دکمه‌ای، عملی خاص صورت گیرد و در این حالت، جهت ارسال اطلاعات، از قالب کامپوننت، به متدها و خواص کلاس متناظر با آن خواهند بود. کامپوننت به اعمال کاربر از طریق event binding گوش فرا می‌دهد:
 <button (click)='toggleImage()'>
syntax آن بسیار شبیه است به حالت property binding و در اینجا بجای [] از () جهت مشخص سازی رخدادی خاص از المان مدنظر استفاده می‌شود. سمت راست این انتساب، متدی است که داخل "" قرار می‌گیرد و این متد متناظر است با متدی مشخص در کلاس متناظر با کامپوننت جاری.
در این حالت اگر کاربر روی دکمه‌ی تعریف شده کلیک کند، متد toggleImage موجود در کلاس متناظر، فراخوانی خواهد شد.
چه رخدادهایی را در اینجا می‌توان ذکر کرد؟ پاسخ آن‌را در آدرس ذیل می‌توانید مشاهده کنید:
https://developer.mozilla.org/en-US/docs/Web/Events

این syntax جدید AngularJS 2.0 سطح API آن‌را کاهش داده است. دیگر در اینجا نیازی نیست تا به ازای هر رخداد ویژه‌ای، یک دایرکتیو و یا syntax خاص آن‌را در مستندات آن
جستجو کرد. فقط کافی است syntax جدید (نام رخداد) را مدنظر داشته باشید.


تکمیل مثال نمایش لیست محصولات با فعال سازی دکمه‌ی Show Image آن

در اینجا قصد داریم با کلیک بر روی دکمه‌ی Show image، تصاویر موجود در ستون تصاویر، مخفی و یا نمایان شوند. برای این منظور خاصیت جدیدی را به نام showImage به کلاس ProductListComponent اضافه می‌کنیم. بنابراین فایل product-list.component.ts را گشوده و تغییر ذیل را به آن اعمال کنید:
export class ProductListComponent {
    pageTitle: string = 'Product List';
    imageWidth: number = 50;
    imageMargin: number = 2;
    showImage: boolean = false;
در اینجا خاصیت Boolean جدیدی به نام showImage با مقدار اولیه‌ی false تعریف شده‌است. به این ترتیب تصاویر، در زمان اولین بارگذاری صفحه نمایش داده نخواهند شد.
سپس به انتهای کلاس، پس از تعاریف خواص، متد جدید toggleImage را اضافه می‌کنیم:
export class ProductListComponent {
    // as before ...
 
    toggleImage(): void {
        this.showImage = !this.showImage;
    }
}
در کلاس‌های TypeScript نیازی به ذکر صریح واژه‌ی کلیدی function برای تعریف متدی وجود ندارد. این متد، خروجی هم ندارد، بنابراین نوع خروجی آن void مشخص شده‌است. در بدنه‌ی این متد، وضعیت خاصیت نمایش تصویر معکوس می‌شود.
پس از این تغییرات، اکنون می‌توان به قالب این کامپوننت یا فایل product-list.component.html مراجعه و event binding را تنظیم کرد:
<button class='btn btn-primary'
        (click)='toggleImage()'>
    Show Image
</button>
در اینجا click به عنوان رخداد مقصد، مشخص شده‌است. سپس آن‌را به متد toggleImage کلاس ProductListComponent متصل می‌کنیم.
خوب، تا اینجا اگر کاربر بر روی دکمه‌ی show image کلیک کند، مقدار خاصیت showImage کلاس ProductListComponent با توجه به کدهای متد toggleImage، معکوس خواهد شد.
مرحله‌ی بعد، استفاده از مقدار این خاصیت، جهت مخفی و یا نمایان ساختن المان تصویر جدول نمایش داده شده‌است. اگر از قسمت قبل به خاطر داشته باشید، کار ngIf*، حذف و یا افزودن المان‌های DOM است. بنابراین ngIf* را به المان تصویر جدول اضافه می‌کنیم:
<tr *ngFor='#product of products'>
    <td>
        <img *ngIf='showImage'
             [src]='product.imageUrl'
             [title]='product.productName'
             [style.width.px]='imageWidth'
             [style.margin.px]='imageMargin'>
    </td>
با توجه به ngIf* تعریف شده، المان تصویر تنها زمانی به DOM اضافه خواهد شد که مقدار خاصیت showImage مساوی true باشد.

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

یک مشکل! در هر دو حالت نمایش و مخفی سازی تصاویر، برچسب این دکمه Show image است. بهتر است زمانیکه قرار است تصاویر مخفی شوند، برچسب hide image نمایش داده شود و برعکس. برای حل این مساله از interpolation به نحو ذیل استفاده خواهیم کرد:
<button class='btn btn-primary'
        (click)='toggleImage()'>
    {{showImage ? 'Hide' : 'Show'}} Image
</button>
در اینجا اگر مقدار خاصیت showImage مساوی true باشد، مقدار رشته‌ای Hide و اگر false باشد، مقدار رشته‌ای show بجای {{}} درج خواهد شد.



بررسی انقیاد دو طرفه یا two-way binding

تا اینجا، اتصال مقدار یک خاصیت عمومی کلاس متناظر با قالبی، به اجزای مختلف آن، یک طرفه بودند. اما در ادامه نیاز است تا بتوان برای مثال در textbox قسمت filter by مثال جاری بتوان اطلاعاتی را وارد کرد و سپس بر اساس آن ردیف‌های جدول نمایش داده شده را فیلتر نمود. این عملیات نیاز به انقیاد دو طرفه یا two-way data binding دارد.
برای تعریف انقیاد دو طرفه در AngularJS 2.0 از دایرکتیو توکاری به نام ngModel استفاده می‌شود:
 <input [(ngModel)]='listFilter' >
ابتدا [] ذکر می‌شود تا مشخص شود که این عملیات در اصل یک property binding است؛ از خاصیت عمومی به نام listFilter به المان textbox تعریف شده.
سپس () تعریف شده‌است تا event binding را نیز گوشزد کند. کار آن انتقال تعاملات کاربر، با المان رابط کاربری جاری، به خاصیت عمومی کلاس یا همان listFilter است.

در اینجا ممکن است که فراموش کنید [()] صحیح است یا ([]) . به همین جهت به این syntax، نام banana in the box را داده‌اند یا «موز درون جعبه»! موز همان event binding است که داخل جعبه‌ی property binding قرار می‌گیرد!

خوب، برای اعمال انقیاد دو طرفه، به مثال جاری، فایل product-list.component.ts را گشوده و خاصیت رشته‌ای listFilter را به آن اضافه می‌کنیم:
export class ProductListComponent {
    pageTitle: string = 'Product List';
    imageWidth: number = 50;
    imageMargin: number = 2;
    showImage: boolean = false;
    listFilter: string = 'cart';
سپس فایل قالب product-list.component.html را گشوده و انقیاد دو طرفه را به آن اعمال می‌کنیم:
<div class='panel-body'>
    <div class='row'>
        <div class='col-md-2'>Filter by:</div>
        <div class='col-md-4'>
            <input type='text'
                   [(ngModel)]='listFilter' />
        </div>
    </div>
    <div class='row'>
        <div class='col-md-6'>
            <h3>Filtered by: {{listFilter}}</h3>
        </div>
    </div>
در اینجا انقیاد دو طرفه، توسط ngModel، به خاصیت listFilter کلاس، در المان input تعریف شده، صورت گرفته است‌. سپس توسط interpolation مقدار این تغییر را در قسمت Filtered by به صورت یک برچسب نمایش می‌دهیم.


پس از اجرای برنامه، تکست باکس تعریف شده، مقدار اولیه‌ی cart را خواهد داشت و اگر آن‌را تغییر دهیم، بلافاصله این مقدار تغییر یافته را در برچسب Filtered by می‌توان مشاهده کرد. به این رخداد two-way binding می‌گویند.
البته هنوز کار فیلتر لیست محصولات در اینجا انجام نمی‌شود که آن‌را در قسمت بعد تکمیل خواهیم کرد.


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

تا اینجا لیست محصولات نمایش داده شد، اما نیاز است برای مثال فرمت ستون نمایش قیمت آن بهبود یابد. برای این منظور، از ویژگی دیگری به نام pipes استفاده می‌شود و کار آن‌ها تغییر داده‌ها، پیش از نمایش آن‌ها است. AngularJS 2.0 به همراه تعدادی pipe توکار برای فرمت مقادیر است؛ مانند date، number، decimal، percent و غیره. همچنین امکان ساخت custom pipes نیز پیش بینی شده‌است.
در اینجا یک مثال ساده‌ی pipes را مشاهده می‌کنید:
 {{ product.productCode | lowercase }}
پس از قید نام خاصیتی که قرار است نمایش داده شود، حرف pipe یا | قرار گرفته و سپس نوع pipe ذکر می‌شود. به این ترتیب کد محصول، پیش از نمایش، ابتدا به حروف کوچک تبدیل شده و سپس نمایش داده می‌شود.

از pipes در property binding هم می‌توان استفاده کرد:
 [title]='product.productName | uppercase'
در اینجا برای مثال عنوان تصویر با حروف بزرگ نمایش داده خواهد شد.

و یا می‌توان pipes را به صورت زنجیره‌ای نیز تعریف کرد:
 {{ product.price | currency | lowercase }}
در اینجا pipe توکار currency سبب نمایش سه حرف اول واحد پولی، با حروف بزرگ می‌شود. اگر علاقمند بودیم که آن‌را با حروف کوچک نمایش دهیم می‌توان یک pipe دیگر را در انتهای این زنجیره قید کرد.

بعضی از pipes، پارامتر هم قبول می‌کنند:
 {{ product.price | currency:'USD':true:'1.2-2' }}
در اینجا هر پارامتر با یک : مشخص می‌شود. برای مثال pipe واحد پولی، سه پارامتر را دریافت می‌کند: یک کد دلخواه، نمایش یا عدم نمایش علامت پولی، بجای کد دلخواه و مشخصات ارقام نمایش داده شده. برای مثال 2-1.2، یعنی حداقل یک عدد پیش از اعشار، حداقل دو عدد پس از اعشار و حداکثر دو عدد پس از اعشار باید ذکر شوند (یعنی در نهایت دو رقم اعشار مجاز است).
مبحث ایجاد custom pipes را در قسمت بعدی دنبال خواهیم کرد.

در ادامه برای ویرایش مثال جاری، فایل قالب product-list.component.html را گشوده و سطرهای جدول را به نحو ذیل تغییر دهید:
<td>{{ product.productName }}</td>
<td>{{ product.productCode | lowercase }}</td>
<td>{{ product.releaseDate }}</td>
<td>{{ product.price | currency:'USD':true:'1.2-2'}}</td>
<td>{{ product.starRating }}</td>
در اینجا با استفاده از pipes، شماره محصول با حروف کوچک و قیمت آن تا حداکثر دو رقم اعشار، فرمت خواهند شد.
اینبار اگر برنامه را اجرا کنید، یک چنین خروجی را مشاهده خواهید کرد:


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


خلاصه‌ی بحث

data binding سبب سهولت نمایش مقادیر خواص کلاس یک کامپوننت، در قالب آن می‌شود. در AngularJS 2.0، چهار نوع binding وجود دارند:


interpolation، عبارت رشته‌ای محاسبه شده را در بین المان‌های DOM درج می‌کند و یا می‌تواند خاصیت المانی را مقدار دهی نماید.
property binding سبب اتصال مقدار خاصیتی، به یکی از خواص المانی مشخص در DOM می‌شود.
event binding به رخ‌دادها گوش فرا داده و سبب اجرای متدی در کلاس کامپوننت، در صورت بروز رخداد متناظری می‌شود.
حالت two-way binding، کار دریافت اطلاعات از کلاس و همچنین بازگشت مقادیر تغییر یافته‌ی توسط کاربر را به کلاس انجام می‌دهد.
اطلاعات نمایش داده شده‌ی توسط binding عموما فرمت مناسبی را ندارد. برای رفع این مشکل از pipes استفاده می‌شود.
مطالب
آشنایی با Fluent interfaces

تعریف مقدماتی fluent interface در ویکی پدیا به شرح زیر است: (+)

In software engineering, a fluent interface (as first coined by Eric Evans and Martin Fowler) is a way of implementing an object oriented API in a way that aims to provide for more readable code.

به صورت خلاصه هدف آن فراهم آوردن روشی است که بتوان متدها را زنجیر وار فراخوانی کرد و به این ترتیب خوانایی کد نوشته شده را بالا برد. پیاده سازی آن هم شامل دو نکته است:
الف) نوع متد تعریف شده باید مساوی با نام کلاس جاری باشد.
ب) در این حالت خروجی متد‌های ما کلمه کلیدی this خواهند بود.

برای مثال:
using System;

namespace FluentInt
{
public class FluentApiTest
{
private int _val;

public FluentApiTest Number(int val)
{
_val = val;
return this;
}

public FluentApiTest Abs()
{
_val = Math.Abs(_val);
return this;
}

public bool IsEqualTo(int val)
{
return val == _val;
}
}
}
مثالی هم از استفاده‌ی آن به صورت زیر می‌تواند باشد:
if (new FluentApiTest().Number(-10).Abs().IsEqualTo(10))
{
Console.WriteLine("Abs(-10)==10");
}
که در آن توانستیم تمام متدها را زنجیر وار و با خوانایی خوبی شبیه به نوشتن جملات انگلیسی در کنار هم قرار دهیم.
خوب! این مطلبی است که همه جا پیدا می‌کنید و مطلب جدیدی هم نیست. اما موردی را که سخت می‌شود یافت این است که طراحی کلاس فوق ایراد دارد. برای مثال شما می‌توانید ترکیب‌های زیر را هم تشکیل دهید و کار می‌کند؛ یا به عبارتی برنامه کامپایل می‌شود و این خوب نیست:
if(new FluentApiTest().Abs().Number(-10).IsEqualTo(10)) ...
if (new FluentApiTest().Abs().IsEqualTo(10)) ...
می‌شود در کدهای برنامه یک سری throw new exception را هم قرار داد که ... هی! اول باید اون رو فراخوانی کنی بعد این رو!
ولی ... این روش هم صحیح نیست. از ابتدای کار نباید بتوان متد بی‌ربطی را در طول این زنجیره مشاهده کرد. اگر قرار نیست استفاده گردد، نباید هم در intellisense ظاهر شود و پس از آن هم نباید قابل کامپایل باشد.

بنابراین صورت مساله به این ترتیب اصلاح می‌شود:
می‌خواهیم پس از نوشتن FluentApiTest و قرار دادن یک نقطه، در intellisense فقط Number ظاهر شود و نه هیچ متد دیگری. پس از ذکر متد Number فقط متد Abs یا مواردی شبیه به آن مانند Sqrt ظاهر شوند. پس از انتخاب مثلا Abs آنگاه متد IsEqualTo توسط Intellisense قابل دسترسی باشد. در روش اول فوق، به صورت دوستانه همه چیز در دسترس است و هر ترکیب قابل کامپایلی را می‌شود با متدها ساخت که این مورد نظر ما نیست.
اینبار پیاده سازی اولیه به شرح زیر تغییر خواهد کرد:
using System;

namespace FluentInt
{
public class FluentApiTest
{
public MathMethods<FluentApiTest> Number(int val)
{
return new MathMethods<FluentApiTest>(this, val);
}
}

public class MathMethods<TParent>
{
private int _val;
private readonly TParent _parent;

public MathMethods(TParent parent, int val)
{
_val = val;
_parent = parent;
}

public Restrictions<MathMethods<TParent>> Abs()
{
_val = Math.Abs(_val);
return new Restrictions<MathMethods<TParent>>(this, _val);
}
}

public class Restrictions<TParent>
{
private readonly int _val;
private readonly TParent _parent;

public Restrictions(TParent parent, int val)
{
_val = val;
_parent = parent;
}

public bool IsEqualTo(int val)
{
return _val == val;
}
}
}
در اینجا هم به همان کاربرد اولیه می‌رسیم:
if (new FluentApiTest().Number(-10).Abs().IsEqualTo(10))
{
Console.WriteLine("Abs(-10)==10");
}
با این تفاوت که intellisense هربار فقط یک متد مرتبط در طول زنجیره را نمایش می‌دهد و تمام متدها در همان ابتدای کار قابل انتخاب نیستند.
در پیاده سازی کلاس MathMethods از Generics استفاده شده به این جهت که بتوان نوع متد Number را بر همین اساس تعیین کرد تا متدهای کلاس MathMethods در Intellisense (یا به قولی در طول زنجیره مورد نظر) ظاهر شوند. کلاس Restrictions نیز به همین ترتیب معرفی شده است و از آن جهت تعریف نوع متد Abs استفاده کردیم. هر کلاس جدید در طول زنجیره، توسط سازنده خود به وهله‌ای از کلاس قبلی به همراه مقادیر پاس شده دسترسی خواهد داشت. به این ترتیب زنجیره‌ای را تشکیل داده‌ایم که سازمان یافته است و نمی‌توان در آن متدی را بی‌جهت پیش یا پس از دیگری صدا زد و همچنین دیگر نیازی به بررسی نحوه‌ی فراخوانی‌های یک مصرف کننده نیز نخواهد بود زیرا برنامه کامپایل نمی‌شود.
اشتراک‌ها
کتابخانه ngx-page-scroll

Animated scrolling functionality for angular written in pure typescript with no additional dependencies  Demo

  • easy-to-use directive: scroll to an element referenced in the href-attribute (href="#mytarget) just by adding pageScrolldirective
  • service usage: trigger scroll animations from your component or when server responds
  • customizable: adjust duration, offset or whether scrolling stops if the user interrupts (read more)
  • use custom easing functions to calculate the scroll position over time
  • works across routes (scrolls to target element as soon as the routing has finished) and in both directions (horizontal/vertical) 
npm install ngx-page-scroll  
کتابخانه ngx-page-scroll