مطالب
ارسال ویدیو بصورت Async توسط Web Api
فریم ورک ASP.NET Web API صرفا برای ساخت سرویس‌های ساده‌ای که می‌شناسیم، نیست و در واقع مدل جدیدی برای برنامه نویسی HTTP است. کارهای بسیار زیادی را می‌توان توسط این فریم ورک انجام داد که در این مقاله به یکی از آنها می‌پردازم. فرض کنید می‌خواهیم یک فایل ویدیو را بصورت Asynchronous به کلاینت ارسال کنیم.

ابتدا پروژه جدیدی از نوع ASP.NET Web Application بسازید و قالب آن را MVC + Web API انتخاب کنید.


ابتدا به فایل WebApiConfig.cs در پوشه App_Start مراجعه کنید و مسیر پیش فرض را حذف کنید. برای مسیریابی سرویس‌ها از قابلیت جدید Attribute Routing استفاده خواهیم کرد. فایل مذکور باید مانند لیست زیر باشد.
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services

        // Web API routes
        config.MapHttpAttributeRoutes();
    }
}
حال در مسیر ریشه پروژه، پوشه جدیدی با نام Videos ایجاد کنید و یک فایل ویدیو نمونه بنام sample.mp4 در آن کپی کنید. دقت کنید که فرمت فایل ویدیو در مثال جاری mp4 در نظر گرفته شده اما به سادگی می‌توانید آن را تغییر دهید.
سپس در پوشه Models کلاس جدیدی بنام VideoStream ایجاد کنید. این کلاس مسئول نوشتن داده فایل‌های ویدیویی در OutputStream خواهد بود. کد کامل این کلاس را در لیست زیر مشاهده می‌کنید.
public class VideoStream
{
    private readonly string _filename;
    private long _contentLength;

    public long FileLength
    {
        get { return _contentLength; }
    }

    public VideoStream(string videoPath)
    {
        _filename = videoPath;
        using (var video = File.Open(_filename, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            _contentLength = video.Length;
        }
    }

    public async void WriteToStream(Stream outputStream,
        HttpContent content, TransportContext context)
    {
        try
        {
            var buffer = new byte[65536];

            using (var video = File.Open(_filename, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                var length = (int)video.Length;
                var bytesRead = 1;

                while (length > 0 && bytesRead > 0)
                {
                    bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length));
                    await outputStream.WriteAsync(buffer, 0, bytesRead);
                    length -= bytesRead;
                }
            }
        }
        catch (HttpException)
        {
            return;
        }
        finally
        {
            outputStream.Close();
        }
    }
}

شرح کلاس VideoStream
این کلاس ابتدا دو فیلد خصوصی تعریف می‌کند. یکی filename_ که فقط-خواندنی است و نام فایل ویدیو درخواستی را نگهداری می‌کند. و دیگری contentLength_ که سایز فایل ویدیو درخواستی را نگهداری می‌کند.

یک خاصیت عمومی بنام FileLength نیز تعریف شده که مقدار خاصیت contentLength_ را بر می‌گرداند.

متد سازنده این کلاس پارامتری از نوع رشته بنام videoPath را می‌پذیرد که مسیر کامل فایل ویدیوی مورد نظر است. در این متد، متغیر‌های filename_ و contentLength_ مقدار دهی می‌شوند. نکته‌ی قابل توجه در این متد استفاده از پارامتر FileShare.Read است که باعث می‌شود فایل مورد نظر هنگام باز شدن قفل نشود و برای پروسه‌های دیگر قابل دسترسی باشد.

در آخر متد WriteToStream را داریم که مسئول نوشتن داده فایل‌ها به OutputStream است. اول از همه دقت کنید که این متد از کلمه کلیدی async استفاده می‌کند بنابراین بصورت asynchronous اجرا خواهد شد. در بدنه این متد متغیری بنام buffer داریم که یک آرایه بایت با سایز 64KB را تعریف می‌کند. به بیان دیگر اطلاعات فایل‌ها را در پکیج‌های 64 کیلوبایتی برای کلاینت ارسال خواهیم کرد. در ادامه فایل مورد نظر را باز می‌کنیم (مجددا با استفاده از FileShare.Read) و شروع به خواندن اطلاعات آن می‌کنیم. هر 64 کیلوبایت خوانده شده بصورت async در جریان خروجی نوشته می‌شود و تا هنگامی که به آخر فایل نرسیده ایم این روند ادامه پیدا می‌کند.
while (length > 0 && bytesRead > 0)
{
    bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length));
    await outputStream.WriteAsync(buffer, 0, bytesRead);
    length -= bytesRead;
}
اگر دقت کنید تمام کد بدنه این متد در یک بلاک try/catch قرار گرفته است. در صورتی که با خطایی از نوع HttpException مواجه شویم (مثلا هنگام قطع شدن کاربر) عملیات متوقف می‌شود و در آخر نیز جریان خروجی (outputStream) بسته خواهد شد. نکته دیگری که باید بدان اشاره کرد این است که کاربر حتی پس از قطع شدن از سرور می‌تواند ویدیو را تا جایی که دریافت کرده مشاهده کند. مثلا ممکن است 10 پکیج از اطلاعات را دریافت کرده باشد و هنگام مشاهده پکیج دوم از سرور قطع شود. در این صورت امکان مشاهده ویدیو تا انتهای پکیج دهم وجود خواهد داشت.

حال که کلاس VideoStream را در اختیار داریم می‌توانیم پروژه را تکمیل کنیم. در پوشه کنترلر‌ها کلاسی بنام VideoControllerبسازید. کد کامل این کلاس را در لیست زیر مشاهده می‌کنید.
public class VideoController : ApiController
{
    [Route("api/video/{ext}/{fileName}")]
    public HttpResponseMessage Get(string ext, string fileName)
    {
        string videoPath = HostingEnvironment.MapPath(string.Format("~/Videos/{0}.{1}", fileName, ext));
        if (File.Exists(videoPath))
        {
            FileInfo fi = new FileInfo(videoPath);
            var video = new VideoStream(videoPath);

            var response = Request.CreateResponse();

            response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)video.WriteToStream,
                new MediaTypeHeaderValue("video/" + ext));

            response.Content.Headers.Add("Content-Disposition", "attachment;filename=" + fi.Name.Replace(" ", ""));
            response.Content.Headers.Add("Content-Length", video.FileLength.ToString());

            return response;
        }
        else
        {
            return Request.CreateResponse(HttpStatusCode.NotFound);
        }
    }
}

شرح کلاس VideoController
همانطور که می‌بینید مسیر دستیابی به این کنترلر با استفاده از قابلیت Attribute Routing تعریف شده است.

[Route("api/video/{ext}/{fileName}")]
نمونه ای از یک درخواست که به این مسیر نگاشت می‌شود:
api/video/mp4/sample
بنابراین این مسیر فرمت و نام فایل مورد نظر را بدین شکل می‌پذیرد. در نمونه جاری ما فایل sample.mp4 را درخواست کرده ایم.
متد Get این کنترلر دو پارامتر با نام‌های ext و fileName را می‌پذیرد که همان فرمت و نام فایل هستند. سپس با استفاده از کلاس HostingEnvironment سعی می‌کنیم مسیر کامل فایل درخواست شده را بدست آوریم.
string videoPath = HostingEnvironment.MapPath(string.Format("~/Videos/{0}.{1}", fileName, ext));
استفاده از این کلاس با Server.MapPath تفاوتی نمی‌کند. در واقع خود Server.MapPath نهایتا همین کلاس HostingEnvironment را فراخوانی می‌کند. اما در کنترلر‌های Web Api به کلاس Server دسترسی نداریم. همانطور که مشاهده می‌کنید فایل مورد نظر در پوشه Videos جستجو می‌شود، که در ریشه سایت هم قرار دارد. در ادامه اگر فایل درخواست شده وجود داشت وهله جدیدی از کلاس VideoStream می‌سازیم و مسیر کامل فایل را به آن پاس می‌دهیم.
var video = new VideoStream(videoPath);
سپس آبجکت پاسخ را وهله سازی می‌کنیم و با استفاده از کلاس PushStreamContent اطلاعات را به کلاینت می‌فرستیم.
var response = Request.CreateResponse();

response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)video.WriteToStream, new MediaTypeHeaderValue("video/" + ext));

کلاس PushStreamContent در فضای نام System.Net.Http وجود دارد. همانطور که می‌بینید امضای Action پاس داده شده، با امضای متد WriteToStream در کلاس VideoStream مطابقت دارد.

در آخر دو Header به پاسخ ارسالی اضافه می‌کنیم تا نوع داده ارسالی و سایز آن را مشخص کنیم.
response.Content.Headers.Add("Content-Disposition", "attachment;filename=" + fileName);
response.Content.Headers.Add("Content-Length", video.FileLength.ToString());
افزودن این دو مقدار مهم است. در صورتی که این Header‌‌ها را تعریف نکنید سایز فایل دریافتی و مدت زمان آن نامعلوم خواهد بود که تجربه کاربری خوبی بدست نمی‌دهد. نهایتا هم آبجکت پاسخ را به کلاینت ارسال می‌کنیم. در صورتی هم که فایل مورد نظر در پوشه Videos پیدا نشود پاسخ NotFound را بر می‌گردانیم.
if(File.Exists(videoPath))
{
    // removed for bravity
}
else
{
    return Request.CreateResponse(HttpStatusCode.NotFound);
}
خوب، برای تست این مکانیزم نیاز به یک کنترلر MVC و یک View داریم. در پوشه کنترلر‌ها کلاسی بنام HomeController ایجاد کنید که با لیست زیر مطابقت داشته باشد.
public class HomeController : Controller
{
    // GET: Home
    public ActionResult Index()
    {
        return View();
    }
}
نمای این متد را بسازید (با کلیک راست روی متد Index و انتخاب گزینه Add View) و کد آن را مطابق لیست زیر تکمیل کنید.
<div>
    <div>
        <video width="480" height="270" controls="controls" preload="auto">
            <source src="/api/video/mp4/sample" type="video/mp4" />
            Your browser does not support the video tag.
        </video>
    </div>
</div>
همانطور که مشاهده می‌کنید یک المنت ویدیو تعریف کرده ایم که خواص طول، عرض و غیره آن نیز مقدار دهی شده اند. زیر تگ source متنی درج شده که در صورت لزوم به کاربر نشان داده می‌شود. گرچه اکثر مرورگرهای مدرن از المنت ویدیو پشتیبانی می‌کنند. تگ سورس فایلی با مشخصات sample.mp4 را درخواست می‌کند و نوع آن را نیز video/mp4 مشخص کرده ایم.

اگر پروژه را اجرا کنید می‌بینید که ویدیو مورد نظر آماده پخش است. برای اینکه ببینید چطور داده‌های ویدیو در قالب پکیج‌های 64 کیلو بایتی دریافت می‌شوند از ابزار مرورگرتان استفاده کنید. مثلا در گوگل کروم F12 را بزنید و به قسمت Network بروید. صفحه را یکبار مجددا بارگذاری کنید تا ارتباطات شبکه مانیتور شود. اگر به المنت sample دقت کنید می‌بینید که با شروع پخش ویدیو پکیج‌های اطلاعات یکی پس از دیگری دریافت می‌شوند و اطلاعات ریز آن را می‌توانید مشاهده کنید.

پروژه نمونه به این مقاله ضمیمه شده است. قابلیت Package Restore فعال شده و برای صرفه جویی در حجم فایل، تمام پکیج‌ها و محتویات پوشه bin حذف شده اند. برای تست بیشتر می‌توانید فایل sample.mp4 را با فایلی حجیم‌تر جایگزین کنید تا نحوه دریافت اطلاعات را با روشی که در بالا بدان اشاره شد مشاهده کنید.

AsyncVideoStreaming.rar  
نظرات مطالب
ارتقاء به ASP.NET Core 2.0 - معرفی بسته‌ی Microsoft.AspNetCore.All
امکان اجرای درون پروسه‌ای در نگارش 2.2 میسر هست و دقیقا جزئی از بسته‌ی هاستینگ آن است (لینک مطرح شده‌ی در نظر فوق قدیمی است). فقط در این حالت ممکن است برنامه مسیرهای فایل‌های مشخص شده‌ی در آن را پیدا نکند. باید با دستور dotnet ./proj.dll برنامه را اجرا کنید (و یا یکی از روش‌های مطرح شده‌ی در مطلب «بررسی خطاهای ممکن در حین راه اندازی اولیه برنامه‌های ASP.NET Core در IIS») و دقیقا مشاهده کنید که مشکل از کجاست. چند مثال از این نوع:
هر دو مورد به این اشاره می‌کنند که در نگارش فعلی این ماژول درون پروسه‌ای، متد Directory.GetCurrentDirectory بجای اینکه مسیر برنامه را باز گرداند، مسیر C:\windows\system32\inetsrv یا همان پروسه‌ی IIS را بر می‌گرداند و باید به این موضوع دقت داشت.  
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت دوازدهم - توزیع برنامه
روش فعال سازی متد require به این صورت است:
الف) نیاز است typings مربوط به nodejs اضافه شود؛ جهت شناسایی متد require و همچنین شیء process در فایل main.ts جدید. بنابراین باید فایل typings.json جهت افزودن سطر جدید node، ویرایش شود:
{
    "ambientDependencies": {
        "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654",
        "jasmine": "registry:dt/jasmine#2.2.0+20160412134438",
        "node": "registry:dt/node#4.0.0+20160509154515"
    }
}
با ذخیره سازی فایل package.json مداخل فوق اضافه خواهند شد.

ب) اینبار فایل main.ts چنین شکلی را پیدا می‌کند:
/// <reference path="../typings/browser/ambient/es6-shim/index.d.ts" />
/// <reference path="../typings/browser/ambient/node/index.d.ts" /> 
 
import { bootstrap } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
 
// Our main component
import { AppComponent } from "./app.component";
 
if (process.env.ENV === "production") {
    enableProdMode();
}
 
bootstrap(AppComponent, []);
در اینجا ذکر ambient/node/index.d.ts به همراه import و اجرای متد enableProdMode، جدید هستند.

ج) مداخل تعاریف قالب‌ها و شیوه نامه‌ها، به صورت زیر اصلاح می‌شوند و مسیر آن‌ها باید به نحو ذیل مقدار دهی شود (از ابتدای پوشه‌ی جاری):
@Component({
    selector: 'pm-products',
    //templateUrl: 'app/products/product-list.component.html',
    template: require('./product-list.component.html'),
    //styleUrls: ['app/products/product-list.component.css'],
    styles: [require('./product-list.component.css')],
    pipes: [ProductFilterPipe],
    directives: [StarComponent, ROUTER_DIRECTIVES]
})
مطالب
چطور مسیریابی‌های ASP.NET MVC را دیباگ کنیم؟
سؤال: من برای تهیه sitemap برنامه، یک route سفارشی نوشته‌ام تا یک فایل xml ایی را که در وب سرور، وجود خارجی ندارد، در آدرس‌های سایت قابل دسترسی کند. برای مثال:
            routes.MapRoute(
                "SiteMap_route", // Route name
                "sitemap.xml", // URL with parameters
                new { controller = "Sitemap", action = "index", name = UrlParameter.Optional, area = "" } // Parameter defaults
            );
با استفاده از این مسیریابی خاص، قرار است هر زمانیکه آدرس http://site/sitemap.xml در مرورگر وارد شد، برنامه در پشت صحنه، به صورت خودکار به کنترلر sitemap و اکشن متد index آن مراجعه کرده و یک محتوای پویای XML ایی را تولید کند و بازگشت دهد. اما ... کار نمی‌کند! یعنی آدرس یاد شده اصلا پاسخ نمی‌دهد. چرا؟ نحوه‌ی ثبت مسیریابی سفارشی تعریف شده نیز به صورت زیر است:
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

            routes.MapRoute(
                "SiteMap_route", // Route name
                "sitemap.xml", // URL with parameters
                new { controller = "Sitemap", action = "index", name = UrlParameter.Optional, area = "" } // Parameter defaults
            );
        }
پاسخ: اگر با تقدم و تاخر و معنای مسیریابی‌های تعریف شده آشنایی داشته باشید، شاید بلافاصله بتوانید مشکل را حدس بزنید. اما اگر تعداد مسیریابی‌های سفارشی تعریف شده زیاد باشد، اینکار ساده نیست و حتما نیاز به ابزار دیباگ دارد تا بتوان تشخیص داد که در صفحه جاری کدامیک از مسیریابی‌های تعریف شده کار را تمام کرده‌اند و نوبت به دیگری نرسیده است.

برای این منظور می‌توان از افزونه‌ای به نام RouteDebug نوشته یکی از اعضای سابق تیم ASP.NET MVC استفاده کرد:
کار کردن با آن نیز بسیار ساده است.
الف) ارجاعی را به اسمبلی RouteDebug.dll (حاصل از کامپایل پروژه فوق) به پروژه جاری ASP.NET MVC خود اضافه کنید.
ب) سپس به فایل Global.asax.cs خود مراجعه و در سطر آخر متد Application_Start آن، فراخوانی ذیل را اضافه نمائید:
 RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
اکنون هر صفحه و آدرسی را که باز کنید، بجای محتوای اصلی صفحه، مسیریابی‌های فعال و برنده آن‌را مشاهده خواهید کرد. برای مثال در صفحه اول برنامه داریم:


نکته مهمی که در این تصویر باید به آن دقت داشت، اولین True سبز رنگی است که نمایش می‌دهد. یعنی اولین مسیریابی که کار هدایت و نمایش صفحه جاری را برعهده دارد. در اجرای عادی ASP.NET MVC، همینجا کار پردازش سیستم مسیریابی صفحه جاری خاتمه خواهد یافت و نوبت به سایرین نخواهد رسید.
در مورد صفحه sitemap.xml چطور؟ اگر این آدرس را در مرورگر، بدون فعال سازی افزونه RouteDebug وارد کنیم، پیام 404 را دریافت می‌کنیم. اگر افزونه را فعال کنیم، اینبار به صفحه زیر خواهیم رسید:


بله. همانطور که مشاهده می‌کنید، مسیریابی پیش فرض، اینبار نیز برنده بوده است و اولین تطابق صورت گرفته با آن صورت می‌گیرد. بنابراین اصلا کار به استفاده از مسیریابی سفارشی تعریف شده توسط ما نخواهد رسید.
بنابراین محل تعریف این مسیریابی را اکنون به پیش از مسیریابی پیش فرض انتقال می‌دهیم:
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "SiteMap_route", // Route name
                "sitemap.xml", // URL with parameters
                new { controller = "Sitemap", action = "index", name = UrlParameter.Optional, area = "" } // Parameter defaults
            );

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );      
        }
در ادامه اگر مجددا مسیر sitemap.xml را درخواست کنیم، به تصویر ذیل خواهیم رسید:


بله. با این تنظیم صورت گرفته، اینبار دیگر سیستم مسیریابی، برای تفسیر مسیر سفارشی تعریف شده، به سراغ مسیریابی پیش فرض نخواهد رفت و کار همینجا خاتمه می‌یابد.

سؤال: آیا اینکار تداخلی در عملکرد اصلی برنامه ایجاد نمی‌کند؟ مثلا اگر به مسیر index کنترلر home مراجعه کنیم، مشکلی نخواهد بود؟
پاسخ: خیر. برای آزمایش آن باز هم به افزونه RouteDebug مراجعه خواهیم کرد:


همانطور که مشخص است، مسیریابی برنده در این حالت، همان مسیریابی پیش فرض است و نه مسیریابی سفارشی آدرس خاص sitemap.xml سایت.


یک نکته تکمیلی
افزونه گلیمپس نیز امکان دیباگ Routeها را دارد؛ اما توانایی بررسی مشکلات Routing یک خطای 404 مانند مثال فوق را حداقل تا زمان نگارش این مطلب ندارد و همان افزونه RouteDebug یاد شده، بهتر عمل می‌کند.
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت ششم - کامپوننت‌های تو در تو
با تشکر از مطالبتون.
من در فایل app.routes  یک کامپوننت را به عنوان Route پیش فرض تعیین کردم بنابراین وقتی AppComponent نمایش داده میشود این کامپوننت هم نمایش داده میشود .این کامپوننت شامل یک Output event می‌باشد .حالا کجا باید تعیین کرد که اگر این event رخ داد چه متدی صدا زده شود  یعنی  این خط را باید کجا نوشت :

(ratingClicked)='onRatingClicked($event)'
چون selector این کامپوننت در جایی وجود ندارد از طرفی چون مسیر پیش فرض می‌باشد ابتدا نمایش داده می‌شود.

کد فایل app.routes.ts
import { provideRouter, RouterConfig } from '@angular/router';
import { SubSystemComponent } from './subsystem/subsystem.component';
import { AppComponent } from './app.component';

const routes: RouterConfig = [
    { path: 'Subsystem', component: SubSystemComponent },
    { path: '', component: SubSystemComponent }
];

export const appRouterProviders = [
    provideRouter(routes)
];

کد html فایل app.component
<div>
    <div>Header</div>
    <div style="float:right">
        Menu
        <!--<app-menu #menucomp [subSystemId]="currentSubsSystemId">Loading Menu</app-menu>-->
    </div>
    <div>
        <!--روتر پیش فرض اینجا نمایش داده میشود نیاز است برای این روتر پارامتر ورودی و خروجی تعیین کرد-->
        <router-outlet></router-outlet>
    </div>
</div>


اشتراک‌ها
یک مثال ساده Asp.net Core و Angular 4
یک مثال ساده برای تازه کارانی که میخواهند با  Asp.net Core و Angular وب آپلیکشن‌های تک صفحه ای درست کنند.
از نسخه ی Angular 4.33 و  Angular CLI 1.27 استفاده شده
یک مثال ساده Asp.net Core و Angular 4
نظرات مطالب
آشنایی با فریمورک الکترون Electron
با تشکر
سوال1: منظورتون از «در بر گیرنده» چیه؟ (یعنی electron.net و electron-angular-native هر دو زیر مجموعه‌ی الکترون هستند.)
سوال۲: خب وقتی من می‌تونم درون پروژه‌ی الکترونی ام از angular و ... استفاده کنم دیگه چرا باید برم سراغ electron-angular-native؟
مطالب
AngularJS #1
پیش از اینکه آموزش AngularJs را شروع کنیم بهتر است با مفهوم برنامه‌های تک صفحه ای وب  و یا  Single Page Web Applications آشنا شویم؛ چرا که AngularJS برای توسعه هر چه ساده‌تر و قوی‌تر این گونه برنامه‌ها متولد شده است.

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

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

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

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

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

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

ویژگی‌های AngularJS:

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

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

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

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

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

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

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

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

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

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

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

جامعه‌ی متن باز بسیار قوی
   
این‌ها فقط یک مرور کلی بر توانایی‌های این فریم ورک بود و در ادامه هر کدام از این ویژگی را به صورت دقیق بررسی خواهیم کرد.
در مقاله‌ی بعدی، به چگونگی نصب AngularJS خواهیم پرداخت. سپس، اولین کد خود را با استفاده از آن خواهیم نوشت و مطالب Client Side Templates و MVC را دقیق‌تر بررسی خواهیم کرد.
 
اشتراک‌ها
معرفی و بررسی روش فعالسازی Bazel در Angular 8

Try Bazel’s opt-in preview in Angular CLI

In Google we build all our software with a tool called Bazel. We’ve been using it for the past over 12 years to build projects with any size — from small internal apps, to large applications such as Gmail, Google Drive, and Google Cloud Console. 

معرفی و بررسی روش فعالسازی Bazel در Angular 8