نظرات مطالب
مبانی TypeScript؛ ماژول‌ها
خیلی ممنون ، الان پروژه کامپایل شد ولی نتیجه ای که باید اتفاق میوفتاد نیوفتاد . توی developer tools وقتی Console Log مرورگر رو نگاه کردم موارد زیر خطا گرفته شده بود 
testmd.ts:1Uncaught ReferenceError: exports is not defined
require.js:143 Uncaught Error: Module name "testmd" has not been loaded yet for context: _. Use require([])

کتابخانه RequireJs رو هم به پروژه اضافه کردم حالا محتوای فایل html برابر با موارد زیر هست 
<!DOCTYPE html>

<html>
<head>
    <meta charset="utf-8" />
    <title>TypeScript HTML App</title>
    <link rel="stylesheet" href="app.css" type="text/css"/>
    <script src="Scripts/require.js"></script>
    <script src="Scripts/App/app.js"></script>
</head>
<body>
    <h1>TypeScript HTML App</h1>

    <div id="content"></div>
</body>
</html>
و فایل tsconfig.json  رو به پروژه با محتوای زیر اضافه کردم
{
    "compilerOptions": {
         "target": "es5",
         "outDir": "Scripts/App",
          "moduleResolution": "node",
          "module": "commonjs",
         "sourceMap": true,
         "experimentalDecorators": true,
         "emitDecoratorMetadata": true
    }
}

مشکل کجاست به نظرتون ؟
اشتراک‌ها
دانلود فیلم آموزشی نسبتا جامع در مورد SignalR ؛ مدت زمان : یک ساعت
توضیحاتی در مورد SignalR  به همراه نمونه برنامه : 1- بازی چند نفره  2- برنامه‌ی چت روم
In this segment the instructors go over SignalR, and an incredibly simple real-time web for .NET. It will also provide an overview for real-time hit counter, what SignalR is and how to build a chat application, a multi-player game and load balancing SignalR 
دانلود فیلم آموزشی نسبتا جامع در مورد SignalR ؛ مدت زمان : یک ساعت
نظرات مطالب
آموزش (jQuery) جی کوئری 5#
سلام؛ من یه اسلاید شو ساده را از اموزشهای یه سایت انگلیسی زبان ساختم که مدتهاست دنبالش بودم
اما هنوز یه ایراد کوچولو داره و اونهم اینه که بعد از رسیدن به اخرین عکس برمیگرده به اول یعنی بصورت بک اسلاید میشه و اگر عکسها از سمت راست به چپ اسلاید میشوند وقتی به اخرین عکس میرسه تمام عکسها در کسری از ثانیه از چپ به راست برمیگردند. نمونه کد کوئری رو میزارم و ممنون میشم منو در این زمینه راهنمایی کنید که چطور کاری کنم با رسیدن به اخرین عکس به همون روش از سمت راست به چپ دوباره برگرده به عکس اول نه تمام عکسها رو از چپ به راست برگردونه ؟
اسکریپت فراخوانده شده :
  < script src = "http://code.jquery.com/jquery-latest.js" ></ script >
 کوئری نوشته شده : 
<script type = "text/javascript" >
$(document).ready(function () { 
   slideShow();
});
 
var n = 0;
function slideShow() {
   id = n % 5 + 1;
   leftpost = (1 - parseInt(id)) * 500 + "px";
   $("div.slider-item").animate({ left: leftpost }, 1500);
   n = n + 1;
   s = setTimeout("slideShow()", 3000);
}
 </ script >
فایل css :
 

<style type = "text/css" >
div#slider {
  width: 500px;
  height: 300px;
  margin: auto;
  overflow: hidden;
  border: 10px solid gray;
}
div#slider-mask {
  width: 500%;
  height: 100%;
}
div.slider-item {
  width: 20%;
  height: 100%;
  position: relative;
  float: left;
}
 </ style >
فایل html :
  < div id = "slider" > < div id = "slider-mask" > 
< div class = "slider-item" >< img src = "img1.jpg" alt = "1" /></ div > 
< div class = "slider-item" >< img src = "img2.jpg" alt = "2" /></ div > 
< div class = "slider-item" >< img src = "img3.jpg" alt = "3" /></ div >
 < div class = "slider-item" >< img src = "img4.jpg" alt = "4" /></ div > 
< div class = "slider-item" >< img src = "img5.jpg" alt = "5" /></ div >
</ div > </ div >
مطالب
آشنایی با Saltarelle کامپایلر قدرتمند #C به جاوااسکریپت

شاید ساده‌ترین تعریف برای  Saltarelle  این باشد که «کامپایلریست که کد‌های C# را به جاوا اسکریپت تبدیل می‌کند». محاسن زیادی را می‌توان برای اینگونه کامپایلر‌ها نام برد؛ مخصوصا در پروژه‌های سازمانی که نگهداری از کد‌های جاوا اسکریپت بسیار سخت و گاهی خارج از توان است و این شاید مهمترین عامل ظهور ابزارهای جدید از قبیل Typescript باشد.

در هر صورت اگر حوصله و وقت کافی برای تجهیز تیم نرم افزاری، به دانش یک زبان جدید مانند Typescript نباشد، استفاده از توان و دانش تیم تولید، از زبان C# ساده‌ترین راه حل است و اگر ابزاری مطمئن برای استفاده از حداکثر قدرت JavaScript همراه با امکانات نگهداری و توسعه کد‌ها وجود داشته باشد، بی شک Saltarelle یکی از بهترین‌های آنهاست.

قبلا کامپایلر هایی از این دست مانند  Script# وجود داشتند، اما فاقد همه امکانات C# بوده وعملا قدرت کامل C# در کد نویسی وجود نداشت. اما با توجه به ادعای توسعه دهندگان این کامپایلر سورس باز در استفاده‌ی حداکثری از کلیه ویژگی‌های C# 5 و با وجود Library ‌های متعدد می‌توان Saltarelle  را عملا یک کامپایلر موفق در این زمینه دانست.

برای استفاده از Saltarelle در یک برنامه وب ساده باید یک پروژه Console Application به Solution اضافه کرد و پکیج Saltarelle.Compiler را از nuget نصب نمایید. بعد از نصب این پکیج، کلیه Reference ‌ها از پروژه حدف می‌شوند و هر بار Build توسط کامپایلر Saltarelle  انجام می‌شود. البته با اولین Build، مقداری Error را خواهید دید که برای از بین بردنشان نیاز است پکیج Saltarelle.Runtime را نیز در این پروژه نصب نمایید:

PM> Install-Package Saltarelle.Compiler
PM> Install-Package Saltarelle.Runtime

در صورتیکه کماکان Build  نهایی با Error همرا بود، یکبار این پروژه را Unload  و سپس مجددا Load نمایید



UI یک پروژه وب MVC است و Client یک Console Application که پکیج‌های مورد نیاز Saltarelle  روی آن نصب شده است.

در صورتیکه پروژه را Build نماییم و نگاهی به پوشه‌ی Debug بیاندازیم، یک فایل JavaScript همنام پروژه وجود دارد:


برای اینکه بعد از هر بار Build ، فایل اسکریپت به پوشه‌ی مربوطه در پروژه UI منتقل شود کافیست کد زیر را در Post Build  پروژه Client بنویسیم: 

copy "$(TargetDir)$(TargetName).js" "$(SolutionDir)SalratelleSample.UI\Scripts"

اکنون پس از هر بار Build ، فایل اسکریپت مورد نظر در پوشه‌ی Scripts پروژه UI  آپدیت می‌شود:


در ادامه کافیست فایل اسکریپت را به layout اضافه کنیم. 

<script src="~/Scripts/SaltarelleSample.Client.js"></script>

در پوشه‌ی Saltarelle.Runtime در پکیج‌های نصب شده، یک فایل اسکریپت به نام mscorlib.min.js نیز وجود دارد که حاوی اسکریپت‌های مورد نیاز Saltarelle در هنگام اجراست. آن را به پوشه اسکریپت‌های پروژه UI کپی نمایید و سپس به Layout  اضافه کنید. 

<script src="~/Scripts/mscorlib.min.js"></script>
<script src="~/Scripts/SaltarelleSample.Client.js"></script>

حال نوبت به اضافه نمودن library‌های مورد نیازمان است. برای دسترسی به آبجکت هایی از قبیل document, window, element و غیره در جاوااسکریپت می‌توان پکیج Saltarelle.Web را در پروژه‌ی Client نصب نمود و برای دسترسی به اشیاء و فرمانهای jQuery، پکیج Salratelle.jQuery را نصب نمایید. 

> Install-Package Saltarelle.Web
> Install-Package Saltarelle.jQuery

به این library‌ها imported library می‌گویند. در واقع، در زمان کامپایل، برای این library‌ها فایل اسکریپتی تولید نمی‌شود و فقط آبجکت‌های #C هستند که که هنگام کامپایل تبدیل به کدهای ساده اسکریپت می‌شوند که اگر اسکریپت مربوط به آنها به صفحه اضافه نشده باشد، اجرای اسکریپت با خطا مواجه می‌شود.

به طور ساده‌تر وقتی از jQuery library استفاده می‌کنید هیچ فایل اسکریپت اضافه‌ای تولید نمی‌شود، اما باید اسکریپت jQuery به صفحه شما اضافه شده باشد.

<script src="~/Scripts/jquery-1.10.2.min.js"></script>

مثال ما یک اپلیکیشن ساده برای خواندن فید‌های همین سایت است. ابتدا کد‌های سمت سرور را در پروژه UI  می نویسیم.

کلاس‌های مورد نیاز ما برای این فید ریدر: 

public class Feed
    {
        public string FeedId { get; set; }
        public string Title { get; set; }
        public string Address { get; set; }

    }
    public class Item
    {
        public string Title { get; set; }
        public string Link { get; set; }
        public string Description { get; set; }
    }

و یک کلاس برای مدیریت منطق برنامه 

 public class SiteManager
    {
        private static List<Feed> _feeds;
        public static List<Feed> Feeds
        {
            get
            {
                if (_feeds == null)
                    _feeds = CreateSites();
                return _feeds;
            }
        }
        private static List<Feed> CreateSites()
        {
            return new List<Feed>() { 
                new Feed(){
                    FeedId = "1",
                    Title = "آخرین تغییرات سایت",
                    Address = "https://www.dntips.ir/rss.xml"
                },
                 new Feed(){
                    FeedId = "2",
                    Title = "مطالب سایت",
                    Address = "https://www.dntips.ir/feeds/posts"
                },
                 new Feed(){
                    FeedId = "3",
                    Title = "نظرات سایت",
                    Address = "https://www.dntips.ir/feeds/comments"
                },
                 new Feed(){
                    FeedId = "4",
                    Title = "خلاصه اشتراک ها",
                    Address = "https://www.dntips.ir/feed/news"
                },
            };
        }

        public static IEnumerable<Item> GetNews(string id)
        {
            XDocument feedXML = XDocument.Load(Feeds.Find(s=> s.FeedId == id).Address);
            var feeds = from feed in feedXML.Descendants("item")
                        select new Item
                        {
                            Title = feed.Element("title").Value,
                            Link = feed.Element("link").Value,
                            Description = feed.Element("description").Value
                        };
            return feeds;
        }

    }

کلاس SiteManager فقط یک لیست از فید‌ها دارد و متدی که با گرفتن شناسه‌ی فید ، یک لیست از آیتم‌های موجود در آن فید ایجاد می‌کند.

حال دو ApiController برای دریافت داده‌ها ایجاد می‌کنیم

public class FeedController : ApiController  
{
        // GET api/<controller>
        public IEnumerable<Feed> Get()
        {
            return SiteManager.Feeds;
        }
    }

public class ItemsController : ApiController
    {
        // GET api/<controller>/5
        public IEnumerable<Item> Get(string id)
        {
            return SiteManager.GetNews(id);
        }
    }

در View پیش‌فرض که Index از کنترلر Home  است،  یک Html ساده برای فرم  صفحه اضافه می‌کنیم 

<div>
    <div>
        <h2>Feeds</h2>
        <ul id="Feeds">
           
        </ul>
    </div>
    <div>
        <h2>Items</h2>
        <p id="FeedItems">
        </p>
    </div>
   
</div>

در المنت Feeds لیست فید‌ها را قرار می‌دهیم و در FeedItems آیتم‌های مربوط به هر فید. حال به سراغ کد‌های سمت کلاینت می‌رویم و به جای جاوا اسکریپت از Saltarelle استفاده می‌کنیم.

کلاس Program را از پروژه Client باز می‌کنیم و متد Main را به شکل زیر تغییر می‌دهیم:

static void Main()
        {
            jQuery.OnDocumentReady(() => {
                FillFeeds();
            });
        }

بعد از کامپایل شدن، کد #C شارپ بالا به صورت زیر در می‌آید: 

$SaltarelleSample_Client_$Program.$main = function() {
$(function() {
$SaltarelleSample_Client_$Program.$fillFeeds();
});
};
$SaltarelleSample_Client_$Program.$main();

و این همان متد معروف jQuery است که Saltarelle.jQuery برایمان ایجاد کرده است.

متد FillFeeds را به شکل زیر پیاده سازی می‌کنیم

private static void FillFeeds()
        {
            jQuery.Ajax(new jQueryAjaxOptions()
            {
                Url = "/api/feed",
                Type = "GET",
                Success = (d,t,r) => {

                    // Fill 
                    var ul = jQuery.Select("#Feeds");
                    jQuery.Each((List<Feed>)d, (idx,i) => {
                        var li = jQuery.Select("<li>").Text(i.Title).CSS("cursor", "pointer");
                        li.Click(eve => {
                            FillData(i.FeedId);
                        });
                        ul.Append(li);
                    });
                }
            });
        }

آبجکت jQuery، متدی به نام Ajax دارد که یک شی از کلاس jQueryAjaxOptions را به عنوان پارامتر می‌پذیرد. این کلاس کلیه خصوصیات متد Ajax در jQuery را پیاده سازی می‌کند. نکته شیرین آن توانایی نوشتن lambda برای Delegate هاست.

خاصیت Success یک Delegate است که 3 پارامتر ورودی را می‌پذیرد.

public delegate void AjaxRequestCallback(object data, string textStatus, jQueryXmlHttpRequest request);

data همان مقداریست که api باز می‌گرداند که یک لیست از Feed هاست. برای زیبایی کار، من یک کلاس Feed در پروژه Client اضافه می‌کنم که خصوصیاتی مشترک با کلاس اصلی سمت سرور دارد و مقدار برگشی Ajax را به آن تبدیل می‌کنم.

کلاس Feed و Item

 [PreserveMemberCase()]
    public class Feed
    {
        //[ScriptName("FeedId")]
        public string FeedId;

        //[ScriptName("Title")]
        public string Title;

        //[ScriptName("Address")]
        public string Address;

    }

    [PreserveMemberCase()]
    public class Item
    {
        // [ScriptName("Title")]
        public string Title;

        // [ScriptName("Link")]
        public string Link;

        // [ScriptName("Description")]
        public string Description;
    }
Attrubute‌های زیادی در Saltarelle وجود دارند و از آنجایی که کامپایلر اسم فیلد‌ها را camelCase کامپایل می‌کند من برای جلوگیری از آن از PreserveMemberCase  بر روی هر کلاس استفاده کردم. می‌توانید اسم هر فیلد را سفارشی کامپایل نمایید. 
jQuery.Each((List<Feed>)d, (idx,i) => {
                        var li = jQuery.Select("<li>").Text(i.Title).CSS("cursor", "pointer");
                        li.Click(eve => {
                            FillData(i.FeedId);
                        });
                        ul.Append(li);
                    });

به ازای هر آیتمی که در شیء بازگشتی وجود دارد، با استفاد از متد each در jQuery یک li ایجاد می‌کنیم. همان طور که می‌بینید کلیه خواص، به شکل Fluent قابل اضافه شدن می‌باشد. سپس برای li یک رویداد کلیک که در صورت وقوع، متد FillData را با شناسه فید کلیک شده فراخوانی می‌کند و در آخر li را به المنت ul اضافه می‌کنیم.

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

private static void FillData(string p)
        {
            jQuery.Ajax(new jQueryAjaxOptions()
            {
                Url = "/api/items/" + p,
                Type = "GET",
                Success = (d, t, r) => {
                    var content = jQuery.Select("#FeedItems");
                    content.Html("");
                    foreach (var item in (List<Item>)d)
                    {
                        var row = jQuery.Select("<div>").AddClass("row").CSS("direction", "rtl");
                        var link = jQuery.Select("<a>").Attribute("href", item.Link).Text(item.Title);
                        row.Append(link);
                        content.Append(row);
                    }
                }
            });
        }
خروجی برنامه به شکل زیر است: 

در این مثال ما از Saltarelle.jQuery برای استفاده از jQuery.js استفاده نمودیم. library‌های متعددی برای Saltarelle  از قبیل  linq,angular,knockout,jQueryUI,nodeJs ایجاد شده و همچنین قابلیت‌های زیادی برای نوشتن imported library‌های سفارشی نیز وجود دارد. 

مطمئنا استفاده از چنین کامپایلرهایی راه حلی سریع برای رهایی از مشکلات متعدد کد نویسی با جاوا اسکریپت در نرم افزارهای بزرگ مقیاس است. اما مقایسه آنها با ابزارهایی از قبیل typescript احتیاج به زمان و تجربه کافی در این زمینه دارد.

مطالب
HTML5 Web Component - قسمت دوم - Custom Elements
Custom Elements، دارای یک چرخه حیات می‌باشند. در طی این چرخه حیات، می‌توان تعدادی متد خاص را به المان سفارشی خود اضافه کرد که به صورت خودکار توسط مرورگر فراخوانی می‌شوند. به این متدها Life-cycle Callbacks یا Custom Element Reactions نیز می‌گویند. برای درک بهتر چرخه حیات مذکور، به تکه کد زیر توجه نمائید:
customElements.define("x-component", class extends HTMLElement {
    constructor() {
        super();
        console.log('constructed!');
    }

    connectedCallback() {
        console.log('connected!');
    }

    disconnectedCallback() {
        console.log('disconnected!');
    }

    adoptedCallback() {
        console.log('adopted!');
    }

    attributeChangedCallback(name, oldValue, newValue) {
        console.log('attirbuteChanged!', name, oldValue, newValue);
    }

    static get observedAttributes() {
        return ['checked','demo','label'];
    }
});

Element Upgrades
به صورت پیش‌فرض، المان‌های موجود در DOM که مبتنی‌بر استانداردهای HTML تعریف نشده‌ا‌ند، توسط مرورگر به عنوان HTMLUnknownElement تجزیه و تحلیل خواهند شد. ولی این موضوع برای المان‌های سفارشی که نام معتبری دارند (وجود «-»)، صدق نمی‌کند. برای مثال دو خط کد زیر را در کنسول مربوط به Developer tools مرورگر خود اجرا کنید:
// "tabs" is not a valid custom element name
document.createElement('tabs') instanceof HTMLUnknownElement === true //true

// "x-tabs" is a valid custom element name
document.createElement('x-tabs') instanceof HTMLElement === true //true
در این صورت، امکان استفاده از المان سفارشی، قبل از معرفی و ثبت آن توسط متد customElements.define نیز وجود خواهد داشت. یعنی اگر در DOM شما تعدادی المان سفارشی وجود داشته باشند که به هر دلیل نیاز است پس از گذشت یک بازه زمانی کوتاهی معرفی و ثبت شوند (مثال: lazy load اسکریپت‌های متناظر با المان‌های سفارشی در Angular)، این المان‌ها معتبر هستند. فرآیند فراخوانی متد define و استفاده از کلاس معرفی شده برای ارتقاء المان سفارشی موجود در DOM، اصطلاحا Element Upgrades نامیده می‌شود. همچنین با استفاده از متد customElements.whenDefined که یک Promise را بازگشت می‌دهد، می‌توان از معرفی و ثبت شدن المان خاصی آگاه شد:
customElements.whenDefined('x-component').then(() => {
  console.log('x-component defined');
});
یا حتی امکان استفاده از سلکتور «‎:defined‏» نیز به شکل زیر وجود دارد:
<share-buttons>
  <social-button type="twitter"><a href="...">Twitter</a></social-button>
  <social-button type="fb"><a href="...">Facebook</a></social-button>
  <social-button type="plus"><a href="...">G+</a></social-button>
</share-buttons>

// Fetch all the children of <share-buttons> that are not defined yet.
let undefinedButtons = buttons.querySelectorAll(':not(:defined)');

let promises = [...undefinedButtons].map(socialButton => {
  return customElements.whenDefined(socialButton.localName);
));

// Wait for all the social-buttons to be upgraded.
Promise.all(promises).then(() => {
  // All social-button children are ready.
});
در اینجا ابتدا تمام المان‌های تعریف نشده، کوئری شده‌اند و با استفاده از متد map و اجرای متد whenDefined، به لیستی از Promiseها رسیده‌ایم و در نهایت با استفاده از Promise.all منتظر اتمام مرحله upgrade المان‌های مذکور هستیم.
سازنده مرتبط با کلاس المان سفارشی x-component، در هنگام وهله‌سازی یا فرآیند upgrades فراخوانی می‌شود و می‌تواند برای مقداردهی اولیه خواص وهله جاری، تنظیم رخدادگردان‌ها (Event Listeners) و یا ایجاد و اتصال ShadowDOM با استفاده از متد attachShadow، محل مناسبی باشد. طبق مستندات مرتبط، فراخوانی ()super بدون ارسال هیچ آرگومانی باید در اولین خط این سازنده انجام شود.
برای وهله‌سازی المان سفارشی نیز می‌توان از متد customeElements.get به شکل زیر استفاده کرد:
customeElements.define('x-component',...)
let XComponent = customElements.get('x-component');
document.body.appendChild(new XComponent())
متد get، ارجاعی به سازنده کلاس x-component را بازگشت خواهد داد.

connectedCallback 
 اولین متد بعد از فراخوانی سازنده، connectedCallback نام دارد و زمانی رخ می‌دهد که وهله‌ای از یک المان سفارشی به Light DOM افزوده شده‌است و یا توسط Parser مرورگر، در DOM شناسایی شود. این متد، محل پیشنهاد شده برای اجرای کدهای زمان راه‌اندازی مانند دریافت منابع از سرور و یا رندر کردن محتوایی خاص، می‌باشد. 
نکته: این متد ممکن است بیش از یکبار نیز فراخوانی شود! هنگامیکه یک المان موجود در DOM از طریق کد از DOM جداشده و سپس اضافه شود:
const el = document.createElement('x-component');
document.body.appendChild(el);
// connectedCallback() called

el.remove();
// disconnectedCallback()

document.body.appendChild(el);
// connectedCallback() called again


disconnectedCallback
این متد نیز هر وقت المانی از DOM حذف شود، اجرا خواهد شد و مانند متد connectedCallback ممکن است چندین بار فراخوانی شود. همچنین محل مناسبی برای آزادسازی منابع استفاده شده در پیاده‌سازی المان سفارشی، می‌باشد. مانند: استفاده از متد clearInterval برای پاکسازی یک تایمر که با متد setInterval ایجاد شده‌است.  
نکته: هیچ تعهدی به اجرای متد disconnectedCallback در تمام حالاتی که یک المان از DOM حذف می‌شود، وجود ندارد. به عنوان مثال هنگامیکه یک برگه‌ی مرورگر بسته شود، این متد فراخوانی نخواهد شد.

‌attributeChangedCallback
 این متد هنگامی فراخوانی خواهد شد که خصوصیات مشخص شده از طریق observedAttributes به عنوان یک static getter، اضافه، حذف، ویرایش و یا جایگزین شوند. همچنین در مرحله Upgrades برای مقادیر اولیه خصوصیات که توسط استفاده کننده از المان سفارشی، مشخص شده‌است نیز فراخوانی می‌شود.
adoptedCallback
متدهای قبلی بیشترین استفاده را دارند؛ این متد خاص نیز زمانیکه یک المان سفارشی به یک DOM دیگری منتقل می‌شود، اجرا خواهد شد. برای مثال:
const iframe = document.querySelector('iframe');
const iframeImages = iframe.contentDocument.querySelectorAll('img');
const newParent = document.getElementById('images');

iframeImages.forEach(function(imgEl) {
  newParent.appendChild(document.adoptNode(imgEl));
});
در تکه کد بالا، لیست المان‌های img موجود در داخل iframe کوئری شده و سپس با پیمایش بر روی لیست بدست آمده و در زمان فراخوانی متد adoptNode که کار تغییر ownerDocument مرتبط با یک المان را انجام می‌دهد، متد adoptedCallback ما نیز اجرا خواهد شد.

Methods

اگر المان‌های بومی و استاندارد موجود را بررسی کنید، همه آنها دارای یکسری متد، پراپرتی و صفات مشخصی هستند. در اینجا نیز تعریف رفتاری برای یک المان سفارشی و کپسوله، نکته خاصی ندارد و به شکل زیر قابل تعریف و استفاده می‌باشد:
class XComponent extends HTMLElement {
  constructor() {
    super();
  }
  
  doSomething(){
    console.log('doSomething');
  }
}

let component = document.querySelector('x-component');
component.doSomething();
مشخص است که امکان تعریف انواع و اقسام متدها با پارامترها و خروجی‌های مختلفی و حتی نسخه‌های همزمان یا ناهمزمان آن‌ها وجود دارد.

Attributes & Properties

در HTML خیلی رایج است که مقادیر پراپرتی‌های یک المان در قالب یکسری صفات، نمودی در DOM هم داشته باشند. برای مثال:
div.id = 'id-value';
div.hidden = true;
انتظار چنین خروجی داریم:
<div id="id-value" hidden>
این موضوع، تحت عنوان «Reflecting Properties to Attributes» مطرح می‌باشد. در این صورت علاوه بر اینکه با یک نگاه به DOM، از مقادیر خصوصیات یک المان آگاه خواهیم بود، امکان استفاده از این صفات به عنوان سلکتورهایی در زمان استایل‌دهی نیز وجود دارد. حال از این مکانیزم برای اعمال یکسری صفات دسترسی‌پذیری مانند صفات ARIA به المان سفارشی خود نیز می‌توان استفاده کرد. برای مثال:
class XComponent extends HTMLElement {
    constructor() {
        super();
    }

    connectedCallback() {
        this._render();
    }

    get disabled() {
        return this.hasAttribute('disabled');
    }

    set disabled(val) {
        // Reflect the value of `disabled` as an attribute.
        if (val) {
            this.setAttribute('disabled', '');
        } else {
            this.removeAttribute('disabled');
        }

        this._render();
    }

    _render() {
        //... 
    }
}
ایده کار خیلی ساده است؛ پراپرتی‌های یک المان‌سفارشی را از طریق متدهای getter و setter تعریف کرده و در بدنه پیاده‌سازی آنها، صفات HTML ای المان جاری را تغییر داده و یا از مقادیر آنها استفاده کنیم.
اینبار با مقداردهی پراپرتی disabled برروی وهله‌ای از المان سفارشی ما، این مقادیر نمودی در DOM هم خواهند داشت. با استفاده از متدهای setAttribute یا removeAttribute کار همگام‌سازی پراپرتی‌ها با صفات را انجام داده‌ایم.
همچین با استفاده از متد attributeChangedCallback نیز می‌توان برای اعمال صفات ARIA که اشاره شد، به شکل زیر استفاده کرد:
attributeChangedCallback(name, oldValue, newValue) {
  switch (name) {
    case 'checked':
      // Note the attributeChangedCallback is only handling the *side effects*
      // of setting the attribute.
      this.setAttribute('aria-disabled', !!newValue);
      break;
    ...
  }
یا حتی به شکل زیر:
attributeChangedCallback(name, oldValue, newValue) {
    // When the component is disabled, update keyboard/screen reader behavior.
    if (this.disabled) {
      this.setAttribute('tabindex', '-1');
      this.setAttribute('aria-disabled', 'true');
    } else {
      this.setAttribute('tabindex', '0');
      this.setAttribute('aria-disabled', 'false');
    }
    // TODO: also react to the other attribute changing.
  }
در اینجا از همان getter که طبق پیاده‌سازی ما، در پشت صحنه از همان مقدار صفت disabled استفاده می‌کند، برای تنظیم یکسری صفات دیگر استفاده کرده‌ایم. به عنوان مثال اگر المان ما غیرفعال شده بود، صفت tabindex آن را با «‎-1‏» مقداردهی می‌کنیم تا از توالی پیمایش مبتنی‌بر Tab خارج شود.
نکته: پیشنهاد می‌شود از مکانیزم همگام‌سازی پراپرتی‌ها و صفات، برای انواع داده‌ای اولیه (رشته، عدد و ...) استفاده شود و برای دریافت مقادیری مانند objects و یا arrays، از متدها یا پراپرتی‌ها استفاده کنید. در غیر این صورت نیاز خواهد بود که این مقادیر را سریالایز و در داخل المان سفارشی، عملیات معکوس آن را انجام دهید که می‌تواند هزینه‌ی زیادی داشته باشد. عملیات سریالایز نیز خود باعث از دست دادن ارجاعات به آن مقادیر خواهد شد. به صورت کلی هیچکدام از المان‌های بومی موجود، چنین اطلاعاتی را دریافت نمی‌کند. برای مثال:
constructor() {
    super();

    this._data = [];
}

get data() {
    return _data;
}

set data(value) {
    if (this_data === value) return;
    this._data = value;
    this._render();
}

Lazy Properties

همانطور که اشاره شد حتی قبل از مرحله upgrades مربوط به المان‌های سفارشی استفاده شده در سند HTML برنامه شما، به عنوان المان‌های معتبری هستند که امکان کوئری کردن و مقداردهی اولیه خصوصیات آنها از طریق کد نیز ممکن است. این موضوع زمانیکه از فریم‌ورکی مثل Angular استفاده می‌شود، المان موردنظر به صورت خودکار توسط فریم‌ورک لود و به صفحه اضافه شده و در انتهای عملیات، binding پراپرتی‌های آن به خصوصیات موجود در کامپوننت Angular ای انجام خواهد شد. به مثال زیر توجه کنید:
<x-component [disabled]="model.disabled"></x-component>
در این صورت اگر لود اسکریپت، معرفی و ثبت این المان سفارشی به صورت Lazy انجام شود، امکان آن وجود دارد که فریم‌ورک، عملیات binding را قبل از مرحله upgrades انجام دهد. خوب... این موضوع چه مشکلی را ایجاد می‌کند؟ در این صورت چون مرحله upgrades تمام نشده است، پیاده‌سازی بدنه متد setter متناظر با خصوصیات المان سفارشی، توسط پراپرتی جدیدی که توسط فریم‌ورک برروی وهله موجود تعریف می‌شود، بی‌استفاده خواهد ماند. برای مثال، اگر سعی کنیم قبل از مرحله upgrades خصوصیت disabled المان x-component را مقداردهی کنیم، عملیات مکانیزم همگام‌سازی مدنظر ما اجرا نخواهد شد:
let el = document.querySelector('x-component');
el.disabled = true;

customElements.define("x-component", class extends HTMLElement {
    constructor() {
        super();
    }

    get disabled() {
        return this.hasAttribute('disabled');
    }

    set disabled(val) {
        // Reflect the value of `disabled` as an attribute.
        if (val) {
            this.setAttribute('disabled', '');
        } else {
            this.removeAttribute('disabled');
        }

        this._render();
    }
});

با این خروجی مواجه خواهیم شد که هیچ اثری از صفت disabled دیده نمی‌شود:


یکی از روش‌های پیشنهاد شده برای حل این مشکل، مقداردهی مجدد پراپرتی‌ها بعد از مرحله upgrades و پس از اینکه متد setter تعریف شده‌است، می‌باشد:
let el = document.querySelector('x-component');
el.disabled = true;

customElements.define("x-component", class extends HTMLElement {
    constructor() {
        super();
    }

    connectedCallback() {
        this._upgradeProp('disabled');
    }

    get disabled() {
        return this.hasAttribute('disabled');
    }

    set disabled(val) {
        // Reflect the value of `disabled` as an attribute.
        if (val) {
            this.setAttribute('disabled', '');
        } else {
            this.removeAttribute('disabled');
        }

        this._render();
    }

    _upgradeProp(prop) {
        if (this.hasOwnProperty(prop)) {
            let value = this[prop];
            delete this[prop]; //delete instance property
            this[prop] = value; // set prototype property
        }
    }
});
ایده کار به این صورت است که مقدار پراپرتی مورد نظر را که قبل از مرحله upgrades برروی وهله جاری (instance property) تنظیم شده‌است، در متغییری نگهداری کرده و آن پراپرتی را حذف و سپس پراپرتی تعریف شده در کلاس (prototype property) را برای وهله جاری مقداردهی کنیم.
نکته: به یاد داشته باشید که قبل از اینکه یکسری صفات خاص را مقدار دهی کنید، بررسی شود که استفاده کننده از المان سفارشی، مقداری را تنظیم نکرده باشد. برای مثال اگر قصد دارید المان سفارشی شما قابلیت focus را داشته باشد، نیاز است شما حداقل tabindex=-1 را تنظیم کنید؛ حتی اگر استفاده کننده، آن را مقداردهی نکرده باشد:
connectedCallback() {
  if (!this.hasAttribute('role'))
    this.setAttribute('role', 'checkbox');
  if (!this.hasAttribute('tabindex'))
    this.setAttribute('tabindex', -1); //element is not reachable via sequential keyboard navigation, but could be focused
}

مطالب
Angular Material 6x - قسمت دوم - معرفی Angular Flex layout
در این سری قصد داریم یک برنامه‌ی ساده‌ی دفترچه تلفن را توسط Angular 6x و کامپوننت‌های متریال آن ایجاد کنیم؛ اما Grid جزئی از بسته‌ی Angular Material نیست. بنابراین برای طرحبندی برنامه و قرار دادن المان‌های مختلف در مکان‌های تعیین شده‌ی صفحه، از Angular FlexBox Module استفاده خواهیم کرد که محصور کننده‌ی CSS 3 FlexBox است.


آشنایی با Flex Layout Box Model

برای طراحی ظاهر یک برنامه‌ی وب نیاز است عناصر آن‌را در مکان‌های مختلفی از صفحه قرار داد که به آن Layout گفته می‌شود. برای این منظور عموما 4 روش ذیل مرسوم هستند:
1. Table
2. Float, position, clear
3. CSS Grids
4. FlexBox CSS

امروزه دیگر آنچنان روش‌های 1 و 2 به صورت مستقیم مورد استفاده قرار نمی‌گیرند. CSS Grid روش نهایی طراحی Layout در آینده خواهد بود و در حال حاضر تعداد مرورگرهایی که از آن پشتیبانی می‌کنند، قابل توجه نیست؛ اما از FlexBox در IE 11، کروم 21 و فایرفاکس 22 به بعد پشتیبانی می‌شود.


FlexBox CSS، سیلان المان‌های قرار گرفته‌ی در داخل آن‌را سبب می‌شود. در اینجا یک container اصلی وجود دارد که در برگیرنده‌ی المان‌ها است. در تصویر فوق دو محور را مشاهده می‌کنید. محور افقی از چپ به راست ادامه پیدا می‌کند. محور عمودی نحوه‌ی ارتباط عناصر را مشخص می‌کند.
اکنون این سؤال مطرح می‌شود که چه تفاوتی بین یک Grid و FlexBox CSS وجود دارد؟ در یک Grid طراحی دو بعدی سطرها و ستون وجود دارد. اما به FlexBox باید به صورت سیلان یک بعدی سلول‌ها نگاه کرد. برای مثال عناصر قرار گرفته‌ی درون Container یا به صورت افقی درون آن گسترده شده و قرار می‌گیرند و یا به صورت عمودی.


نحوه‌ی تفکر و کارکرد با FlexBox چگونه است؟

در اینجا باید به دو مفهوم دقت داشت:
الف) سیلان عناصر درون Container که می‌تواند افقی و یا عمودی باشد.
ب) اندازه‌ی المان‌ها که می‌تواند ثابت و یا نسبی باشد.

یک Container، جهت سیلان عناصر درون آن‌را مشخص می‌کند. المان‌های آن، اندازه، فاصله‌ی از یکدیگر و ترتیب قرارگیری را ارائه می‌دهند. یک flex container می‌تواند شامل چندین flex container تو در تو نیز باشد.


نحوه‌ی سیلان عناصر در FlexBox چگونه است؟

برای نمونه طرحبندی متداول ذیل را درنظر بگیرید:


نحوه تفکر در مورد طراحی این طرحبندی، باید از بیرون به درون و از بالا به پایین (سیلان عمودی) باشد:


سپس نحوه‌ی سیلان عناصر درون Containerهای تعریف شده را مشخص می‌کنیم. برای مثال اولین Container دارای سیلان افقی از چپ به راست خواهد بود که عنصر سوم آن به دلیل اندازه‌های مشخص دو عنصر قبلی، به سطر دوم منتقل شده‌است.


در ادامه به قسمت میانی می‌رسیم که آن نیز دارای سیلان افقی از چپ به راست است:


در اینجا نیز می‌توان سه Container را متصور شد که وسطی دارای سیلان افقی از راست به چپ است و مواردی که بر اساس اندازه‌ی آن‌ها در یک سطر جا نشده‌اند، به سطر بعدی منتقل خواهند شد:


و تمام این سیلان‌ها و انتقال به سطرهای بعدی بر اساس اندازه‌ی المان‌ها صورت می‌گیرد:


البته در این تصویر یک ایراد هم وجود دارد. با توجه به اینکه در ناحیه‌ی میانی سه Container تعریف خواهند شد. Container ایی که در میان آن قرار می‌گیرد، دارای سیلان خاص خودش است و اندازه‌های آن باید نسبت به این Container تعریف شوند و نه نسبت به کل ناحیه‌ی میانی. یعنی بجای اینکه 50 درصد، 25، 25 و 50 درصد را داشته باشیم، این‌ها در اصل 100 درصد، 50 و 50 درصد و سپس 100 درصد هستند.


معرفی کتابخانه‌ی Angular Flex Layout

برای کار با Flex CSS نیاز است:
- مقدار زیادی کد CSS نوشت.
- نیاز به درک عمیقی از Flex Box دارد.
- نیاز است با باگ‌های مرورگرها و تفاوت‌های پیاده سازی‌های آن‌ها در مورد FlexBox آشنا بود.
- نیاز به Prefixing دارد.
- برای Angular طراحی نشده‌است.

جهت رفع این مشکلات و محدودیت‌ها، تیم Angular کتابخانه‌ای را به نام Angular Flex Layout مخصوص نگارش‌های جدید Angular طراحی کرده‌است. این کتابخانه مستقل از Angular Material است اما عموما به همراه آن استفاده می‌شود.

مزایای کار با کتابخانه‌ی Angular flex layout
- یک کتابخانه‌ی متکی به خود و مستقل است و برای کار با آن الزامی به استفاده‌ی از Angular Material نیست.
- به همراه هیچ فایل CSS جانبی ارائه نمی‌شود.
- پیاده سازی TypeScript ایی دارد. در اصل یک سری directives مخصوص Angular است که با TypeScript نوشته شده‌است.
- به صورت پویا و inline تمام CSSهای مورد نیاز را تولید و تزریق می‌کند.
- به همراه یک API استاتیک است و همچنین یک API واکنشگرا
- با Angular CLI نیز یکپارچه شده‌است.


نصب و تنظیم کتابخانه‌ی Angular Flex layout

برای نصب این کتابخانه، در ریشه‌ی پروژه دستور زیر را صادر کنید:
 npm install @angular/flex-layout --save
سپس ماژول آن‌را باید به shared.module.ts اضافه کرد:
import { FlexLayoutModule } from "@angular/flex-layout";

@NgModule({
  imports: [
    FlexLayoutModule
  ],
  exports: [
    FlexLayoutModule
  ]
})
export class SharedModule {
}


کار با API استاتیک Angular Flex layout

API استاتیک Angular Flex layout شامل این مزایا و مشخصات است:
- به صورت یکسری دایرکتیو Angular طراحی شده‌است که به HTML قالب کامپوننت‌ها اضافه می‌شود.
- از data binding پشتیبانی می‌کند.
- CSS نهایی را به صورت پویا و inline تولید و به صفحه تزریق می‌کند. Inline CSS تزریق شده به ویژگی‌های styles هر المان تزریق می‌شوند و موارد مشابه را در صورت وجود بازنویسی می‌کنند.
- از تشخیص تغییرات پشتیبانی می‌کند.
- به همراه ویژگی‌های fxHide و fxShow است.
- کارآیی مطلوبی دارد.

در اینجا برای تعریف container اصلی از دایرکتیوهای زیر استفاده می‌شود:
- fxLayout جهت‌های flex را مشخص می‌کند.
<div fxLayout="row" 
     fxLayout.xs="column"></div>
- fxLayout می‌تواند دارای مقداری مانند row، column و row-reverse و column-reverse باشد. برای مثال مقدار row-reverse‌، نمایش از راست به چپ را سبب می‌شود.
- fxLayoutWrap مشخص می‌کند که آیا المان‌ها باید به سطر و یا ستون بعدی منتقل شوند یا خیر؟
<div fxLayoutWrap></div>
- fxLayoutGap فاصله‌ی بین المان‌ها را مشخص می‌کند.
<div fxLayoutGap="10px"></div>
- fxLayoutAlign نحوه‌ی چیدمان المان را تعیین می‌کند.
<div fxLayoutAlign="start stretch"></div>

چند مثال:


و یا حالت راست به چپ آن به صورت زیر است:


و برای تعریف آیتم‌های قرار گرفته‌ی درون containers می‌توان از دایرکتیوهای زیر استفاده کرد:
- fxflex برای تعیین اندازه و flex المان‌ها
<div fxFlex="1 2 calc(15em + 20px)"></div>
در اینجا سه مقداری که ذکر می‌شوند (و یا تنها یک مقدار) چنین معنایی را به همراه دارند:
 fxFlex="grow shrink basis"
و یا
 fxFlex="basis"
- grow به این معنا است که آیتم جاری در صورت وجود فضا (طراحی واکنشگرا و واکنش نشان دادن به اندازه‌ی صفحه)، نسبت به سایر المان‌ها تا چه اندازه‌ای می‌تواند بزرگ شود.
- shrink به این معنا است که اگر به اندازه‌ی کافی فضا وجود نداشت، این المان نسبت به سایر المان‌های دیگر تا چه اندازه‌ای می‌تواند کوچک شود.
- basis به معنای اندازه‌ی پیش‌فرض المان است.

در اینجا اندازه‌ها برحسب پیکسل، درصد و یا calcs, em, cw, vh می‌توانند تعیین شوند. همچنین یک سری نام مستعار مانند grow, initial, auto, none, nogrow, noshrink هم قابل استفاده هستند.

- fxflexorder برای تعیین ترتیب قرارگیری یک المان
<div fxFlexOrder="2"></div>
-  fxflexoffset برای تعیین فاصله یک المان از container آن
 <div fxFlexOffset="20px"></div>
-  fxflexAlign برای تعیین محل قرارگیری المان
 <div fxFlexAlign="center"></div>
- fxflexfill برای تعیین اینکه این المان کل ردیف یا ستون را پر خواهد کرد
 <div fxFlexFill></div>

چند مثال:


در اینجا سه نمایشی را که در ذیل تعریف div‌ها مشاهده می‌کنید بر اساس تغییر اندازه‌ی صفحه حاصل شده‌اند. چون آیتم دوم دارای مقدار grow مساوی 5 است، به همین جهت با تغییر اندازه‌ی صفحه و دسترسی به مقدار فضای بیشتر، بزرگ‌تر شده‌است.

یک مثال کامل
اگر علاقمند باشید تا توانمندی‌های angular flex layout را در قالب یک مثال کامل مشاهده کنید، به آدرس زیر مراجعه نمائید:
https://tburleson-layouts-demos.firebaseapp.com/#/docs
در این مثال با تغییر گزینه‌‌های مختلف، کد معادل angular flex layout آن نیز تولید می‌شود.
همچنین wiki خود پروژه نیز به همراه مثال‌های بیشتری است:
https://github.com/angular/flex-layout/wiki



کار با API واکنشگرای Angular Flex layout


در طراحی واکنشگرا، container و عناصر داخل آن بر اساس تغییرات اندازه‌ی صفحه و یا اندازه‌ی وسیله‌ی نمایشی، تغییر اندازه و همچنین موقعیت می‌دهند و این تغییرات بر اساس انطباق با viewport وسیله‌ی نمایشی صورت می‌گیرند. به همین جهت برای طراحی واکنشگرا نیاز به Flex CSS و همچنین Media Query است. نوشتن Medial Query و ترکیب آن با Flex CSS کار مشکلی است. به همین جهت Angular Flex layout به همراه یک API واکنشگرا نیز هست که در پشت صحنه Flex CSS را بر اساس طراحی متریال و Medial Queries مورد استفاده قرار می‌دهد.
اگر علاقمند هستید تا اندازه‌های واکنشگرای استاندارد متریال را ملاحظه کنید، می‌توانید به آدرس زیر مراجعه نمائید (قسمت Breakpoint system آن):
https://material.io/design/layout/responsive-layout-grid.html#breakpoints
برای مثال هر اندازه‌ای کمتر از 600px در گروه extra small قرار می‌گیرد (با مخفف xs). از 600px تا 1024px در بازه‌ی small (با مخفف sm)، از 1024px تا 1440px در بازه‌ی medium (با مخفف md) و از 1440px تا 1920px در بازه‌ی large (با مخفف lg) و بیشتر از آن در بازه‌ی xlrage قرار می‌گیرند (با مخفف xl). این اعداد و بازه‌ها، پایه‌ی طراحی API واکنشگرای Angular Flex layout هستند. به همین جهت نام این بازه‌ها در این API به صورت مخفف xs, sm, md, lg, xl درنظر گرفته شده‌اند و مورد استفاده قرار می‌گیرند. همچنین اگر اندازه‌های مدنظر از این بازه‌ها کمتر باشند، می‌توان از lt-sm, lt-md, lt-lg, lt-xl نیز استفاده کرد. در اینجا lt به معنای less than است و یا اگر بازه‌های مورد نیاز بیش از این اندازه‌ها باشند می‌توان با gt-xs, gt-sm, gt-md, gt-lg کار کرد. در اینجا gt به معنای greater than است.
به این مخفف‌ها «media query alias» هم گفته می‌شود و اکنون که لیست آن‌ها مشخص است، تنها کافی است آن‌ها را به API استاتیکی که پیشتر بررسی کردیم، اضافه کنیم. برای مثال:
fxLayout.sm = "..."
fxLayoutAlign.md = "..."
fxHide.gt-sm = "..."
برای نمونه فرض کنید یک چنین طرحبندی دسکتاپی را داریم:


معادل طراحی آن با API استاتیک Angular Flex Layout به صورت زیر است:

که در اینجا دو container را ملاحظه می‌کنید. ابتدا Container بیرونی جهت ارائه‌ی ستونی از سه المان اضافه شده‌است. سپس یک Container میانی برای  تعریف ردیفی از سه المان تعریف شده‌است. توسط روش "fxFlex="grow shrink basis نیز اندازه‌های آن‌ها مشخص شده‌اند.

اکنون که این طرحبندی دسکتاپ را داریم، چگونه باید آن‌را تبدیل به طرحبندی موبایل، مانند شکل زیر کنیم؟


برای اینکار ابتدا fxLayout.xs را به سطر میانی اضافه می‌کنیم تا هرگاه به این اندازه رسیدیم، بجای ردیف، تبدیل به ستون شود. سپس توسط fxFlexOrder.xs، در اندازه‌ی xs، محل قرارگیری المان‌های این ستون را هم مشخص می‌کنیم:


همانطور که ملاحظه می‌کنید کار کردن با این API بسیار ساده‌است و نیازی به کارکردن مستقیم با Media Queries و یا برنامه نویسی مستقیم ندارد و تمام آن در قالب HTML یک کامپوننت قابل پیاده سازی است.
یک نکته: مثال کاملی که پیشتر در این بحث مطرح شد، به همراه مثال واکنشگرا نیز هست که برای مشاهده‌ی اثر آن‌ها بهتر است اندازه‌ی مرورگر را کوچک و بزرگ کنید.


مخفی کردن و یا نمایش قسمتی از صفحه بر اساس اندازه‌ی آن

علاوه بر media query alias هایی که عنوان شد، امکان نمایش و یا مخفی سازی قسمت‌های مختلف صفحه بر اساس اندازه‌ی صفحه‌ی نمایشی نیز هست:
 <div fxShow fxHide.xs="false" fxHide.lg="true"></div>
در اینجا fxShow سبب نمایش این div در حالت عادی می‌شود (پیش‌فرض آن xl، md و sm است). اما اگر اندازه‌ی صفحه lg باشد، fxHide.lg تنظیم شده‌ی به true سبب مخفی سازی آن خواهد شد و در اندازه‌ی xs مجددا نمایش داده می‌شود.


تغییر اندازه‌ی قسمتی از صفحه بر اساس اندازه‌ی آن

در مثال زیر اگر اندازه‌ی صفحه gt-sm باشد (بیشتر از small)، اندازه‌ی این div به 100 درصد بجای 50 درصد حالت‌های دیگر، تنظیم می‌شود:
 <div fxFlex="50%" fxFlex.gt-sm="100%"></div>

حالت‌های ویژه‌ی طراحی واکنشگرا در Angular Flex Layout

در API واکنشگرای آن حالت‌های ویژه‌ی fxshow, fxhide, ngclass  و  ngstyle نیز درنظر گرفته شده‌اند که امکان فعالسازی آن‌ها در اندازه‌های مختلف صفحه مسیر است:
<div fxShow [fxShow.xs]="isVisibleOnMobile()"></div>
<div fxHide [fxHide.gt-sm]="isVisibleOnDesktop()"></div>
<div [ngClass.sm]="{'fxClass-sm': hasStyle}" ></div>
<div [ngStyle.xs]="{color: 'blue'}"></div>


امکان کار با API واکنشگرا از طریق برنامه نویسی

برای این منظور می‌توان از سرویس ObservableMedia مانند مثال زیر استفاده کرد:


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



برای مطالعه‌ی بیشتر
قسمت‌های عمده‌ای از مطلب جاری، از ویدیوی زیر که توسط نویسنده‌ی اصلی angular flex layout تهیه شده‌است، گردآوری شدند.
 
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت نهم - مسیریابی
یکسری قابلیت به روتر جدید (نگارش 3) و نهایی Angular 2 اضافه شده‌اند، مانند child routes و named routes که از ترکیب این‌ها چنین کاری را می‌توان انجام داد. از named routes برای داشتن چندین و چند router-outlet در صفحه استفاده می‌شود و هدایت محتوایی خاص به قسمتی خاص از صفحه و از child routes برای مخفی کردن روت‌های زیر مجموعه‌ی یک روت خاص. یک مثال
پاسخ به بازخورد‌های پروژه‌ها
حذف نظرات
حق با شماست. فقط مقدار parent را برابر با null قرار میدهد.
مطالب
استفاده از Re-Captcha
در اینجا استفاده از re-CAPTCHA برای ASP.Net و در اینجا برای ASP.Net MVC با استفاده از سرویس گوگل نسخه 1 آن آشنا شدید. در این مقاله می‌خواهیم توضیحاتی را در مورد دلیل استفاده و نحوه‌ی ثبت re-CAPTCHA نسخه 2 برای تکنولوژی‌های ASP.Net و ASP.Net MVC ارائه کنیم.

  reCAPTCHA چیست؟

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



دلیل استفاده از reCAPTCHA:

  1. گزارش روزانه از وضعیت موفقیت آمیز بودن هویت سنجی
  2. سهولت استفاده برای کاربران
  3. سهولت استفاده جهت برنامه نویسان
  4. دسترسی پذیری مناسب بدلیل وجود سؤالات تصویری و تلفظ و پخش عبارت بصورت صوتی
  5. امنیت بالا 

آیا می‌توان قالب reCAPTCHA را تغییر داد؟

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



زبان‌های پشتیبانی شده در این سرویس:


اضافه نمودن reCAPTCHA به سایت:

اگر قبلا در گوگل ثبت نام نموده‌اید کافیست وارد این سایت شوید و بر روی Get reCAPTCHA کلیک نمائید؛ در غیر اینصورت می‌بایستی یک حساب کاربری ایجاد نماید. بعد از ورود، به کنترل پنل هدایت خواهید شد. در نمای اول به تصویر زیر برخورد خواهید کرد که از شما ثبت سایت جدید را خواستار است:



نام، دامنه سایت و مالک را وارد و ثبت نام نماید.

پس از آنکه بر روی دکمه‌ی ثبت نام کلیک نمودید، برای شما دو کلید جدید را ثبت می‌نماید که منحصر به سایت شماست. Site Key رشته ای را داراست که در کد‌های HTML قرار خواهد گرفت و کلید بعدی Secret Key می‌باشد. ارتباط سایت شما با گوگل می‌بایستی به صورت محرمانه محفوظ بماند.


گام‌های لازم جهت نمایش سرویس در سایت:

  1. دستورات سمت کاربر
  2. دستورات سمت سرور 

دستورات سمت کاربر:

کد زیر را در قبل از بسته شدن تک <head/> قرار دهید:

<script src='https://www.google.com/recaptcha/api.js'></script>
کد زیر را در داخل تگ فرمی که می‌خواهید کپچا نمایش داده شود قرار دهید:
<div data-sitekey="6LdHGgwTAAAAAClKFhGthRrjBXh5AUGd4eWNCQq7"></div>

نکته: مقدار data-sitekey برابر است با رشته Site Key که گوگل برای شما ثبت نمود.



دستورات سمت سرور:

وقتی کاربر فرم حاوی کپچا را که به صورت صحیح هویت سنجی آن انجام شده باشد به سمت سرور ارسال کند، به عنوان بخشی از داده‌ی ارسال شده، یک رشته با نام g-recaptcha-response  با دستور Request دریافت خواهید کرد که به منظور بررسی اینکه آیا گوگل تایید کرده است که کاربر، یک درخواست POST ارسال نمود‌است. با این پارامترها یک مقدار json برگشت داده خواهد شد که می‌بایستی کلاسی متناظر با آن جهت خواندن ساخته شود.

با استفاده از کد زیر مقدار برگشتی Json را در کلاس مپ می‌نمائیم:
using System.Collections.Generic;
using Newtonsoft.Json;

namespace BaseConfig.Security.Captcha
{
    public class RepaptchaResponse
    {
        [JsonProperty("success")]
        public bool Success { get; set; }

        [JsonProperty("error-codes")]
        public List<string> ErrorCodes { get; set; }
    }
}

با استفاده از کلاس زیر درخواستی به گوگل ارسال شده و در صورتیکه با خطا مواجه شود با استفاده از دستور switch به آن دسترسی خواهیم یافت.
using System.Configuration;
using System.Net;
using Newtonsoft.Json;

namespace BaseConfig.Security.Captcha
{
    public class ReCaptcha
    {
        public static string _secret;

        static ReCaptcha()
        {
            _secret = ConfigurationManager.AppSettings["ReCaptchaGoogleSecretKey"];
        }

        public static bool IsValid(string response)
        {
            //secret that was generated in key value pair
            var client = new WebClient();
            var reply = client.DownloadString($"https://www.google.com/recaptcha/api/siteverify?secret={_secret}&response={response}");

            var captchaResponse = JsonConvert.DeserializeObject<RepaptchaResponse>(reply);

            // when response is false check for the error message
            if (!captchaResponse.Success)
            {
                //if (captchaResponse.ErrorCodes.Count <= 0) return View();

                //var error = captchaResponse.ErrorCodes[0].ToLower();
                //switch (error)
                //{
                //    case ("missing-input-secret"):
                //        ViewBag.Message = "The secret parameter is missing.";
                //        break;
                //    case ("invalid-input-secret"):
                //        ViewBag.Message = "The secret parameter is invalid or malformed.";
                //        break;

                //    case ("missing-input-response"):
                //        ViewBag.Message = "The response parameter is missing.";
                //        break;
                //    case ("invalid-input-response"):
                //        ViewBag.Message = "The response parameter is invalid or malformed.";
                //        break;

                //    default:
                //        ViewBag.Message = "Error occured. Please try again";
                //        break;
                //}
                return false;
            }
            // Captcha is valid
            return true;
        }
    }
}

تابع IsValid از نوع برگشتی Boolean بوده و خطایی برگشت داده نخواهد شد و از این جهت به صورت کامنت برای شما گذاشته شده که می‌توان متناظر با کد نویسی آن را تغییر دهید.
در اکشن زیر مقدار response برسی می‌شود تا خالی نباشد و همچنین مقدار آن را می‌توان با استفاده از تابع IsValid در کلاس ReCaptcha به سمت گوگل فرستاد.
        //
        // POST: /Account/Login
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public virtual async Task<ActionResult> Login(LoginPageModel model, string returnUrl)
        {
            var response = Request["g-recaptcha-response"];
            if (response != null && ReCaptcha.IsValid(response))
            {
                // 
            }
         }

گاها اتفاق می‌افتد که از دستورات Ajax برای ارسال اطلاعات به سمت سرور استفاده می‌شود که در این صورت لازم است بعد از پایان عملیات، کپچا ریفرش گردد. برای این کار می‌توان از دستور جاوا اسکریپتی زیر استفاده نمود. در صورتیکه صفحه Postback شود، کپچا مجددا ریفرش خواهد شد.

/**
 * 
 * @param {} data 
 * @returns {} 
 */
function Success(data) {
    grecaptcha.reset();
}

تا اینجا موفق شدیم تا فرم ارسالی همراه کپچا را به سمت سرور ارسال کنیم. اما ممکن است در یک صفحه از چند کپچا استفاده شود که در این صورت می‌بایستی دستورات سمت کاربر تغییر نمایند.

برای این کار دستور
<div data-sitekey="6LdHGgwTAAAAAClKFhGthRrjBXh5AUGd4eWNCQq7"></div>  
که در بالا تعریف شد، به شکل زیر تغییر خواهد کرد:

    <script>
        var recaptcha1;
        var recaptcha2;
        var myCallBack = function () {
            //Render the recaptcha1 on the element with ID "recaptcha1"
            recaptcha1 = grecaptcha.render('recaptcha1', {
                'sitekey': '6Lf9FQwTAAAAAE6XlDqrey24K4xJOPM5nNVBmNO9',
                'theme': 'light'
            });

            //Render the recaptcha2 on the element with ID "recaptcha2"
            recaptcha2 = grecaptcha.render('recaptcha2', {
                'sitekey': '6Lf9FQwTAAAAAE6XlDqrey24K4xJOPM5nNVBmNO9',
                'theme': 'light'
            });

            //Render the recaptcha3 on the element with ID "recaptcha3"
            recaptcha2 = grecaptcha.render('recaptcha3', {
                'sitekey': '6Lf9FQwTAAAAAE6XlDqrey24K4xJOPM5nNVBmNO9',
                'theme': 'light'
            });
        };
    </script>

برای نمایش کپچا، تگ‌های div با id متناظر با recaptcha1, recaptcha2, recaptcha3 ( در این مثال از سه کپچا در صفحه استفاده شده است ) در صفحه قرار خواهند گرفت.

<div id="recaptcha1"></div>
<div id="recaptcha2"></div>
<div id="recaptcha3"></div>

کار ما تمام شد. حال اگر پروژه را اجرا نمائید، در صفحه سه کپچا مشاهده خواهید کرد.


چند زبانه کردن کپچا:

برای چند زبانه کردن کافیست با مراجعه به این لینک و یا استفاده از تصویر بالا ( زبان‌های پشتیبانی ) مقدار آن زبان را برابر با پراپرتی hl که به صورت کوئری استرینگ برای گوگل ارسال می‌گردد، استفاده نمود. کد زیر نمونه‌ی استفاده شده برای زبان‌های انگلیسی و فارسی می‌باشد که با ریسورس مقدار دهی می‌شود.
<script src='https://www.google.com/recaptcha/api.js?hl=@(App_GlobalResources.CP.CurrentAbbrivation)'></script>

در صورتی که از فایل ریسوس استفاده نمی‌کنید می‌توان به صورت مستقیم مقدار دهی نمائید:
<script src='https://www.google.com/recaptcha/api.js?hl=fa'></script>



برای دوستانی که با تکنولوژی ASP.Net کار می‌کنند، این روال هم برای آنها هم صادق می‌باشد.

برای دریافت پروژه اینجا کلیک نمائید.
نظرات مطالب
استفاده از چند فرم در کنار هم در ASP.NET MVC

child action قراره فقط محتوای قسمت کوچکی از صفحه رو تامین کنه. در اینجا redirect کل صفحه معنی نداره چون هنوز کار رندر صفحه تموم نشده و وسط کار هست.