مطالب
HTML5 Web Component - قسمت اول - معرفی و مفاهیم اولیه
Web Components مجموعه‌ای از تکنولوژی‌هایی می‌باشند که امکان ساخت المان‌های سفارشی با قابلیت استفاده‌ی مجدد و به همراه کپسوله‌سازی ساختار، استایل و عاملیت (Functionality) متناظر با المان ایجاد شده را در اختیار شما قرار می‌دهد. 

A Path to Standard Components

در این سری چند قسمتی، ابتدا روش ساخت Web Components را بدون استفاده از ابزار خاصی بررسی کرده و در ادامه با معرفی Stenciljs، چند کامپوننت سفارشی را طراحی خواهیم کرد.

سه تکنولوژی اصلی مورد استفاده برای ساخت Web Components عبارتند از:

  • Custom Elements
  • Shadow DOM
  • HTML Templates


Custom Elements

مجموعه‌ای از API‌های جاوااسکریپتی هستند که امکان تعریف یکسری المان معتبر HTML را با قالب‌، رفتار و نام سفارشی، فراهم می‌کنند. علاوه بر اینکه می‌توان یک المان سفارشی مستقل (Autonomous custom elements) را تعریف کرد، امکان سفارشی‌سازی و گسترش المان‌های موجود (Customized built-in elements) را نیز خواهیم داشت. 
المان‌های سفارشی تعریف شده را مانند کامپوننت‌های Angular و یا React تصور کنید؛ با این تفاوت که هیچ وابستگی به Angular و یا React ندارند. همچنین امکان استفاده از آنها در انوع و اقسام فریمورک‌های فرانت-اند وجود دارد.
شکل ساده‌ی یک Custom Element، متشکل است از کلاسی که کلاس HTMLElement را گسترش می‌دهد و یک نام یکتا که باید حتما دارای یک «-» باشد (kebab-case). برای مثال:
//app.component.js
class AppComponent extends HTMLElement {
  static is = 'app-root'
  connectedCallback(){
    this.innerHTML=`<h1>Hello World!</h1>`
  }
}

customElements.define(AppComponent.is, AppComponent)

//index.html
<app-root></app-root>



در تکه کد بالا، از متد connectedCallback به عنوان یکی از متدهای چرخه‌ی حیات یک المان سفارشی، برای تنظیم innerHTML آن استفاده شده است. سپس با استفاده متد define مربوط به CustomElementRegistry که از طریق window.customeElements قابل دسترسی می‌باشد و با مشخص کردن نام و کلاس مرتبط، المان مورد نظر را ثبت و معرفی کردیم.

برای سفارشی‌سازی یک المان موجود، کار با ارث‌بری از کلاس متناظر با آن المان شروع می‌شود. به عنوان مثال در اینجا قصد داریم با کلیک بر روی لینک‌های موجود در صفحه و قبل از هدایت به آدرس مقصد، یک تأییدیه را از کاربر بگیریم:
//confirm-link.component.js
class ConfirmLinkComponent extends HTMLAnchorElement {
  static is = "confim-link";
  connectedCallback() {
    this.addEventListener("click", e => {
      if (!confirm("Are you sure?")) {
        e.preventDefault();
      }
    });
  }
}
customElements.define(ConfirmLinkComponent.is, ConfirmLinkComponent, {
  extends: "a"
});
<a href="http://google.com" is='confirm-link'>
google.com
</a>
در اینجا در بدنه‌ی متد connectedCallback، مشترک رخداد کلیک المان جاری شده و براساس نتیجه‌ی تأییدیه، تصمیم به ادامه یا لغو رفتار پیش‌فرض آن گرفته‌ایم. برای معرفی این المان جدید، کافی است از طریق آرگومان سوم متد define، مشخص کنیم که قصد گسترش چه المانی را داریم. از این پس اگر لینک‌های موجود را از طریق ویژگی is با "confirm-link" تزئین کرده باشید، شاهد رفتار سفارشی جدید خواهیم بود.

Shadow DOM

جنبه‌ی بسیار مهم Web Components، کپسوله‌سازی می‌باشد که امکان مخفی کردن ساختار، استایل و رفتار متناظر با المان سفارشی را مهیا می‌کند تا با سایر قسمت‌های کد تداخلی نداشته باشد. در این میان Shadow DOM نقش اصلی را برای رسیدن به این سطح از کپسوله‌سازی ایفا خواهد کرد. Shadow DOM به عنوان یک نسخه‌ی کپسوله شده‌ی DOM می‌باشد که امکان کپسوله‌سازی Markup & Styling و همچنین کاهش پیچیدگی استایل‌دهی را مهیا خواهد کرد. می‌توان Shadow DOM را همانند iframe تصور کرد که امکان نشتی استایل‌ها و سلکتورها از Light DOM (همان DOM اصلی) به داخل و از داخل به Light DOM وجود ندارد؛ ولی برخلاف iframe همچنان Shadow Rootهای (المان ریشه Shadow DOM) متناظر، در همان کانتکست DOM اصلی قرار دارند و همچنین در اینجا برای دسترسی به مقادیر المان‌های موجود در Shadow DOM، می‌توان یک API مناسب را در سطح المان سفارشی در نظر گرفت، ولی در مقابل برای همین کار در یک iframe نیاز است تا DOM متناظر با آن را Traverse کنیم که البته به عمد به این صورت طراحی شده است؛ چرا که اهداف دیگری را نیز دنبال می‌کند.

به تصویر زیر توجه نمائید:

برای مشاهده‌ی Shadow DOM متناظر با یکسری المان توکار مانند input.range یا input.date در مرورگر کروم، لازم است به قسمت Developer tools آن مراجعه کرده و سپس با فشردن دکمه‌ی F1 به قسمت تنظیمات آن مراجعه کرده و در قسمت Preferences آن و بخش Elements گزینه "Show user agent shadow DOM" را انتخاب کنید. در اینجا input به عنوان المانی که Shadow DOM به آن متصل شده (attach) نقش Shadow Host را ایفا می‌کند.
برای اتصال یک Shadow DOM به یک المان، به شکل زیر می‌توان عمل کرد:
var div = document.createElement('div');
var shadowRoot = div.attachShadow({mode: 'open'}); //or mode: 'closed'
shadowRoot.innerHTML = '<h1>Hello Shadow DOM</h1>';

متد attachShadow یک Shadow DOM Tree جدید را به div متصل کرده و ارجاعی به shadowRoot آن را برمی‌گرداند. این shadowRoot از طریق خصوصیت host نیز ارجاعی را به میزبان خود دارد. از این پس نیز از طریق خصوصیت div.shadowRoot امکان دسترسی به Shadow DOM ایجاد شده خواهیم داشت. البته به دلیل اینکه در اینجا با حالت 'open' استفاده شده است، این دسترسی به shadowRoot از طریق المان وجود دارد، در غیر این صورت اگر با 'closed' مقداردهی شود، خصوصیت div.shadowRoot مقدار نال خواهد داشت و امکان دسترسی به المان‌های داخلی از طریق جاوااسکریپت ممکن نخواهد بود. 
نکته: 'user-agent' نیز حالتی است که برای المان‌های توکار مانند input.range و ... مورد استفاده قرار می‌گیرد و رفتاری شبیه به حالت 'closed' را دارد.
نکته: امکان اتصال Shadow DOM به المان‌های خاصی از جمله div و یا المان‌های سفارشی که کلاس HTMLElement را گسترش می‌دهند (این اتصال در زمان پیاده‌سازی آنها انجام خواهد شد)، وجود دارد.
در زمان استفاده از متد attachShadow، علاوه بر امکان تعیین حالت آن، امکان تنظیم delegatesFocus برای برطرف کردن موضوعات مرتبط با focus در زمان توسعه‌ی المان‌های سفارشی مورد استفاده قرار می‌گیرد. به این صورت که اگر در قسمتی از المان سفارشی که خاصیت و قابلیت focus را ندارد، کلیک شود، focus به اولین المان با قابلیت focus داخل المان سفارشی خواهد رسید و از این پس امکان استایل‌دهی با استفاده از سلکتور custom-element:focus را خواهیم داشت.

مفهوم دیگری وجود دارد تحت عنوان Shadow Boundary که تعیین کننده‌ی مرز بین Light DOM و Shadow DOM و همان لایه‌ی مهیا کننده‌ی کپسوله‌سازی Styling و Markup می‌باشد. در مطالب آتی خواهیم دید که به چه شکلی رخدادهای سفارشی ما قابلیت عبور از این لایه را خواهند داشت.


HTML Templates 

تا قبل از اینکه المان template معرفی شود، از یکی از روش‌های زیر برای استفاده‌ی مجدد از یک قالب HTML عمل می‌کردیم:
1- استفاده از یک المان خاص با ویژگی hidden یا استایل display:none 
  • استفاده از DOM و آگاه بودن مرورگر از وجود آن، عملیات clone را ساده خواهد کرد.
  • محتوای داخل آن رندر نخواهد شد.
  • اگرچه محتوای آن مخفی می‌باشد، با این حال درخواست‌های مرتبط با تصاویر یا اسکریپت‌ها انجام خواهد شد.
<div id="template" hidden>
  <img src="logo.png">
  <div class="comment"></div>
</div>

2- استفاده از تگ script با یک type سفارشی
<script id="template" type="text/x-template">
  <img src="logo.png">
  <div class="comment"></div>
</script>

<script id="template" type="text/x-kendo-template">
        <ul>
            # for (var i = 0; i < data.length; i++) { #
            <li>#= data[i] #</li>
            # } #
        </ul>
</script>
  • از آنجا که تگ script به صورت پیش‌فرض دارای استایل display:none می‌باشد، محتوای داخل آن رندر نخواهد شد.
  • به دلیل عدم مقداردهی ویژگی type آن با "text/javascript"، مرورگر محتوای آن را به عنوان کد جاوااسکریپت parse نخواهد کرد.
  • استفاده از خصوصیت innerHTML مشکل امنیتی XSS را بدنبال خواهد داشت .
HTML Template ویژگی‌های مثبت هر دو روش قبل را دارد. از طریق کد امکان clone و رندر کردن آن وجود دارد و همچنین کوئری المان‌های داخل آن نیز ممکن نیست:
<template id="template">
  <img src="logo.png">
  <div class="comment"></div>
</template>
و برای فعال‌سازی آن از طریق متد cloneNode متناظر با خصوصیت content به شکل زیر عمل خواهیم کرد:
  var template = document.querySelector("template");
  var clonedNode = template.content.cloneNode(true); //deep:true
  document.body.appendChild(clonedNode);
نکته: امکان تعریف قالب‌های تودرتو نیز وجود دارد که در این صورت به شکل جداگانه‌ای باید عملیات فعال‌سازی هر کدام از آنها انجام گیرد.
مطالب
مراحل ارتقاء پروژه‌های Angular از نسخه‌ی 6.0 به 7.0
مراحل ارتقاء پروژه‌های Angular از نگارش 6 به 7 آن به شرح زیر هستند:
1- به روز رسانی Angular CLI
ابتدا نیاز است نگارش قبلی را حذف و سپس نگارش جدید را نصب کنید:
npm uninstall -g @angular/cli
npm cache verify
# if npm version is < 5 then use `npm cache clean` 
npm install -g @angular/cli@latest
البته Angular 7 پشتیبانی از  Node 10 را اضافه کرده است (بیشتر؛ دانلود Node). بنابراین پیش از اجرای دستورات فوق بهتر است NodeJS خود را نیز به روز کنید:
npm i -g npm

2- به روز رسانی RxJS (اگر در نگارش 6 آن‌را تکمیل نکرده‌اید)
1-تعویض کردن HttpModule با HttpClientModule و سرویس Http با سرویس HttpClient
2-حذف کردن ویژگی‌های منسوخ شده از RxJS 6 با اجرای دستور زیر:
npm install -g rxjs-tslint
rxjs-5-to-6-migrate -p src/tsconfig.app.json
3-حذف rxjs-compat بعد از بروزرسانی RxJS 6
 
3- به روز رسانی TypeScript
Angular 7 از تایپ اسکریپت 3.1 استفاده می‌کند (بیشتر). به همین جهت نیاز است وابستگی‌های سراسری سیستم خود را مانند TypeScript، پس از نصب CLI جدید، به نحو زیر به روز کنید:
npm update -g

4- به روز رسانی وابستگی‌های اصلی پروژه
برای به‌‌روز رسانی به نسخه 7 (core framework و CLI)، دستورات زیر را اجرا کنید:
ng update @angular/cli
ng update @angular/core
ng update rxjs
و اگر از Angular Material نیز استفاده می‌کنید، نیاز به اجرای دستور زیر را نیز خواهید داشت:
ng update @angular/material
اگر در اینجا خطای  peer dependency را مشاهده کردید، از سوئیچ force-- در انتهای دستورات، استفاده کنید.
و یا به صورت خلاصه دستور زیر تمام مراحل فوق را به صورت یکجا انجام می‌دهد:
 ng update --all --force
 
5- به روز رسانی Service worker 

اگر شما از Service worker  مربوط به Angular استفاده می‌کنید، بجای versionedFiles از files استفاده کنید. رفتار همان است و تغییر نکرده‌است.
 
6- به روز رسانی وابستگی‌های ثالث پروژه
برای به روز رسانی سایر وابستگی‌های پروژه‌، می‌توان از بسته‌ی npm-check-updates استفاده کرد:
npm install npm-check-updates -g
ncu -u
npm install
دستور دوم تمام شماره نگارش‌های بسته‌های موجود در فایل package.json را به صورت خودکار به آخرین نگارش آن‌ها روز رسانی می‌کند و دستور سوم این بسته‌های جدید را دریافت و نصب خواهد کرد.  
مطالب
آشنایی با الگوی MVP

پروژه‌های زیادی را می‌توان یافت که اگر سورس کدهای آن‌ها را بررسی کنیم، یک اسپاگتی کد تمام عیار را در آن‌ها می‌توان مشاهده نمود. منطق برنامه، قسمت دسترسی به داده‌ها، کار با رابط کاربر، غیره و غیره همگی درون کدهای یک یا چند فرم خلاصه شده‌اند و آنچنان به هم گره خورده‌اند که هر گونه تغییر یا اعمال درخواست‌های جدید کاربران، سبب از کار افتادن قسمت دیگری از برنامه می‌شود.
همچنین از کدهای حاصل در یک پروژه، در پروژه‌‌های دیگر نیز نمی‌توان استفاده کرد (به دلیل همین در هم تنیده بودن قسمت‌های مختلف). حداقل نتیجه یک پروژه برای برنامه نویس، باید یک یا چند کلاس باشد که بتوان از آن به عنوان ابزار تسریع انجام پروژه‌های دیگر استفاده کرد. اما در یک اسپاگتی کد، باید مدتی طولانی را صرف کرد تا بتوان یک متد را از لابلای قسمت‌های مرتبط و گره خورده با رابط کاربر استخراج و در پروژه‌ای دیگر استفاده نمود. برای نمونه آیا می‌توان این کدها را از یک برنامه ویندوزی استخراج کرد و آن‌ها را در یک برنامه تحت وب استفاده نمود؟

یکی از الگوهایی که شیوه‌ی صحیح این جدا سازی را ترویج می‌کند، الگوی MVP یا Model-View-Presenter می‌باشد. خلاصه‌ی این الگو به صورت زیر است:


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

View :
من می‌دانم که چگونه باید اطلاعاتی را به کاربر به شکلی بصری ارائه داد.
من می‌دانم که چگونه باید اعمالی مانند data binding و امثال آن را انجام داد.
من نمی‌دانم که چگونه باید منطق پردازشی موارد ذکر شده را فراهم آورم.

Presenter :
من می‌دانم که چگونه باید درخواست‌های رسیده کاربر به View را دریافت کرده و آن‌ها را به Model‌ انتقال دهم.
من می‌دانم که چگونه باید اطلاعات را به Model ارسال کرده و سپس نتیجه‌ی پردازش آن‌ها را جهت نمایش در اختیار View قرار دهم.
من نمی‌دانم که چگونه باید اطلاعاتی را ترسیم کرد (مشکل View است نه من) و نمی‌دانم که چگونه باید پردازشی را بر روی اطلاعات انجام دهم. (مشکل Model است و اصلا ربطی به اینجانب ندارد!)


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

الف) پیاده سازی به روش متداول (اسپاگتی کد)

protected void btnGetData_Click(object sender, EventArgs e)
{
lblResult.Text = (Math.PI * double.Parse(txtRadius.Text) * double.Parse(txtRadius.Text)).ToString();
}
بله! کار می‌کنه!
اما این مشکلات را هم دارد:
- منطق برنامه (روش محاسبه مساحت دایره) با رابط کاربر گره خورده.
- کدهای برنامه در پروژه‌ی دیگری قابل استفاده نیست. (شما متد یا کلاسی را این‌جا با قابلیت استفاده مجدد می‌توانید پیدا می‌کنید؟ آیا یکی از اهداف برنامه نویسی شیءگرا تولید کدهایی با قابلیت استفاده مجدد نبود؟)
- چگونه باید برای آن آزمون واحد نوشت؟

ب) بهبود کد و جدا سازی لایه‌ها از یکدیگر

در روش MVP متداول است که به ازای هر یک از اجزاء ابتدا یک interface نوشته شود و سپس این اینترفیس‌ها پیاده سازی گردد.

پیاده سازی منطق برنامه:

1- ایجاد Model :
یک فایل جدید را به نام CModel.cs به پروژه اضافه کرده و کد زیر را به آن خواهیم افزود:

using System;

namespace MVPTest
{
public interface ICircleModel
{
double GetArea(double radius);
}

public class CModel : ICircleModel
{
public double GetArea(double radius)
{
return Math.PI * radius * radius;
}
}
}
همانطور که ملاحظه می‌کنید اکنون منطق برنامه از موارد زیر اطلاعی ندارد:
- خبری از textbox و برچسب و غیره نیست. اصلا نمی‌داند که رابط کاربری وجود دارد یا نه.
- خبری از رخ‌دادهای برنامه و پاسخ دادن به آن‌ها نیست.
- از این کد می‌توان مستقیما و بدون هیچ تغییری در برنامه‌های دیگر هم استفاده کرد.
- اگر باگی در این قسمت وجود دارد، تنها این کلاس است که باید تغییر کند و بلافاصله کل برنامه از این بهبود حاصل شده می‌تواند بدون هیچگونه تغییری و یا به هم ریختگی استفاده کند.
- نوشتن آزمون واحد برای این کلاس که هیچگونه وابستگی به UI ندارد ساده است.


2- ایجاد View :
فایل دیگری را به نام CView.cs را به همراه اینترفیس زیر به پروژه اضافه می‌کنیم:

namespace MVPTest
{
public interface IView
{
string RadiusText { get; set; }
string ResultText { get; set; }
}
}

کار View دریافت ابتدایی مقادیر از کاربر توسط RadiusText و نمایش نهایی نتیجه توسط ResultText است البته با یک اما.
View نمی‌داند که چگونه باید این پردازش صورت گیرد. حتی نمی‌داند که چگونه باید این مقادیر را به Model جهت پردازش برساند یا چگونه آن‌ها را دریافت کند (به همین جهت از اینترفیس برای تعریف آن استفاده شده).

3- ایجاد Presenter :
در ادامه فایل جدیدی را به نام CPresenter.cs‌ با محتویات زیر به پروژه خواهیم افزود:

namespace MVPTest
{
public class CPresenter
{
IView _view;

public CPresenter(IView view)
{
_view = view;
}

public void CalculateCircleArea()
{
CModel model = new CModel();
_view.ResultText = model.GetArea(double.Parse(_view.RadiusText)).ToString();
}
}
}

کار این کلاس برقراری ارتباط با Model است.
می‌داند که چگونه اطلاعات را به Model ارسال کند (از طریق _view.RadiusText) و می‌داند که چگونه نتیجه‌ی پردازش را در اختیار View قرار دهد. (با انتساب آن به _view.ResultText)
نمی‌داند که چگونه باید این پردازش صورت گیرد (کار مدل است نه او). نمی‌داند که نتیجه‌ی نهایی را چگونه نمایش دهد (کار View است نه او).
روش معرفی View به این کلاس به constructor dependency injection معروف است.

اکنون کد وب فرم ما که در قسمت (الف) معرفی شده به صورت زیر تغییر می‌کند:

using System;

namespace MVPTest
{
public partial class _Default : System.Web.UI.Page, IView
{
protected void Page_Load(object sender, EventArgs e)
{
}

public string RadiusText
{
get { return txtRadius.Text; }
set { txtRadius.Text = value; }
}
public string ResultText
{
get { return lblResult.Text; }
set { lblResult.Text = value; }
}

protected void btnGetData_Click(object sender, EventArgs e)
{
CPresenter presenter = new CPresenter(this);
presenter.CalculateCircleArea();
}
}
}

در این‌جا یک وهله از Presenter برای برقراری ارتباط با Model ایجاد می‌شود. همچنین کلاس وب فرم ما اینترفیس View را نیز پیاده سازی خواهد کرد.

نظرات اشتراک‌ها
کجا می توانیم از Task.WhenAll استفاده کنیم و چگونه؟
در کل یک نسخه واحد برای همه مسائل وجود ندارد، بستگی به شرایط و منابع در دسترس می‌توان تصمیم درست گرفت. Task.WhenAll نیز از این قاعده مستثنا نیست.
مرسی که به این موضوع اشاره کردین.
اشتراک‌ها
ایجاد دکمه های دلخواه Bootstrap ای

اگر می‌خواهید درگیر مسائل کدنویسی برای ایجاد یک دکمه که باید شکلی خاص داشته باشه نشوید، لینک بالا کار را برای شما راحت کرده است.

ایجاد دکمه های دلخواه Bootstrap ای
نظرات مطالب
ASP.NET Web API - قسمت سوم

سلام آقای راد خیلی خوب و سلیس توضیح میدید

در رابطه با مسائل امنیتی در این روش خیلی خوب می‌شه اگر توضیحی ارائه بدید.

متشکرم

نظرات مطالب
ASP.NET Web API - قسمت سوم
با تشکر از شما آقای راد اگر این زحمت رو بکشین ممنون میشم .دونستن مسائل امنیتی باعث استفاده بهتر از مواردی که شما فرمودین میشه. موفق باشید