مطالب
آموزش (jQuery) جی کوئری 1#
با سلام خدمت دوستان عزیز
تصمیم گرفتم در طی چندین پست در حد توانم به آموزش jQuery بپردازم. (مطالب نوشته شده برداشت ازادی از کتاب jQuery in action است)

جی کوئری (jQuery) چیست؟
jQuery یک کتابخانه بسیار مفید برای جاوا اسکریپت است. بسیار ساده و کارآمد است و مشکل جاوا اسکریپت را برای تطابق با مرورگرهای اینترنتی مختلف برطرف نموده است؛ یادگیریjQuery بسیار آسان است. در جی کوئری کد جاوا اسکریپت از فایل HTML جدا شده و بنابراین کنترل کدھا و بھینه‌سازی آنھا بسیار ساده‌تر خواھد شد. توابعی برای کار با AJAX فراھم نموده و در این زمینه نیز کار را بسیار ساده کرده است. در جی کوئری می‌توان از خصوصیت فراخوانی زنجیره‌ای متدھا استفاده نمود و این باعث می‌شود چندین کد فقط در یک سطر قرار گیرد و در نتیجه کد بسیار مختصر گردد. در مقایسه با سایر ابزارهایی که تاکید عمده‌ای بروی تکنیک‌های هوشمند جاوا اسکریپت دارند، هدف جی کوئری تغییر تفکر سازندگان وب سایت‌ها، به ایجاد صفحه‌هایی با کارکرد بالا می‌باشد. به جای صرف زمان برای مقابله با پیچیدگی‌های جاوا اسکریپت پیشرفته، طراحان می‌توانند با استفاده از زمان و دانش خود در زمینه‌ی CSS، HTML، XHTML و جاوا اسکریپت‌های ساده، عناصر صفحه را مستقیما دستکاری کنند و از همین طریق تغییرهای گشترده و سریعی انجام دهند.

نکته: برای استفاده از جی کوئری باید HTML و CSS و جاوا اسکریپت آشنایی داشته باشید.

چگونه از جی کوئری استفاده کنیم؟
برای استفاده از جی کوئری باید ابتدا فایل آن را از سایت آن دانلود کرده و در پروژه خود استفاده نمایید. البته روش‌های دیگری برای استفاده از این فایل وجود دارد که در آینده بیشتر با آن آشنا خواهیم شد. برای استفاده از این فایل در پروژه باید به شکل زیر آن را به صفحه HTML خود معرفی کنیم.
<html>
   <head>
       <script type="text/javascript" src="jquery-1.9.1.min.js"></script>
   </head>
   <body>
   </body>
</html>
سپس بعد از معرفی خط فوق در قسمت head صفحه باید کد‌های خود را در یک تگ script بنویسیم.

کوتاه کردن کد: هر زمان شما خواسته باشید کارکرد یک صفحه وب را پویا‌تر کنید، در اکثر مواقع به ناچار این کار از طریق عناصری بروی صفحه انجام داده اید که با توجه به انتخاب شدن آنها، صفحه کارکردی خاص خواهد داشت. مثلا در جاوا اسکریپت اگر بخواهیم عنصری را که در یک radioGroup انتخاب شده است را برگردانیم باید کد‌های زیر را بنویسیم:
var checkedValue;
var elements = document.getElementByTagName ('input');
for (var n = 0; n < elements.length; n++) {
       if (elements[n].type == 'radio' && elements[n].name == 'myRadioGroup' && elements[n].checked) {
               checkedValue = elements[n].value;
       }
}
اما اگر بخواهیم همین کد را با جی کوئری بنویسیم:
var checkedValue = $ ('[name="myRadioGroup"]:checked').val();
ممکن است مثال بالا کمی گنگ باشد نگران نباشید در آینده با این دستورات بیشتر آشنا خواهیم شد.
قدرت اصلی جی کوئری برگفته از انتخاب‌کننده‌ها (Selector) هاست، انتخاب‌کننده ، یک عبارت است که دسترسی به عنصری خاص بر روی صفحه را موجب می‌شود؛ انتخاب‌کننده این امکان را فراهم می‌سازد تا به سادگی عنصر مورد نظر را مشخص و به آن دسترسی پیدا کنیم که در مثال فوق، عنصر مورد نظر ما گزینه انتخاب شده از myRadioGroup بود.
Unobtrusive JavaScript: اگر پیش از پیدایش CSS در کار ایجاد صفحه‌های اینترنتی بوده‌اید حتما مشکلات و مشقات آن دوران را به خاطر می‌آورید. در آن زمان برای فرمت‌دهی به اجزای مختلف صفحه ، به ناچار علائم فرمت‌دهی را به همراه دستورات خود اجزا، در صفحه‌های HTML استفاده می‌کردیم. اکنون بسیار بعید به نظر می‌رسد کسی ترجیح دهد فرمت‌دهی اجزا را به همراه دستورهای HTML آن انجام دهد. اگر چه هنوز دستوری مانند زیر بسیار عادی به نظر می‌آید:
<button type="button" onclick="document.getElementById('xyz').style.color='red';">
        Click Me
</button>
نکته ای که در مثال فوق حائز اهمیت است، این است که خصوصیات ظاهری دکمه ایجاد شده از قبیل فونت و عنوان دکمه، از طریق تگ <font> و یا پارامترهای قابل استفاده در خود دستور دکمه تعیین نشده است، بلکه CSS وظیفه تعیین آنها را دارد. اما اگرچه در این مثال فرمت‌دهی و دستور خود دکمه از یکدیگر جدا شده‌اند؛ شاهد ترکیب این دکمه با رفتار آن هستیم. در جی کوئری می‌توانیم رفتار را از اجزا به آسانی جدا کنیم.

مجموعه عناصر در جی کوئری:

زمانی که CSS به عنوان یک تکنولوژی به منظور جداسازی طراحی از ساختار به دنیای صفحه‌های اینترنتی معرفی شد، می‌بایست راهی برای اشاره به اجزای صفحات از طرف فایل CSS نیز معرفی می‌شد. این امر از طریق انتخاب‌کننده‌ها (Selector) صورت پذیرفت.

برای مثال انتخاب‌کننده زیر، به تمام عناصر <a> اشاره دارد که در یک عنصر <p> قرار گرفته‌اند:

p a
جی کوئری نیز از چنین انتخاب‌کننده‌هایی استفاده می‌کند، الته نه تنها از انتخاب‌کننده‌هایی که هم اکنون در CSS موجود می‌باشند، بلکه برخی از انتخاب‌کننده‌هایی که هنوز در تمام مرورگرها پشتیبانی نمی‌شوند.
برای انتخاب مجموعه‌ای از عناصر از یکی از دو Syntax زیر استفاده می‌کنیم.
$(Selector)
یا
jQuery(Selector)
ممکن است در ابتدا ()$ کمی نا معمول به نظر آید، اما اکثر کسانی که با جی کوئری کار می‌کنند از اختصار و کوتاهی این ساختار استفاده می‌کنند.
مثال زیر نمونه‌ای دیگر است که در آن مجموعه‌ای از تمام لینک‌هایی که درون تگ <p> قرار دارند را انتخاب می‌کند:
$("p a")
تابع ()$ که در حقیقت نام خلاصه‌ای برای ()jQuery می‌باشد، نوع خروجی مخصوصی دارد که شامل یک آرایه از اشیایی می‌شود که انتخاب‌کننده آن را برگزیده است. این نوع خروجی این مزیت را دارد که شمار زیادی متد از پیش تعریف شده را داراست که به سادگی قابل اعمال می‌باشند.
در اصطلاح برنامه نویسی به چنین توابعی که گروهی از عناصر را جمع می‌کنند، Wrapper می‌گویند زیرا تمام عناصر مطلوب را تحت یک شی بسته‌بندی می‌کند. در جی‌کوئری به آنها Wrapped Set یا jQuery Wrapper می‌گویند و به متدهایی که قابل اعمال بروی اینها به نام jQuery Wrapper Methodes شناخته می‌شوند.
در مثال زیر می‌خواهیم تمام عناصر <div> در صورتی که دارای کلاس notLongForThisWorldباشند را مخفی (با فید شدن) کنیم.

$("div.notLongForThisWorld").fadeOut();
یکی از مزیت‌های اکثر متدهای قابل اجرا بروی مجموعه عناصر انتخاب شده آن است که خروجی خود آنها مجموعه‌ای دیگر است. به این معنا که خروجی این متد، آماده اعمال یک متد دیگر است.
فرض کنید در مثال بالا بخواهیم پس از مخفی کردن هر <div> بخواهیم یک کلاس به نام removedبه آن بیافزاییم. به این منظور می‌توان کدی مانند زیر نوشت:
$("div.notLongForThisWorld").fadeOut().addClass("removed");
این زنجیره متدها می‌توانند به هرتعداد ادامه پیدا کند.

چند نمونه  انتخاب کننده:

نتیجه
  انتخاب کننده
 تمام <p>‌های زوج را انتخاب می‌کند

$('p:even')
 سطر اول هر جدول را انتخاب می‌کند

$("tr:nth-child(1)");
 <div>هایی که مستقیما در <body> تعریف شده باشند را انتخاب می‌کند.

$("body > div");
 لینک هایی که به یک فایل pdf اشاره دارند را انتخاب می‌کند.

$("a[href$=pdf]");
 تمام <div> هایی که مستقیما در <body> معرفی شده اند و دارای لینک می‌باشند را انتخاب می‌کند.

$("body > div:has(a)")
   

ادامه مطالب در پست‌های بعدی تشریح خواهد شد.


جهت مطالعه بیشتر می‌توانید از این منابع ^  و  ^  و  ^  و  ^  و  ^ استفاده کنید.
موفق و موید باشید
نظرات اشتراک‌ها
دریافت نصاب آفلاین Visual Studio 2012 نسخه Ultimate
یک نکته تکمیلی.
فایل نصاب VS 2012 را اگر با برنامه 7zip باز کنید، داخل آن یک فایل به نام 0 وجود دارد. این فایل XML ایی را اگر باز کنید ProductKey داخل آن است (برای نمونه نسخه RTM دارای این کلید هست).
بعد از اینکه کلید را یافتید، برای نصب نسخه‌های آزمایشی باید به نحو زیر از طریق خط فرمان عمل کنید:
vs_ultimate.exe /ProductKey yourKey

مطالب
Blazor 5x - قسمت 24 - تهیه API مخصوص Blazor WASM - بخش 1 - ایجاد تنظیمات ابتدایی
تا اینجا با اصول توسعه‌ی برنامه‌های مبتنی بر Blazor Server آشنا شدیم. در ادامه‌ی این سری، روش توسعه برنامه‌های مبتنی بر Blazor WASM را بررسی خواهیم کرد و پیش از شروع آن، باید بتوان امکانات سمت سرور مورد نیاز این نوع برنامه‌های سمت کلاینت را از طریق یک Web API تامین کرد که شامل دریافت و ارائه‌ی اطلاعات و همچنین اعتبارسنجی و احراز هویت مبتنی بر JWT یکپارچه‌ی با ASP.NET Core Identity است.


ایجاد پروژه‌ی ASP.NET Core Web API

برای تامین اطلاعات برنامه‌ی سمت کلاینت Blazor WASM و همچنین فراهم آوردن زیرساخت اعتبارسنجی کاربران آن، نیاز به یک پروژه‌ی ASP.NET Core Web API داریم که آن‌را با اجرای دستور dotnet new webapi در یک پوشه‌ی خالی، برای مثال به نام BlazorWasm.WebApi ایجاد می‌کنیم.
البته این پروژه، از زیرساختی که در برنامه‌ی Blazor Server بررسی شده‌ی تا این قسمت، ایجاد کردیم نیز استفاده خواهد کرد. همانطور که پیشتر نیز عنوان شد، هدف از قسمت Blazor Server مثال این سری، آشنایی با مدل برنامه نویسی خاص آن بود؛ وگرنه می‌توان کل این پروژه را با Blazor Server و یا کل آن‌را با Web API + Blazor WASM نیز پیاده سازی کرد. در این مثال، قسمت‌های مدیریتی برنامه‌ی مدیریت هتل را توسط Blazor Server (مانند قسمت‌های تعریف اتاق‌ها و امکانات رفاهی هتل) و قسمت مخصوص کاربران آن‌را مانند رزرو کردن اتاق‌ها، توسط Blazor WASM پیاده سازی می‌کنیم. به همین جهت قسمت‌هایی از این دو پروژه، مانند سرویس‌های استفاده شده‌ی در پروژه‌ی Blazor server، در پروژه‌ی Web API مکمل Blazor WASM، قابلیت استفاده‌ی مجدد را دارند.


افزودن سرویس‌های آغازین مورد نیاز، به پروژه‌ی Web API

در فایل آغازین BlazorWasm\BlazorWasm.WebApi\Startup.cs، برای شروع به تکمیل Web API، نیاز به این سرویس‌ها را داریم:
namespace BlazorWasm.WebApi
{
    public class Startup
    {
        //...

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAutoMapper(typeof(MappingProfile).Assembly);

            services.AddScoped<IHotelRoomService, HotelRoomService>();
            services.AddScoped<IAmenityService, AmenityService>();
            services.AddScoped<IHotelRoomImageService, HotelRoomImageService>();

            var connectionString = Configuration.GetConnectionString("DefaultConnection");
            services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString));

            services.AddIdentity<IdentityUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            //...
در اینجا سرویس‌های AutoMapper، تنظیمات ابتدایی DbContext برنامه، به همراه سرویس‌های Identity (بدون UI آن) و افزودن سرویس‌های اتاق‌ها و امکانات رفاهی هتل را نیاز داریم. به همین جهت ارجاعات و وابستگی‌های زیر را به فایل csproj جاری اضافه می‌کنیم تا پروژه‌های DataAccess ،Services و Mappings قابل دسترسی و استفاده شوند:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\BlazorServer\BlazorServer.DataAccess\BlazorServer.DataAccess.csproj" />
    <ProjectReference Include="..\..\BlazorServer\BlazorServer.Services\BlazorServer.Services.csproj" />
    <ProjectReference Include="..\..\BlazorServer\BlazorServer.Models.Mappings\BlazorServer.Models.Mappings.csproj" />
  </ItemGroup>
</Project>
همچنین در این پروژه نیز از همان بانک اطلاعاتی پروژه‌ی Blazor Server که تاکنون تکمیل کردیم، استفاده می‌کنیم. بنابراین محتوای فایل BlazorWasm\BlazorWasm.WebApi\appsettings.json آن نیز مشابه‌است:
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=HotelManagement;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}


تعریف کنترلر HotelRoom

در ادامه کدهای اولین کنترلر Web API را مشاهده می‌کنید که مرتبط است با بازگشت اطلاعات تمام اتاق‌های ثبت شده و یا بازگشت اطلاعات یک اتاق ثبت شده:
using BlazorServer.Models;
using BlazorServer.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BlazorWasm.WebApi.Controllers
{
    [Route("api/[controller]")]
    public class HotelRoomController : ControllerBase
    {
        private readonly IHotelRoomService _hotelRoomService;

        public HotelRoomController(IHotelRoomService hotelRoomService)
        {
            _hotelRoomService = hotelRoomService;
        }

        [HttpGet]
        public IAsyncEnumerable<HotelRoomDTO> GetHotelRooms()
        {
            return _hotelRoomService.GetAllHotelRoomsAsync();
        }

        [HttpGet("{roomId}")]
        public async Task<IActionResult> GetHotelRoom(int? roomId)
        {
            if (roomId == null)
            {
                return BadRequest(new ErrorModel
                {
                    Title = "",
                    ErrorMessage = "Invalid Room Id",
                    StatusCode = StatusCodes.Status400BadRequest
                });
            }

            var roomDetails = await _hotelRoomService.GetHotelRoomAsync(roomId.Value);
            if (roomDetails == null)
            {
                return BadRequest(new ErrorModel
                {
                    Title = "",
                    ErrorMessage = "Invalid Room Id",
                    StatusCode = StatusCodes.Status404NotFound
                });
            }

            return Ok(roomDetails);
        }
    }
}
- این کنترلر، از سرویس IHotelRoomService که در قسمت‌های قبل تکمیل کردیم، استفاده می‌کند.
- ErrorModel آن‌را در همان پروژه‌ی قبلی مدل‌ها، در فایل BlazorServer\BlazorServer.Models\ErrorModel.cs به صورت زیر ایجاد کرده‌ایم:
namespace BlazorServer.Models
{
    public class ErrorModel
    {
        public string Title { get; set; }

        public int StatusCode { get; set; }

        public string ErrorMessage { get; set; }
    }
}
در این حالت اگر برنامه‌ی Web API را اجرا کنیم، به خروجی Swagger زیر می‌رسیم که جزئیات این فناوری را در سری «مستند سازی ASP.NET Core 2x API توسط OpenAPI Swagger» پیشتر بررسی کردیم:


یکی از مزایای آن، امکان آزمایش API تهیه شده، بدون نیاز به تهیه‌ی هیچ نوع کلاینت خاصی است. برای مثال اگر بر روی api​/hotelroom آن کلیک کنیم، گزینه‌ی «try it out» آن ظاهر شده و با کلیک بر روی آن، اینبار دکمه‌ی execute ظاهر می‌شود. در ادامه با کلیک بر روی دکمه‌ی اجرای آن، اکشن متد GetHotelRooms اجرا شده و خروجی زیر ظاهر می‌شود:


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



انجام تنظیمات ابتدایی CORS و خروجی JSON برنامه

قرار است این API را از طریق پروژه‌ی Blazor سمت کلاینت خود استفاده کنیم که آدرس آن، با آدرس API یکی نیست. به همین جهت نیاز است تنظیمات CORS را به صورت زیر اضافه کنیم:
namespace BlazorWasm.WebApi
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
         // ... 

            services.AddCors(o => o.AddPolicy("HotelManagement", builder =>
            {
                builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
            }));

            services.AddControllers()
                    .AddJsonOptions(options =>
                    {
                        options.JsonSerializerOptions.PropertyNamingPolicy = null;
                        // To avoid `JsonSerializationException: Self referencing loop detected error`
                        options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
                    });
         // ... 
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // ... 

            app.UseCors("HotelManagement");
            app.UseRouting();

            app.UseAuthentication();
            // ...
        }
    }
}
در اینجا علاوه بر تنظیمات CORS، تنظیمات JsonSerializer را هم تغییر داده‌ایم تا خطاهای Self referencing loop را در حین ارائه‌ی خروجی‌های Web API، مشاهده نکنیم (همان نکته‌ی «تهیه خروجی JSON از مدل‌های مرتبط، بدون Stack overflow»).


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-24.zip
مطالب
پشتیبانی از IE در برنامه‌های Angular
اگر یک برنامه‌ی Angular را به صورت پیش‌فرض در IE اجرا کنیم، یک چنین تصویری مشاهده خواهد شد:


برای اجرای برنامه توسط نگارش‌های مختلف IE می‌توانید برنامه‌ی IE Tester را نصب کنید.


مشکل چیست؟

مشکل عدم اجرای برنامه‌های Angular در IE، به قدیمی بودن موتور JavaScript آن بر می‌گردد؛ خصوصا در مورد توابع کار با آرایه‌ها. برای مثال در مورد کار با for..of هیچ نوع پشتیبانی از آن در IE وجود ندارد (و نخواهد داشت؛ با توجه به پایان دوره‌ی پشتیبانی آن):



چگونه پشتیبانی از ویژگی‌های جدید JavaScript را به مرورگر IE اضافه کنیم؟

Angular امکان افزودن کمبودهای موتور JavaScript پیش‌فرض IE را توسط کتابخانه‌ی core-js میسر کرده‌است که اصطلاحا به آن polyfills گفته می‌شود. برای این منظور فایل src\polyfills.ts را گشوده و تغییرات زیر را به آن اعمال کنید:
الف) در این فایل، هرجایی  import // وجود دارد، آن‌را تبدیل به import کنید (تمام importهایی که کامنت شده‌اند را از حالت کامنت خارج کنید).
ب) دو بسته‌ی زیر را نیز باید نصب کنید:
npm install --save classlist.js
npm install --save web-animations-js
البته اگر به کامنت‌های این فایل دقت کنید، نیاز به نصب این بسته‌ها نیز در آن عنوان شده‌است.


نتیجه‌ی نهایی پس از افزودن polyfills مخصوص IE

اکنون اگر مجددا برنامه را کامپایل و اجرا کنید، برنامه‌ی Angular بدون مشکل در IE اجرا خواهد شد:

پاسخ به بازخورد‌های پروژه‌ها
تنظیم چندین OverallSummarySettings برای ستون های مختلف
OverallSummarySettings یک برچسب عمومی وکلی است (و به اندازه‌ی کافی جا برای نمایش آن هست). اگر می‌خواهید درکنار مقدار نهایی محاسبه شده، یک عبارت دلخواه را نمایش دهید، DisplayFormatFormula را مقدار دهی کنید که یک Func است با این ساختار:
public Func<object, string> DisplayFormatFormula { set; get; }
در اینجا object ایی که در اختیار شما قرار می‌گیرد، حاصل اعمال AggregateFunction است. رشته‌ای را که انتظار دارد، عبارتی است که شما سفارشی سازی خواهید کرد:
column.AggregateFunction(aggregateFunction =>
{
   aggregateFunction.NumericAggregateFunction(AggregateFunction.Sum);
   aggregateFunction.DisplayFormatFormula(obj => obj == null || string.IsNullOrEmpty(obj.ToString()) ? string.Empty : string.Format("{0:n0}", obj));
});
برای مثال در اینجا جمع ستون مرتبطی محاسبه شده، اما حاصل آن به فرمول نمایشی فوق ارسال شده تا سه رقم جداکننده‌ی هزارها به نتیجه‌ی جمع اضافه شود. در اینجا اگر علاقمند بودید هر رشته یا برچسب دیگری را هم اضافه کنید.
مطالب
Pipeها در Angular 2 – قسمت سوم – Pipeهای Pure و Impure
 در قسمت قبل بیان شد که Angular برای اعمال Pipe بر روی Template expressions باید تمامی رخدادهای برنامه را تحت نظر قرار داده و با مشاهده‌ی هر تغییری بر روی عبارت ورودی Pipe، فراخوانی Pipe را آغاز کند. از جمله این رخدادها می‌توان به رخدادهای mouse move، timer tick، server response و فشرده شدن کلیدهای ماوس و یا کیبورد اشاره کرد. واضح است که بررسی تغییرات عبارت در این همه رخداد می‌تواند مخرب باشد و بر روی کارآئی (Performance) تاثیر منفی خواهد گذاشت. اما Angular برای حل این مشکل و همچنین هنگام مشاهده سریع تغییرات هنگام استفاده از Pipeها، الگوریتم‌های سریع و ساده‌ای در نظر گرفته است که آن‌ها را در این بخش مورد برسی قرار خواهیم داد.


Pipeهای Pure و Impure

Pipeها کلا در دو دسته‌ی Pure و Impure قرار می‌گیرند. هنگام ساخت Pipe سفارشی در صورتیکه نوع Pipe مشخص نشود، به صورت پیش فرض از نوع Pure خواهد بود. برای تعریف Pipeهایی از نوع Impure کافی است در متادیتای Pipe@، پرچم Pure را به مقدار false تنظیم کنید.
@Pipe({ name: 'impurePipe', pure: false })
تفاوت این Pipeها در زمان فراخوانی دوباره آنها است.


Pure Pipe

این نوع Pipeها تنها زمانی فراخوانی مجدد می‌شوند که یک تغییر محض (Pure Change) بر روی عبارت ورودی آنها رخ دهد. هر نوع تغییری بر روی عبارات ورودی از جنس string ، number ، Boolean ، Symbol و عبارات اولیه، یا هرنوع تغییری در ارجاع یک شیء مانند  Date ، Array ، Function و Object نیز تغییر محض محسوب می‌شود. به عنوان مثال هیچکدام از تغییرات زیر یک تغییر محض محسوب نمی‌شوند:
numbers.push(10);
obj.name = ‘javad’;
زیرا با اضافه شدن عنصری به یک آرایه یا تغییر خصوصیتی از یک شیء، باعث تغییری در ارجاع آنها نمی‌شود و همانطور که اشاره شد، در عبارات از نوع آرایه و Object، فقط تغییر در ارجاع آن‌ها یک تغییر محض محسوب می‌شود.
حالا می‌توان به این نتیجه رسید که اضافه شدن مقداری به آرایه یا به‌روزرسانی یک property از object، باعث فراخوانی مجدد Pure Pipe نخواهد نشد. شاید این نوع از Pipeها محدود کننده باشند، اما بسیار سریع هستند (برسی تغییر در ارجاع یک شیء بسیار سریعتر از بررسی کامل یک شیء، صورت می‌گیرد).


Impure Pipe

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

مثال: قصد داریم Pipe سفارشی را پیاده سازی کنیم تا آرایه‌ای از اعداد را دریافت و فقط اعداد زوج را فیلتر کرده و نمایش دهد.
برای این منظور یک فایل جدید را با نام even-numbers.pipe.ts با محتویات زیر ایجاد می‌کنیم: 
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'evenNumbers'
})
export class EvenNumbersPipe implements PipeTransform {
  transform(numbers: Array<number>): Array<number> {
    var x=numbers.filter(r => r % 2 == 0);
    return x;
  }
}
همانطور که مشخص است این Pipe در متد transform، آرایه‌ای از اعداد را دریافت کرده و فقط اعداد زوج را بازگشت می‌دهد. حالا باید Pipe تعریف شده خود را در AppModule در قسمت declares تعریف کنیم.
// . . .
import { EvenNumbersPipe } from './pipes/even-numbers.pipe'
@NgModule({
  declarations: [
    . . .
    EvenNumbersPipe
  ],
 . . .
})
export class AppModule { }

سپس در کامپوننت مورد نظر خود متغیری را به نام numbers از نوع آرایه، با مقدار اولیه‌ی اعداد از یک تا ده، تعریف می‌کنیم:
numbers: Array<number> = [1,2,3,4,5,6,7,8,9,10];
برای نمایش این اعداد در رابط کاربری تگ‌های زیر را به قالب کامپوننت خود اضافه می‌کنیم:
<h1>All numbers</h1>
<span *ngFor="let number of numbers">
  {{number}}
</span>
همچنین با استفاده از تگ‌های زیر یک input برای اضافه کردن مقدار جدید به آرایه درنظر می‌گیریم:
<p>
  <input type="text" #number />
  <input type="button" (click)="numbers.push(number.value)" value="Add number"/>
</p>

تگ‌های زیر را نیز برای اعمال Pipe نمایش اعداد زوج، به قالب کامپوننت اضافه می‌کنیم:
<h1>even numbers</h1>
<span *ngFor="let number of numbers | evenNumbers">
  {{number}}
</span>
بعد از اجرای برنامه، یک عدد جدید زوج را به آرایه اضافه کنید. متوجه خواهید شد با اینکه لیست اعداد در قسمت All numbers به‌روز می‌شوند، ولی Pipe، متوجه تغییری بر روی آرایه نشده‌است و همچنان اعداد قبلی را نمایش می‌دهد. دلیل این امر همانطور که قبلا نیز اشاره شد، بخاطر Pure بودن Pipe و عدم فراخوانی مجدد این نوع Pipe‌ها در زمان اضافه شدن مقداری به آرایه یا تغییری در خصوصیت یک شیء است.

برای حل این مشکل، هنگام اضافه شدن عدد به آرایه، اگر ارجاع آرایه را تغییر دهیم، Pure Pipe متوجه تغییرات خواهد شد و لیست اعداد را به‌روز رسانی می‌کند (تغییر در ارجاع یک شیء، از نوع تغییرات محض است):
<p>
  <input type="text" #number />
  <input type="button" (click)="numbers = numbers.concat(number.value)" value="Add number"/>
</p>
با تغییر نحوه اضافه شدن عنصر به آرایه به شکل بالا خواهیم دید که با افزودن اعداد جدید، لیست اعداد زوج نیز در لحظه اعمال خواهند شد. این راه‌حل همیشه کارآمد نخواهد بود. همیشه تشخیص محل اضافه شدن عنصر به آرایه در برنامه کار ساده‌ای نیست تا در آنجا ارجاع آرایه را نیز تغییر دهیم. راه‌حل، استفاده از Impure Pipe است. کافی است متادیتای Pipe@ را هنگام تعریف به شکل زیر تغییر دهید:
@Pipe({
  name: 'evenNumbers',
  pure: false
})
export class EvenNumbersPipe implements PipeTransform {
   //…
}

کسانیکه با Angular 1.x آشنایی دارند، شاید اکنون متوجه این شده‌اند که چرا در Angular به مشابه Angular 1.x دیگر خبری ازfilter و orderBy نیست. با توجه به اینکه این دو فیلتر فقط با عبارات از نوع object سروکار داشتند، پیاده‌سازی آنها فقط با Impure Pipeها امکان پذیر بود و با توجه به اینکه Impure Pipeها در هر بار چرخه تغییرات کامپوننت اجرا خواهند شد، باعث کندی در صفحات خواهند شد. 
مطالب
MongoDb در سی شارپ (بخش اول)
MongoDb  یک دیتابیس Nosql سندگراست که توسط ++C نوشته شده است و از پشتیبانی خوبی در بسیاری از زبان‌ها برخوردار است. مونگو از ساختاری به نام Bson که ساختاری مشابه Json را دارد استفاده می‌کند؛ با این تفاوت که در Json مبحث دیتاتایپ یا نوع داده وجود ندارد، ولی در Bson دیتاتایپ‌ها تعریف می‌شوند. برای دیدن نوع‌های Bson و نحوه نوشته شدن سند آن می‌توانید مقاله MongoDb#7 را مطالعه بفرمایید.


برای آغاز به کار با این دیتابیس ابتدا باید آن را از سایت اصلی دریافت و بر روی سیستم نصب نمایید. متاسفانه سایت مونگو برای کشور ایران محدودیتی قرار داده است و باید از روش‌های دیگری آن را دریافت نمایید و بر روی سیستم خود نصب نمایید. نحوه نصب این دیتابیس را میتوانید در مقاله MongoDb#3 مشاهده نمایید.

شاید نیاز باشد بجای کار کردن با محیط کنسول این دیتابیس، با یک محیط گرافیکی شبیه آن چیزی که Raven دارد کار کنید وتغییرات را مشاهده نمایید؛ برای همین به این آدرس رفته و محیط دلخواه خود را انتخاب نمایید.

  یک پروژه از نوع کنسول را در ویژوال استادیو ایجاد کنید و سپس درایور رسمی مونگو را از این آدرس یا از طریق nuget نصب نمایید:
Install-Package mongocsharpdriver

ابتدا سه مدل را به شکل زیر ایجاد میکنیم:
  public class Author
    {
        public ObjectId Id { get; set; }
        public string Name { get; set; }
    }

public class Language
    {
        public ObjectId Id { get; set; }
        public string Name { get; set; }
    }

  public class Book
    {
        public ObjectId Id { get; set; }
        public string Title { get; set; }
        public string ISBN { get; set; }
        public int Price { get; set; }
        public List<Author> Authors { get; set; }
        public Language Language { get; set; }
    }

نوع ObjectId، نوعی است که توسط مونگو برای مشخص کردن کلید یکتای سند معرفی میشود.

در خطوط اولیه کد زیر، یک شیء از مدل بالا را ساخته و آن را مقداردهی می‌کنیم:
           var book =new Book()
           {
               Title = "Gone With Wind",
               ISBN = "43442424",
               Price = 50000,
               Language = new Language()
               {
                  Name = "Persian"
               },
               Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Margaret Mitchell"
                   },
                     new Author()
                   {
                       Name = "Ali Mahboobi (Translator)"
                   },
               }
           };

بعد از آن یک شیء کلاینت از نوع mongoClient میسازیم که نوع خروجی آن یک اینترفیس میباشد که توسط کلاسی از جنس آن مقداردهی شده است. بیشتر خروجی‌های مونگو در این کتابخانه از نوع اینترفیس هستند. شیء کلاینت وظیفه دارد تا ارتباط شما را با سرور مونگو برقرار کند:
var client = new MongoClient();
البته در این حالت سرور اتصالی مونگو، سیستم جاری و پورت شماره 27017 فرض میشود. در صورتیکه بخواهید آدرسی غیر از آن را بدهید یا حتی همین آدرس را به طور دستی تعیین کنید، از طریق زیر امکان پذیر است. پارامترهای سازنده این شیء کلاینت میتوانند به صورت رشته‌ای، رشته اتصال را دریافت کنند و یا از طریق شیء MangoClientSettings آن را پاس کنید.
 string connectionString = "mongodb://localhost:27017";
            MongoClientSettings settings = MongoClientSettings.FromUrl(new MongoUrl(connectionString));
            var client = new MongoClient(settings);

در قسمت بعد لازم است که از سرور جاری، دیتابیس خود را دریافت کنیم. در صورتیکه دیتابیس درخواستی وجود نداشته باشد، یک دیتابیس جدید با آن نام ساخته خواهد شد:
var db = client.GetDatabase("publisher");
در نمونه کدهای قدیمی مونگو، قبل از دریافت سرور بایستی شیء Server را از طریق متد GetServer نیز دریافت میکردید که از نسخه دو به بعد، آن را منسوخ اعلام کرده‌اند و همین تنظیمات بالا کفایت میکند.
در مونگو اصطلاحی به نام collection وجود دارد که اسناد در آن قرار گرفته و ارتباط با اسناد از طریق آنها انجام می‌پذیرد. پس در اینجا قبل از هر کاری باید یک collection را ایجاد کرد و در صورتیکه کالکشن درخواستی وجود نداشته باشد، آن را تولید و ارتباط با آن را برخواهد گرداند.
var collection = db.GetCollection<Book>("books");

در اینجا کالکشنی با نام books با تبدیلاتی بر اساس مدل Book ایجاد میشود. در مرحله بعد لازم است که شیء ایجاد شده بر اساس کلاس مدل را با استفاده از متدهای insert شیء کالکشن، در دیتابیس ارسال کنیم.
شی‌ءهای درج یک سند جدید به دیتابیس حالات مختلفی را دارد: افزودن تک سند، افزودن چند سند و دو مورد قبلی به صورت غیر همزمان میباشند:
collection.InsertOneAsync(book);
متد بالا سند جدید را به صورت غیرهمزمان در سیستم درج میکند. نمونه ذخیره شده این سند را که توسط برنامه Mongo Compass نمایش داده شده است، می‌توانید در زیر ببینید:

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


عملیات خواندن

برای خواندن یک یا چند سند از دیتابیس میتوانید از دو شیوه موجود Linq و queryBuilder‌ها استفاده کرد. از آنجائیکه با کار با Linq آشنایی داریم، ابتدای شیوه کوئری بیلدر را مورد بررسی قرار میدهیم و سپس نحوه کار با لینک را بررسی میکنیم.
قبل از هر چیزی برای اینکه در مانور دادن بر روی داده‌ها راحت باشیم و اطلاعات را با فیلترهای متفاوتی واکشی کنیم، 7 عدد کتاب را با مشخصات زیر اضافه میکنیم. دو فیلد سال و تاریخ آخرین موجودی انبار را هم اضافه می‌کنیم.
       var client = new MongoClient();
            var db = client.GetDatabase("publisher");
            db.DropCollection("books");
            var collection = db.GetCollection<Book>("books");
            
            var book =new Book()
           {
               Title = "Gone With Wind",
               ISBN = "43442424",
               Price = 50000,
               Year = 1936,
               LastStock = DateTime.Now.AddDays(-13),
               Language = new Language()
               {
                  Name = "Persian"
               },
               Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Margaret Mitchell"
                   },
                     new Author()
                   {
                       Name = "Ali Mahboobi (Translator)"
                   },
               }
           };

            var book2 = new Book()
            {
                Title = "Jane Eyre",
                ISBN = "87897897",
                Price = 60000,
                Year = 1847,
                LastStock = DateTime.Now.AddDays(-5),
                Language = new Language()
                {
                    Name = "English"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Charlotte Brontë"
                   },
                   
               }
            };


            var book3 = new Book()
            {
                Title = "White Fang",
                ISBN = "43442424",
                Price = 50000,
                Year = 1936,
                LastStock = DateTime.Now.AddDays(-13),
                Language = new Language()
                {
                    Name = "English"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Jack London"
                   },
                     new Author()
                   {
                       Name = "Philippe Mignon"
                   },
               }
            };

            var book4 = new Book()
            {
                Title = "The Lost Symbol",
                ISBN = "43442424",
                Price = 3500000,
                Year = 2009,
                LastStock = DateTime.Now.AddDays(-17),
                Language = new Language()
                {
                    Name = "Persian"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Dan Brown"
                   },
                     new Author()
                   {
                       Name = "Mehrdad"
                   },
               }
            };

            var book7 = new Book()
            {
                Title = "The Lost Symbol",
                ISBN = "43442424",
                Price = 47000000,
                Year = 2009,
                LastStock = DateTime.Now.AddDays(-56),
                Language = new Language()
                {
                    Name = "Persian"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Dan Brown"
                   },
                     new Author()
                   {
                       Name = "Mehrdad"
                   },
               }
            };
            var book5= new Book()
            {
                Title = "The Help",
                ISBN = "45345e3er3",
                Price = 9000000,
                Year = 2009,
                LastStock = DateTime.Now.AddDays(-2),
                Language = new Language()
                {
                    Name = "Enlish"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Kathryn Stockett"
                   },
                    
               }
            };

            var book6 = new Book()
            {
                Title = "City of Glass",
                ISBN = "454534545",
                Price = 500000,
                Year = 2009,
                LastStock = DateTime.Now,
                Language = new Language()
                {
                    Name = "Persian"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Cassandra Clare"
                   },
                   new Author()
                   {
                       Name = "Ali"
                   },

               }
            };
            var books = new List<Book> {book, book2, book3, book4, book5, book6,book7};

            collection.InsertManyAsync(books);

برای واکشی دیتاها کالکشنی از آن نوع را همانند قبل درخواست می‌کنیم. بعد از آن نیاز است که فیلتری برای واکشی اطلاعات تعریف کنیم که این فیلتر در قالب یک کلاس به نام BsonDocument ایجاد می‌شود که ما در اینجا، به دلیل اینکه میخواهیم همه اسناد را واکشی کنیم ، این سند Bson را مقداردهی نمیکنیم و توسط متد Find آن را در واکشی دیتاها شرکت میدهیم و سپس با صدا زدن متد ToList، عملیات واکشی را انجام میدهیم، برای اینکار می‌توانیم از عملیات غیرهمزمان هم استفاده کنیم.
            var client = new MongoClient();
            var db = client.GetDatabase("publisher");
            var collection = db.GetCollection<Book>("books");
            
            var filter=new BsonDocument();
            var docs = collection.Find(filter).ToList();
            foreach (var book in docs)
            {
                Console.WriteLine(book.Title + " By "+ book.Authors[0].Name);
            }

با اجرای کد بالا به نتایج زیر میرسیم:
Gone With Wind By Margaret Mitchell
Jane Eyre By Charlotte Brontë
White Fang By Jack London
The Lost Symbol By Dan Brown
The Help By Kathryn Stockett
City of Glass By Cassandra Clare
The Lost Symbol By Dan Brown

اگر بخواهید فیلتری را بر روی این واکشی قرار دهید و مثلا بخواهید کتاب‌های منتشر شده در سال 2009 را واکشی نمایید، باید این سند Bson را مقداردهی نمایید. ولی برای راحتی اینکار، این  کتابخانه شامل یک بیلدر Builder بوده که میتوان از طریق آن فیلترهای متنوعی را به صورت ساده‌تر طراحی کنید:

در خطوط بالا ابتدا یک بیلدر را برای کلاس مورد نظر ایجاد کرده و از خصوصیت Filter آن استفاده میکنیم و این خصوصیت شامل متدهای فراوانی است که میتوانید برای ایجاد شرط یا فیلتر استفاده کنید. تعدادی از متدهای پر استفاده آن همانند eq (برابری) ، gt (برزگتر از ...) ، gte (بزرگتر مساوی ...) و طبیعتا خانواده lt و ... موجود هستند.
var filter = Builders<Book>.Filter.Eq("Year", 2009);

            var docs = collection.Find(filter).ToList();
            foreach (var book in docs)
            {
                Console.WriteLine(book.Title + " By "+ book.Authors[0].Name);
            }
در کد بالا ما از متد eq برای بررسی برابر بودن استفاده کردیم و درخواست اسنادی را کردیم که دارای فیلد سال انتشار هستند و مقدار آن برابر 2009 میباشد و نتیجه آن به صورت زیر نمایش داده میشود:
The Lost Symbol By Dan Brown
The Help By Kathryn Stockett
City of Glass By Cassandra Clare
The Lost Symbol By Dan Brown
حتی می‌توانید با استفاده از شیء فیلتر، ترکیبات شرطی زیر را نیز اعمال نمایید:
   // var filter=new BsonDocument();
            var filterBuilder = Builders<Book>.Filter;
            var filter= filterBuilder.Eq("Year", 2009) | filterBuilder.Gte("Price",700000);

            var docs = collection.Find(filter).ToList();
            foreach (var book in docs)
            {
                Console.WriteLine(book.Title + " By "+ book.Authors[0].Name);
            }
در بالا ابتدا نوعی از شیء Filter را از کلاس Builder دریافت میکنیم و سپس با استفاده عملیات بیتی آن‌ها را با یکدیگر Or میکنیم. در این پرس و جو باید کتابهایی که در سال 2009 منتشر شده‌اند یا قیمتی کمتر از پنجاه هزار ریال یا برابر را دارند، نمایش داده شوند.

Gone With Wind By Margaret Mitchell
White Fang By Jack London
The Lost Symbol By Dan Brown
The Help By Kathryn Stockett
City of Glass By Cassandra Clare
The Lost Symbol By Dan Brown

برای اینکه بتوانید از linq به جای queryBuilder استفاده کنید، میتوانید از خصوصیت AsQueryable استفاده کنید. خط زیر همان شرط یا فیلتر بالا را توسط Linq اعمال میکند
var docs = collection.AsQueryable().Where(x => x.Year == 2009 || x.Price <= 50000).ToList();

Sort کردن داده‌ها
 
برای مرتب سازی اطلاعات به شیوه کوئری بیلدر، همانند فیلتر که از کلاس Builder استفاده میکردیم، از همین شیء استفاده میکنیم؛ با این تفاوت که بجای استفاده از خصوصیت Filter، از Sort استفاده میکنیم و شیء ایجاد شده را به متد Sort میدهیم:
  var sort = Builders<Book>.Sort.Ascending("Title").Descending("Price");
            var docs = collection.Find(filter).Sort(sort).ToList();
            foreach (var book in docs)
            {
                Console.WriteLine(book.Title + " By "+ book.Authors[0].Name);
            }
در نتیجه خطوط بالا کتاب‌ها ابتدا بر اساس نام به صورت صعودی و سپس بر اساس قیمت به صورت نزولی لیست می‌شوند.
City of Glass By Cassandra Clare
Gone With Wind By Margaret Mitchell
The Help By Kathryn Stockett
The Lost Symbol By Dan Brown
The Lost Symbol By Dan Brown
White Fang By Jack London

توجه داشته باشید که متد sort بعد از فیلتر گذاری، یعنی عمل Find در دسترس میباشد.

در قسمت بعدی به روزرسانی، حذف و ایندکس گذاری را مورد بررسی قرار می‌دهیم.
مطالب
ارتقاء به Angular 6: بررسی تغییرات RxJS
پس از ارتقاء Angular CLI و ساختار پروژه‌ی قبلی خود به نگارش 6، اولین موردی را که مشاهده خواهید کرد، این است: برنامه دیگر کامپایل نمی‌شود! اولین دلیل آن عدم استفاده‌ی از HttpClient معرفی شده‌ی در نگارش 4.3 است و دومین دلیل مهم آن، تغییرات بنیادین RxJS است که خلاصه‌ی کاربردی آن‌را در این مطلب بررسی خواهیم کرد.


RxJS اکنون جزئی از پروژه‌های گوگل است

توسعه دهنده‌ی اصلی RxJS یا همان Ben Lesh اکنون به گوگل پیوسته‌است و جزو تیم Angular است. بنابراین در آینده شاهد یکپارچگی بهتر این دو با هم خواهیم بود. البته RxJS هنوز هم به عنوان یک پروژه‌ی مستقل از Angular مدیریت خواهد شد.


آشنایی با تغییرات RxJS 5.5 جهت مهاجرت به RxJS 6.0 ضروری است

در مطلب «کاهش حجم قابل ملاحظه‌ی برنامه‌های Angular با استفاده از RxJS 5.5» با pipe-able operators آشنا شدیم و این موارد پایه‌های مهاجرت به RxJS 6.0 هستند. بنابراین پیش از مطالعه‌ی ادامه‌ی بحث نیاز است این مطلب را به خوبی مطالعه و بررسی کنید.


تغییر رفتار خطاهای مدیریت نشده در RxJS 6.0

تا پیش از RxJS 6.0 اگر خطای مدیریت نشده‌ای رخ می‌داد، این خطا به صورت synchronous به فراخوان صادر می‌شد. این رفتار در نگارش 6 تغییر کرده و صدور آن اینبار asynchronous شده‌است.
برای مثال یک چنین کدی تا پیش از RxJS 6.0 کار می‌کرد:
try {
    source$.subscribe(nextFn, undefined, completeFn);
} catch (err) {
    handleError(err);
}
از این جهت که دومین پارامتر در اینجا، متدی است که کار مدیریت خطا را انجام می‌دهد و چون ذکر نشده‌است، خطای رخ داده‌ی حاصل، به صورت همزمان به فراخوان صادر شده‌است و try/catch نیز در اینجا کار می‌کند. اما این مثال دیگر در نگارش 6 کار نخواهد کرد و صدور خطای مدیریت نشده، دیگر همزمان نیست و قسمت catch این قطعه کد دیگر هیچگاه فراخوانی نمی‌شود. البته این رفتار طبیعی است که می‌بایستی از نگارش‌های پیشین، با Observable هایی که عموما async هستند، وجود می‌داشت و اکنون اصلاح شده‌است.
برای اصلاح این کد در نگارش 6، همان پارامتر دوم متد را مقدار دهی کنید و try/catch را در صورت وجود حذف نمائید.


تغییرات مهم importها در RxJS 6.0

همانطور که در مطلب «کاهش حجم قابل ملاحظه‌ی برنامه‌های Angular با استفاده از RxJS 5.5» نیز بررسی کردیم، تا نگارش 5 این کتابخانه، importها به صورت زیر بودند:
 import 'rxjs/add/operator/map';
و پس از RxJS 5.5 امکان import آن‌ها با روش مخصوص ES 6 میسر شده‌است:
 import { map } from 'rxjs/operators';
هر چند نگارش 5.5 بهبودهای قابل ملاحظه‌ای و مزایایی مانند حذف ساده‌تر کدهای مرده، عدم تعریف عملگرها به صورت استاتیک و همچنین سازگاری بهتر با ابزارهایی مانند TSLint را به همراه دارد، اما باز هم دست آخر به تعداد زیادی import مانند کدهای زیر می‌رسیدیم:
import { timer } from 'rxjs/observable/timer';  
import { of } from 'rxjs/observable/of';
import { from } from 'rxjs/observable/from';
import { range } from 'rxjs/observable/range';
این مشکل در RxJS 6.0 برطرف شده‌است و در ماژول‌های مختلف برنامه حداکثر به دو سطر خلاصه شده‌ی زیر نیاز خواهیم داشت:
import { interval, of } from 'rxjs';  
import { filter, mergeMap, scan } from 'rxjs/operators';
در نگارش 6، تمام «نوع‌ها» مانند Observable و Subject و «متدهای ایجاد» مانند timer و interval از مسیر rxjs دریافت می‌شوند و تمام «عملگرها» مانند map و filter از مسیر rxjs/operators اضافه خواهند شد و ... همین!
البته RxJS 6.0 در کل به همراه 4 گروه کلی importها است که در زیر مشاهده می‌کنید (در اینجا مواردی که کمتر در برنامه‌های Angular به صورت مستقیم استفاده می‌شوند مانند ajax آن و یا webSocket هم قابل مشاهده هستند):
rxjs
rxjs/operators
rxjs/testing
rxjs/webSocket
rxjs/ajax


مواردی که از RxJS 6.0 حذف شده‌اند

برای کاهش حجم کتابخانه‌ی RxJS و همچنین جلوگیری از بکارگیری متدهایی که نمی‌بایستی خارج از کدهای اصلی خود RxJS استفاده شوند، تعداد زیادی از متدهای قدیمی آن و روش‌های کار پیشین با RxJS حذف شده‌اند. برای مثال شما در RxJS 5.5 می‌توانید برای کار با عملگر of، یا آن‌را از مسیر rxjs/add/observable/of دریافت کنید (همان روش وصله کردن تا پیش از RxJS 5.5) و یا آن‌را از مسیر rxjs/observable/of به روش مخصوص ES 6.0 به برنامه اضافه کنید و یا حتی امکان دریافت آن از مسیر rxjs/observable/fromArray نیز میسر است.
در RxJS 6.0 تمام این‌ها حذف شده‌اند و فقط روش زیر باقی مانده‌است:
 import { of } from 'rxjs';
به این ترتیب نه فقط حجم این کتابخانه کاهش یافته‌است، بلکه با کاهش سطح API آن، یادگیری آن نیز ساده‌تر شده‌است.


معرفی بسته‌ی rxjs-compat

در مطلب «ارتقاء به Angular 6: بررسی تغییرات Angular CLI» روش ارتقاء وابستگی‌های پروژه به نگارش 6 را بررسی کردیم. یکی از مراحل آن اجرای دستور زیر بود:
 ng update rxjs
این مورد صرفا وابستگی rxjs ذکر شده‌ی در فایل package.json را به آخرین نگارش آن به روز رسانی و همچنین نصب می‌کند.

پس از آن اگر پروژه را کامپایل کنید، پر خواهد بود از خطاهای rxjs، مانند:
 ERROR in node_modules/ng2-slim-loading-bar/src/slim-loading-bar.service.d.ts(1,10):
error TS2305: Module '"/node_modules/rxjs/Observable"' has no exported member 'Observable'.
این مشکل با بسته‌های ثالثی وجود دارند که هنوز از نگارش‌های قبلی RxJS استفاده می‌کنند و همانطور که عنوان شد، RxJS 6.0 شامل حذفیات و نقل و انتقالات بسیاری است. به همین جهت هیچکدام از کتابخانه‌های ثالث مبتنی بر نگارش‌های پیشین RxJS دیگر کار نکرده و کامپایل نخواهند شد.
برای رفع این مشکل و ارائه‌ی راه‌حلی کوتاه مدت، بسته‌ای به نام rxjs-compat ارائه شده‌است که سبب هدایت تعاریف قدیمی به تعاریف جدید می‌شود و به این ترتیب کدهای کتابخانه‌ی ثالث، بدون مشکل با نگارش 6 نیز قابل استفاده خواهند بود.
برای نصب آن نیاز است دستور زیر را صادر کنید:
 npm i rxjs-compat --save
اکنون اگر برنامه را مجددا کامپایل کنید، تمام خطاهای مرتبط با کتابخانه‌های ثالث مورد استفاده برطرف شده‌اند.

البته دقت داشته باشید از rxjs-compat به عنوان یک راه حل موقت باید استفاده کرد و نیاز است ابتدا کدهای خود را به روش pipe-able operators بازنویسی کنید و مسیرهای importها را اصلاح کنید و در آخر بسته‌های جدید وابستگی‌های ثالث را که از RxJS6 استفاده می‌کنند، نصب نمائید. در نهایت rxjs-compat را حذف کنید.


خودکار سازی اصلاح importها در برنامه‌های پیشین، جهت مهاجرت به RxJS 6.0

با توجه به این تغییرات و حذف و اضافه شدن‌ها در نگارش 6، تقریبا دیگر هیچکدام از importهای قبلی شما کار نمی‌کنند! و اصلاح آن‌ها نیاز به زمان زیادی خواهد داشت. به همین جهت تیم RxJS ابزاری را طراحی کرده‌اند که با اجرای آن بر روی پروژه، به صورت خودکار تمام importهای قبلی را به نگارش جدید تبدیل می‌کند. برای اینکار ابتدا ابزار rxjs-tslint را نصب کنید:
 npm i -g rxjs-tslint
در ادامه فایل tslint.json پروژه خود را گشوده و مداخل زیر را به آن اضافه و ویرایش نمائید:
{
  "rulesDirectory": [
    "node_modules/rxjs-tslint"
  ],
  "rules": {
    "rxjs-collapse-imports": true,
    "rxjs-pipeable-operators-only": true,
    "rxjs-no-static-observable-methods": true,
    "rxjs-proper-imports": true
  }
}
سپس به ریشه‌ی پروژه‌ی خود وارد شده و دستور زیر را اجرا کنید:
 rxjs-5-to-6-migrate -p src/tsconfig.app.json
کیفیت کار این ابزار تا حدی است که تیم‌های داخلی گوگل از آن برای ارتقاء کدهای پیشین استفاده می‌کنند و بر روی پروژه‌های واقعی آزمایش شده‌است.
البته توصیه شده‌است این ابزار را بیش از یکبار نیاز است اجرا کنید.


خلاصه‌ی روش مهاجرت به RxJS 6x

ابتدا آخرین نگارش rxjs را نصب کنید:
 ng update rxjs
سپس rxjs-compat را جهت رفع کمبودهای کتابخانه‌های ثالث مورد استفاده نصب نمائید:
 npm i rxjs-compat --save
پس از آن ابزار rxjs-tslint را نصب و اجرا کنید:
npm i -g rxjs-tslint
rxjs-5-to-6-migrate -p src/tsconfig.app.json
و در آخر ارتقاء به روش pipe-able را باید مدنظر داشته باشید.


یافتن معادل‌های جدید دستورات قدیمی

در حین تبدیل کدهای قدیمی به جدید نیاز خواهید داشت تا معادل‌ها را بیابید. برای این منظور به مستندات رسمی این مهاجرت مراجعه کنید:
https://github.com/ReactiveX/rxjs/blob/master/MIGRATION.md
برای مثال در اینجا مشاهده خواهید کرد که معادل Observable.throw حذف شده، اکنون throwError است و همینطور برای مابقی.


یک مثال واقعی تغییر یافته

مخزن کد تمام مثال‌های سایت جاری که پیشتر منتشر شده‌اند، به نسخه‌ی 6 ارتقاء داده شد. ریز تغییرات RxJS 6.0 آن‌ها را در اینجا می‌توانید مشاهده کنید.
مطالب
مروری بر پلاگین‌های چندسکویی مرورگرها
NPAPI
این عبارت مخفف Netscape Plugin Application Programming Interface هست و یک پلاگین چندسکویی است که بسیاری از مرورگرها از آن استفاده می‌کنند. معماری این پلاگین ابتدا در سال 1995 برای مرورگر netscape نسخه 2 آن توسعه پیدا کرد. ولی رفته رفته توسط دیگر مرورگرها هم استفاده و پیاده سازی شد و البته تعدادی هم بعدها استفاده از آن را رها کردند و از فناوری‌های دیگری استفاده کردند.
کار این پلاگین تعریف یک سری ContentType مانند audio/mp3 است و موقعی‌که مرورگر در رندر کردن این صفحات به چنین محتوایی برخورد کند، پلاگین مربوط به این محتوا را در حافظه بار می‌کند تا پلاگین را در حافظه رندر کرده و جابجایی داده‌ها با پلاگین صورت بگیرد. سپس آن پلاگین محتوای مربوطه را رندر می‌کند. امروزه پلاگین‌ها در همان قسمت از حافظه صفحه مرورگر بار می‌شوند و مانند مرورگرهای قدیمی مجبور به اجرای برنامه خارجی برای محتواهای ناشناخته نیستند.
این API نیاز دارد هر پلاگین تقریبا 15 تابع را پیاده سازی کند. این توابع برای پیاده سازی، ایجاد، نابودسازی آن و موقعیت مکانی آن به کار می‌روند. NPAPI همچنین امکاناتی از قبیل اسکریت نویسی، چاپ کردن، تمام صفحه شدن، پلاگین بدون پنجره، استریم کردن محتوا را نیز دارد.
مبداء و منشا ابتدایی این فناوری از شرکت Adobe و محصول Adobe Reader آن و خواندن PDF‌های تحت وب آغاز گشت. در آن زمان در وب، تنها فرمت HTML به همراه تصاویر به عنوان محتوا حساب میشد و مابقی نوع‌ها به دانلود ختم می‌شدند و شرکت ادوب قصد داشت کاربران تجربه‌ی جدیدی از خواندن فایل‌های PDF را بر روی مرورگر وب داشته باشند و برای دیدن حتما نیازی به دانلود نداشته باشند.
شرکت ادوب برای جلب نظر توسعه دهنگان netscape از یک نسخه‌ی دمو استفاده کرد و موقعی که روی لینک یک PDF کلیک میشد به جای دانلود آن، محتوا داخل صفحه مرورگر رندر میشد که مخلوطی از کدهای html و PDF بود ولی آن‌ها خواستار یک سیستم یکپارچه بودند تا دیگر مرورگر netscape درگیر آن نباشد و بدون وابستگی به مرورگر و به طور مستقل این کار انجام شود که کم کم باعث ایجاد این API شد و از آن موقع در طول این سال‌ها مرورگرهای زیادی از این فناوری استفاده کردند تا استاندارد عمومی در توسعه مرورگرها باشد.

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

پشتیبانی از اسکریپت نویسی
این ویژگی اجازه میدهد که شما با استفاده از کدهای جاوااسکریپت، در صفحات وب با پلاگین تعامل کنید. ابتدا netscape این امکان را اضافه کرد و بعدها موزیلا این ویژگی را با استفاده از فناوری هایی چون Live Connect ،  XP Connect و npRuntime ارتقاء بخشید.

Live Connect
از netscape 4 به بعد NPAPI برای اسکریپت نویسی توسعه یافت. این ویژگی به مرورگرها اجازه میدهد که برنامه‌های نوشته شده با جاوا و جاوااسکریپت، با صفحات وب به تعامل بپردازند. به عنوان مثال اپلت نوشته شده با جاوا می‌تواند با کدهای داخل صفحه ارتباط برقرار کند یا بیشتر از خود اسکریت‌های صفحه با محیط جاوااسکریت داخلی ارتباط برقرار کند.  با این ویژگی یک پلاگین میتواند یک نمونه کلاس جاوا را پیاده سازی و بازگرداند. این کلاس میتواند از طریق جاوااسکریت یا اپلت‌های جاوا صدا زده شود.

XP Connect
ابتدا قبل از هر چیز بگذارید با XPCOM اشنا شویم. XPCOM یا Cross Platform Component Object Model از طرف شرکت موزیلا ارائه شده است و اصلی‌ترین ویژگی آن اتصال چندین زبان به یکدیگر از طریق IDL یا یک زبان واسط است. بدین ترتیب برنامه نویس‌ها میتوانستند کدهای اختصاصی خود را با دیگر کامپوننت‌ها مرتبط کنند.
حال کار xpconnect برقراری ارتباط بین جاوااسکریپت با XPCOM یا کدهای نوشته شده توسعه گران است. XP Connect به اشیاء جاوااسکریپتی اجازه می‌دهد به طور مستقیم به اشیاء XPCom دسترسی داشته و آن‌ها را دستکاری کنند.

NPRuntime
در پایان سال 2004 بسیاری از شرکت‌های اصلی ارائه کننده مرورگرهای وب که از NPAPI استفاده می‌کردند، به این توافق رسیدند که از فناوری NPRuntime به عنوان یک اکستنشن (افزونه) برای تامین اسکریپت نویسی NPAPI استفاده کنند. اسکریپت نویسی این فناوری مستقل از دیگر فناوری‌هایی چون جاوا و XPCOM است و توسط گوگل (در آن زمان)، فایرفاکس، سافاری و اپرا (در آن زمان)  پشتیبانی می‌شود.

PPAPI
این فناوری در 12 آگوست 2009 معرفی شد. Pepper Plugin API مجموعه ای از تغییرات و اصلاحات روی NPAPI است که باعث انعطاف پذیری و امنیت بیشتر می‌شود. این افزونه جهت پیاده سازی آسان اجرای پلاگین در یک پروسه‌ی جداگانه است. هدف این پروژه تهیه یک فریمورک برای ایجاد پلاگین‌های مستقل یا Cross-Platform میباشد.
تعدادی از مواردی که در طراحی این افزونه در نظر گرفته شده است:
  • اجرای جداگانه از پروسه‌ی مرورگر
  • تعریف رویدادهای استاندارد و رسم تصاویر دو بعدی
  • تهیه گرافیک سه بعدی
  • ریجستری پلاگین
  • استاندارد سازی برای استفاده از سیستم رندر مرورگر Compositing Process 
  • تعریف مفهوم یا معنای واحد از NPAPI بین تمامی مروگرها
مرورگرهایی که در حال حاضر به این افزونه روی آورده اند طبعا مرورگر کروم و اپرا از نسخه 24 به بعد است.
شرکت موزیلا در سال 2011 اعلام کرد که فعلا علاقه ای به شرکت در این پروژه ندارد.
شرکت ادوب برای افزونه flash player اعلام کرد که دیگر تنها بر روی PPAPI فعالیت می‌کنند و از آخرین نسخه فلش پلیر 11.2 که برای NPAPI ایجاد شده است تنها به مدت پنج سال بسته‌های آپدیت آن را در دسترس قرار میدهد.

ActiveX
یکی دیگر از این پلاگین‌ها ActiveX است که توسط IE مورد استفاده قرار می‌گیرد ولی در مرورگر Edge  حذف شده است. به این دلیل، پشتیبانی از جاوا در این مرورگر قطع شده است و فلش پلیر را به صورت ذاخلی Built-in پشتیبانی می‌کند. امروزه بسیاری از تولیدکنندگان اعتقاد دارند که با Html5 بسیاری از مسائل حل می‌شود.
مطالب
ساخت بسته‌های نیوگت مخصوص NET Core.
فایل‌های nuspec مخصوص سایر نگارش‌های دات نت، در NET Core. ندید گرفته شده و پردازش نمی‌شوند. در اینجا نیز تمام تنظیمات تولید بسته‌های نیوگت، در فایل project.json درج می‌شوند که در ادامه آن‌ها را بررسی خواهیم کرد.


فعالسازی تولید خودکار بسته‌های نیوگت در پروژه‌های NET Core.

پس از تهیه‌ی یک کتابخانه‌ی مبتنی بر NET Core.، تنها کاری که در جهت تولید خودکار بسته‌های نیوگت باید انجام شود، افزودن مدخل postcompile ذیل به فایل project.json است:
    "scripts": {
        "postcompile": [
            "dotnet pack --no-build --configuration %compile:Configuration%"
        ]
    }
پس از آن هربار که پروژه کامپایل شود، به صورت خودکار فایل nupkg نهایی در پوشه‌ی bin\Release تشکیل می‌شود.
در این‌حالت اگر فایل nupkg تولیدی را توسط برنامه‌های zip باز کنید، مشاهده خواهید کرد که فایل nuspec خودکاری نیز در آن درج شده‌است؛ اما ... مشخصات ثبت شده‌ی در آن ناقص هستند و شامل مواردی مانند نام پروژه، نام نویسنده، مجوز استفاده‌ی از پروژه، آدرس پروژه و امثال آن‌ها نیستند. در نگارش‌های دیگر دات نت، این مشخصات از فایل nuspec تهیه شده‌ی توسط ما جمع آوری و درج می‌شود. اما در اینجا خیر.


تکمیل فایل project.json برای درج مشخصات پروژه و تکمیل اطلاعات فایل nuspec

هرچند به ظاهر دیگر فایل nuspec دستی تهیه شده در اینجا پردازش نمی‌شود، اما تمام اطلاعات آن‌را در فایل project.json نیز می‌توان درج کرد:
{
    "version": "1.1.1.0",
    "authors": [ "Vahid Nasiri" ],
    "packOptions": {
        "owners": [ "Vahid Nasiri" ],
        "tags": [ "PdfReport", "Excel", "Export", "iTextSharp", "PDF", "Report", "Reporting", "Persian", ".NET Core" ],
        "licenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html",
        "projectUrl": "https://github.com/VahidN/iTextSharp.LGPLv2.Core"
    },
    "description": " iTextSharp.LGPLv2.Core  is an unofficial port of the last LGPL version of the iTextSharp (V4.1.6) to .NET Core.",

    "scripts": {
        "postcompile": [
            "dotnet pack --no-build --configuration %compile:Configuration%"
        ]
    }
}
در اینجا یک نمونه از مشخصات فایل project.json ایی را مشاهده می‌کنید که در آن مواردی مانند نویسندگان، برچسب‌هایی که در سایت نیوگت لیست خواهند شد، آدرس مجوز پروژه، آدرس مخزن کد پروژه و توضیحات آن، تکمیل شده‌اند. قسمت postcompile، دقیقا همین اطلاعات را جهت تولید فایل خودکار nuspec نهایی، استفاده می‌کند.


تکمیل تنظیمات Build پروژه

بهتر است کتابخانه‌های خود را در حالت release و همچنین بهینه سازی شده، توزیع کنید. به همین منظور نیاز است مدخل ذیل را نیز به فایل project.json اضافه کرد:
    "configurations": {
        "Release": {
            "buildOptions": {
                "optimize": true,
                "platform": "anycpu"
            }
        }
    },


افزودن مستندات XML ایی کتابخانه

به احتمال زیاد XML-Docهای هر متد (کامنت‌های مخصوص دات نتی هر متد یا خاصیت عمومی) را نیز به کدهای خود افزوده‌اید. برای اینکه فایل XML نهایی آن به صورت خودکار تولید شده و همچنین در بسته‌ی نیوگت نهایی درج شود، نیاز است مدخل xmlDoc را به buildOptions اضافه کنید:
    "buildOptions": {
        "xmlDoc": true
    },
در این حالت هر عنصری با سطح دسترسی public، باید دارای کامنت باشد. اگر می‌خواهید مجبور به انجام اینکار نشوید و کامپایلر اخطار صادر نکند، می‌توانید از اخطار شماره‌ی 1591 صرفنظر کنید:
    "buildOptions": {
        "xmlDoc": true,
        "nowarn": [ "1591" ] // 1591: missing xml comment for publicly visible type or member
    },


برای مطالعه‌ی بیشتر
project.json reference