سلام؛ میخواستم بدونم بابت منوهای سفارشی برای کاربران با نقشهای خاص، چگونه باید عمل کرد. ضمنا چگونه میتوانند یک کامپوننت مانند منو رو از داخل کامپوننت اصلی refresh کنیم
نظرات مطالب
متغیرهای استاتیک و برنامههای ASP.NET
سپاسگزارم.راهنمایی های شما مخصوصا معرفی این کامپوننت آخری خیلی بدرد بخور بود.احتمالا بهترین پیشنهاد برای پیاده سازی سیستم چت استفاده از همین کامپوننت می باشد.
چندی قبل مطلب «نمایش تاریخ شمسی توسط JavaScript در AngularJS» را در این سایت مطالعه کردید. در اینجا قصد داریم معادل Angular آنرا تهیه کنیم (واژهی AngularJS به نگارشهای 1x اشاره میکند و Angular به تمام نگارشهای پس از 2).
نصب پیشنیازهای کار با moment-jalaali
ابتدا نیاز است بستهی npm این کتابخانه را نصب کنیم که به همراه فایلهای js مرتبط با آن میباشد:
سپس جهت بهبود تجربهی کاربری با آن در IDEهای امروزی، خصوصا VSCode، بهتر است typings آنرا نیز نصب کنیم؛ تا علاوه بر داشتن Intellisense، بتوان به صورت strongly typed با آن کار کرد:
VSCode به صورت خودکار پوشهی مخصوص node_modules\@types را تحت نظر قرار میدهد و نصب بستههای typings در آن، سبب بارگذاری آنی آنها خواهد شد.
به علاوه اگر به فایل tsconfig.json واقع در ریشهی پروژه نیز دقت کنید، وجود تعریف ذیل، امکان خوانده شدن این تعاریف را توسط کامپایلر TypeScript میسر میکند:
نحوهی کار Strongly Typed با کتابخانهی moment-jalaali در برنامههای مبتنی بر TypeScript
پس از نصب پیشنیازهای یاد شده، ابتدا برای دسترسی به امکانات این کتابخانه، ماژول آنرا import میکنیم:
- پس از import ماژولی به نام moment-jalaali، اکنون نحوهی استفادهی از آنرا در متد persianDateTests مشاهده میکنید.
- متد momentJalaali.loadPersian باید تنها یکبار فراخوانی شود. کار آن تبدیل نامهای روزها و ماههای میلادی، به شمسی است.
- پس از آن از طریق متد format آن، میتوان انواع و اقسام حالات مختلف را بررسی کرد که در اینجا سه نمونه را مشاهده میکنید.
نوشتن یک Pipe سفارشی برای تبدیل تاریخهای میلادی دریافتی از سرور به قالب شمسی
پس آشنا شدن با نحوهی استفادهی از این کتابخانه در یک برنامهی تایپاسکریپتی، تبدیل کردن آن به یک Pipe سفارشی بسیار سادهاست. برای این منظور ابتدا یک Pipe جدید را به ماژول فرضی custom-pipe.module اضافه میکنیم:
با این محتوا:
در اینجا نیز ابتدا ماژول moment-jalaali تعریف شدهاست و سپس توسط آن، value به عنوان پارامتر متد momentJalaali و args به عنوان پارامتر متد format ارسال شدهاند. در حین استفادهی از Pipe، مقدار value همان تاریخ دریافتی است و args به فرمت خاصی که توسط استفاده کننده مشخص میشود، تنظیم خواهد شد.
به این ترتیب میتوان یک چنین تبدیلات سمت کاربری را انجام داد که نمونهای از خروجی آنرا در تصویر فوق نیز ملاحظه میکنید:
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
نصب پیشنیازهای کار با moment-jalaali
ابتدا نیاز است بستهی npm این کتابخانه را نصب کنیم که به همراه فایلهای js مرتبط با آن میباشد:
npm install moment-jalaali --save
سپس جهت بهبود تجربهی کاربری با آن در IDEهای امروزی، خصوصا VSCode، بهتر است typings آنرا نیز نصب کنیم؛ تا علاوه بر داشتن Intellisense، بتوان به صورت strongly typed با آن کار کرد:
npm install @types/moment-jalaali --save-dev
VSCode به صورت خودکار پوشهی مخصوص node_modules\@types را تحت نظر قرار میدهد و نصب بستههای typings در آن، سبب بارگذاری آنی آنها خواهد شد.
به علاوه اگر به فایل tsconfig.json واقع در ریشهی پروژه نیز دقت کنید، وجود تعریف ذیل، امکان خوانده شدن این تعاریف را توسط کامپایلر TypeScript میسر میکند:
{ "typeRoots": [ "node_modules/@types" ] }
نحوهی کار Strongly Typed با کتابخانهی moment-jalaali در برنامههای مبتنی بر TypeScript
پس از نصب پیشنیازهای یاد شده، ابتدا برای دسترسی به امکانات این کتابخانه، ماژول آنرا import میکنیم:
import * as momentJalaali from "moment-jalaali"; export class MomentJalaaliTestComponent implements OnInit { now: string; nowLongDateFormat: string; nowExtraLongDateFormat: string; ngOnInit() { this.persianDateTests(); } persianDateTests() { // https://github.com/jalaali/moment-jalaali momentJalaali.loadPersian(/*{ usePersianDigits: true }*/); // نمایش فارسی نام ماهها، روزها و امثال آن this.now = momentJalaali().format("jYYYY/jMM/jDD HH:mm"); this.nowLongDateFormat = momentJalaali().format("jD jMMMM jYYYY [ساعت] LT"); this.nowExtraLongDateFormat = momentJalaali().format( "dddd، jD jMMMM jYYYY [ساعت] LT" ); } }
- متد momentJalaali.loadPersian باید تنها یکبار فراخوانی شود. کار آن تبدیل نامهای روزها و ماههای میلادی، به شمسی است.
- پس از آن از طریق متد format آن، میتوان انواع و اقسام حالات مختلف را بررسی کرد که در اینجا سه نمونه را مشاهده میکنید.
نوشتن یک Pipe سفارشی برای تبدیل تاریخهای میلادی دریافتی از سرور به قالب شمسی
پس آشنا شدن با نحوهی استفادهی از این کتابخانه در یک برنامهی تایپاسکریپتی، تبدیل کردن آن به یک Pipe سفارشی بسیار سادهاست. برای این منظور ابتدا یک Pipe جدید را به ماژول فرضی custom-pipe.module اضافه میکنیم:
ng g p CustomPipe/moment-jalaali -m custom-pipe.module
import { Pipe, PipeTransform } from "@angular/core"; import * as momentJalaali from "moment-jalaali"; @Pipe({ name: "momentJalaali" }) export class MomentJalaaliPipe implements PipeTransform { transform(value: any, args?: any): any { return momentJalaali(value).format(args); } }
به این ترتیب میتوان یک چنین تبدیلات سمت کاربری را انجام داد که نمونهای از خروجی آنرا در تصویر فوق نیز ملاحظه میکنید:
<h2>Server side dates:</h2> <div *ngFor="let date of dates"> <span dir="ltr">{{date | momentJalaali:'jYYYY/jMM/jDD hh:mm' }}</span>, <span dir="rtl">{{date | momentJalaali:'jD jMMMM jYYYY [ساعت] LT'}}</span> </div>
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
این مثال شبیه به مثال بررسی وجود نام کاربر با استفاده از jQuery Ajax است که از ذکر توضیحات مشابه آن، در اینجا خودداری خواهد شد.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TestBrokenImages.aspx.cs"
Inherits="testWebForms87.TestBrokenImages" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>detecting broken images</title>
<script src="jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
function errorReplace(arg) {
//ارسال پیغام خطا
$.ajax({
type: "POST",
url: "TestBrokenImages.aspx/GetErros",
data: "{'image': '" + arg.src + "','page':'" + location.href + "'}",
contentType: "application/json; charset=utf-8",
dataType: "json"
});
//نمایش تصویری دلخواه بجای نمونه مفقود
$(arg).attr('src', 'missing.png');
}
//بررسی وضعیت تک تک تصاویر پس از بارگذاری کامل صفحه
$(document).ready(function() {
$(window).bind('load', function() {
$('img').each(function() {
if (!this.complete || (!$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0))) {
errorReplace(this);
}
});
})
});
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<img src="img1.png" />
<img src="img2.png" />
</div>
</form>
</body>
</html>
using System;
using System.IO;
using System.Web.Services;
namespace testWebForms87
{
public partial class TestBrokenImages : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
[WebMethod]
public static void GetErros(string image,string page)
{
//ارسال ایمیل به مسؤول سایت و یا ذخیره خطاها در دیتابیس
}
}
}
در این مثال زمانیکه صفحه کاملا بارگذاری شد، وضعیت تک تک تصاویر بررسی میشود، اگر تصویر مفقودی وجود داشت (با اکثر مرورگرها سازگار است)، اطلاعات آن به تابع errorReplace ارسال خواهد شد.
در این تابع با استفاده از jQuery Ajax ، اطلاعات تصویر مفقود و صفحه مربوطه به وب متد GetErros ما ارسال میشود. سپس در این متد میتوان یا آرگومانهای دریافتی را به صورت یک ایمیل به مسؤول سایت ارسال نمود و یا آنها را جهت بررسی آتی در یک دیتابیس ذخیره کرد.
بدیهی است بجای قرار دادن وب متد فوق در صفحه جاری، میتوان یک وب سرویس را نیز ایجاد و متد را در آن قرار داد تا نیازی نباشد به ازای هر صفحه سایت یکبار این متد تکرار شود.
اگر موفق به اجرای این مثال نشدید، برای مثال یک break point داخل متد GetErrors قرار دهید و برنامه را در حالت دیباگ در ویژوال استودیو شروع کنید، اگر اتفاق خاصی رخ نداد و به این break point نرسیدید، احتمالا تنظیمات وب کانفیگ شما مناسب نیست. قسمت مربوط به system.web.extensions ، webServices و jsonSerialization باید در وب کانفیگ موجود باشد که VS 2008 این موارد را به صورت خودکار اضافه میکند.
اولین کاری را که میتوان پس از نصب Angular CLI انجام داد، ایجاد یک برنامهی جدید است و نمونهای از آنرا در قسمت قبل بررسی کردیم. در ادامه میخواهیم به پارامترهای بیشتر مرتبط با آن و همچنین نحوهی سفارشی سازی ایجاد برنامههای جدید بپردازیم.
ایجاد برنامههای جدید توسط Angular CLI
دستور خط فرمان ابتدایی ایجاد یک برنامهی جدید توسط Angular CLI به صورت ذیل است
در اینجا ng همان Angular CLI است. new عملی است که قرار است رخ دهد و my-app یک نام دلخواه میباشد.
پس از اجرای این دستور، برنامهی جدید ایجاد شده، در پوشهی جدید my-app قرار میگیرد.
گزینهی دیگر این دستور، استفاده از پرچم dry-run است:
کار این پرچم صرفا گزارش دادن جزئیات عملیات ng new است؛ بدون اینکه فایلی را تولید کند. به این ترتیب میتوان برآوردی را از فایلهای تولید شدهی توسط فرامین ng، پیش از تولید واقعی آنها، مشاهده کرد. برای مثال:
همانطور که مشاهده میکنید، در ابتدای کار پیامی را مبنی بر عدم نوشته شدن این فایلها بر روی فایل سیستم، ارائه دادهاست.
گزینهی دیگر دستور ng new را که در قسمت قبل ملاحظه کردید:
کار پرچم skip-install عدم فراخوانی خودکار دستور npm install است که سبب خواهد شد، برنامه بدون نصب وابستگیهای npm آن، با سرعت هرچه بیشتر، ایجاد شود. از این گزینه میتوان جهت مشاهدهی ساختار فایلهای تولیدی استفاده کرد و در نهایت در این حالت باید دستور npm install را به صورت دستی فراخوانی کرد. پرچم dry-run نیز skip-install را به صورت ضمنی به همراه دارد.
برای مشاهدهی سایر پرچمهای مرتبط با دستور ng new میتوان از پرچم help استفاده کرد:
بررسی فایل angular-cli.json.
فایل angular-cli.json. حاوی تنظیمات Angular CLI است.
در ابتدای این فایل، نام برنامهی جدید را مشاهده میکنید. این نام، همانی است که توسط دستور ng new my-app تعیین گردید.
سپس محل پوشهی source برنامه و همچنین خروجی نهایی آن، مشخص میشوند:
یکی از تنظیمات مهم این فایل، مقدار prefix است:
از این مقدار برای تنظیم مقدار prefix تمام کامپوننتها و دایرکتیوهای تولیدی توسط Angular CLI استفاده میشود. برای مثال اگر به فایل src\app\app.component.ts دقت کنید:
نام selector آن با app- شروع شدهاست که این app، از مقدار prefix فایل angular-cli.json. دریافت شدهاست.
تغییر این مقدار صرفا بر روی کامپوننتهای جدید تولید شدهی توسط Angular CLI تاثیرگذار خواهند بود. اگر میخواهید در ابتدای کار تولید یک برنامه، این مقدار را مشخص کنید، میتوان از پرچم prefix استفاده کرد و در صورت عدم ذکر آن، مقدار پیش فرض app برای آن درنظر گرفته میشود:
عدم ایجاد مخزن Git به همراه ng new
با صدور فرمان ng new، کار ایجاد یک مخزن Git نیز به صورت خودکار انجام خواهد شد. برای نمونه اگر خواستید برنامهای را بدون نصب وابستگیها، بدون ایجاد تستها و بدون ایجاد مخزن git آن تولید کنید، میتوان از دستور ذیل استفاده کرد:
استفادهی از sass بجای css توسط Angular CLI
سیستم Build همراه با Angular CLI مبتنی بر webpack است و به خوبی قابلیت پردازش فایلهای sass را نیز دارا است. اگر خواستید حالت پیش فرض تولید فایلهای css این ابزار را که در فایل angular-cli.json. نمونهای از آن ذکر شدهاست، به همراه فایلهایی مانند app.component.css، به sass تغییر دهید:
تنها کافی است پرچم style را با sass مقدار دهی کرد که حالت پیش فرض آن css است:
با ذکر این پرچم، تغییرات فایل angular-cli.json به صورت ذیل خواهند بود:
و حتی کامپوننت src\app\app.component.ts نیز به همراه شیوهنامهای از نوع sass که در حین ارائه نهایی توسط webpack به صورت خودکار پردازش میشود (بدون نیاز به تنظیمات اضافهتری)، مقدار دهی شدهاست:
و یا حتی اگر علاقمند به استفادهی از less باشید نیز میتوان پرچم style less-- را استفاده نمود.
انجام تنظیمات مسیریابی پیش فرض پروژه جدید توسط Angular CLI
حالت پیش فرض تولید برنامههای جدید Angular CLI به همراه تنظیمات مسیریابی آن نیست. اگر علاقمند هستید تا مبحث مسیریابی را خلاصه کرده و به سرعت تنظیمات ابتدایی مسیریابی را توسط این ابزار تولید کنید، میتوان پرچم routing را نیز در اینجا ذکر کرد:
در این حالت اگر به پوشهی src\app مراجعه کنید، فایل جدید app-routing.module.ts را نیز مشاهده خواهید کرد که AppRoutingModule پیش فرضی در آن تنظیم شدهاست و آمادهی استفاده میباشد.
همچنین فایل app.module.ts را نیز اندکی تغییر داده و این AppRoutingModule جدید را نیز به آن معرفی کردهاست.
به این ترتیب ارتباطات ابتدایی مورد نیاز سیستم مسیریابی برقرار شده و قابل استفادهاست. بنابراین ذکر پرچم routing میتواند یکی از پرچمهای اصلی ایجاد برنامههای جدید مبتنی بر Angular CLI باشد.
اجرای ابتدایی یک برنامهی مبتنی بر Angular CLI
پس از انتخاب پرچمهای مناسب جهت ایجاد یک پروژهی جدید مبتنی بر Angular CLI و همچنین نصب وابستگیهای آنها و یا عدم ذکر پرچم skip-install، اکنون نوبت به اجرای این پروژهاست. به همین جهت از طریق خط فرمان به ریشهی پوشهی برنامهی جدید ایجاد شده، وارد شوید. سپس دستور ذیل را صادر کنید:
در اینجا o- به معنای open است؛ یا گشودن آن در یک مرورگر. به این ترتیب کار کامپایل برنامه صورت گرفته و توسط مرورگر پیشفرض سیستم به صورت خودکار باز خواهد شد. آدرس پیش فرض آن نیز به صورت ذیل است:
نکتهی جالب این وب سرور در این است که تغییرات شما را به صورت خودکار دنبال کرده و بلافاصله ارائه میدهد. برای مثال فایل src\app\app.component.html را گشوده و به صورت ذیل تغییر دهید:
پس از ذخیرهی آن، بلافاصله خروجی نهایی را در مرورگر خواهید دید.
تغییر پیش فرضهای عمومی Angular CLI
تا اینجا مشاهده کردیم که اگر بخواهیم مقدار prefix پیش فرض را که به app تنظیم شدهاست به myCompany تغییر دهیم، یا میتوان از پرچم prefix در ابتدای کار فراخوانی دستور ng new استفاده کرد و یا میتوان فایل angular-cli.json. را نیز دستی ویرایش نمود. برای تغییر عمومی و سراسری مقدار پیش فرض app میتوان از دستور ng set استفاده کرد:
دستور اول فایل angular-cli.json. پروژهی جاری را ویرایش میکند و دستور دوم، فایل angular-cli.json سراسری Angular CLI را مقدار دهی خواهد کرد (با توجه به سوئیچ g- آن). البته بدیهی است ویرایشگرهای امروزی به خوبی قابلیت ویرایش فایلهای json را ارائه میدهند و شاید نیازی به استفادهی از این دستورات،حداقل برای اعمال تغییرات محلی نباشد.
و یا اگر بخواهید نوع شیوهنامهی مورد استفاده را ویرایش کنید، میتوان از یکی از دو دستور ذیل استفاده کرد (اولی محلی است و دومی عمومی):
اجرای امکانات Linting پروژههای مبتنی بر Angular CLI
برای بررسی کیفیت کدهای نوشته شده، میتوان از امکانات Linting استفاده کرد. برای این منظور تنها کافی است دستور ذیل را در ریشهی پروژه اجرا نمود:
برای مشاهدهی تمام گزینههای آن دستور زیر را صادر کنید:
اگر علاقمند هستید تا خروجی این ابزار، با رنگهای بهتری نمایش داده شوند، میتوان از دستور ذیل استفاده کرد:
به علاوه این ابزار قابلیت رفع مشکلات را با اعمال تغییراتی در کدهای موجود نیز به همراه دارد:
برای این منظور میتوان از پرچم fix آن استفاده کرد.
یک مثال: فایل src\app\app.component.ts را باز کنید و به عمد تعدادی مشکل را در آن ایجاد نمائید. برای نمونه دو سطر ابتدایی آنرا به صورت ذیل تغییر دهید:
اکنون اگر ng lint را اجرا کنیم، به خروجی ذیل خواهیم رسید:
عنوان میکند که بهتر است number به صورت یک const تعریف شود و همچنین یک سمیکالن در سطر اول فراموش شدهاست.
اکنون اگر دستور ng lint --fix را فراخوانی کنیم، تغییرات ذیل به فایل src\app\app.component.ts اعمال خواهند شد:
به صورت خودکار، به سطر اول، یک سمیکالن را اضافه کرده و همچنین سطر دوم را نیز به const تبدیل کردهاست.
ایجاد برنامههای جدید توسط Angular CLI
دستور خط فرمان ابتدایی ایجاد یک برنامهی جدید توسط Angular CLI به صورت ذیل است
> ng new my-app
پس از اجرای این دستور، برنامهی جدید ایجاد شده، در پوشهی جدید my-app قرار میگیرد.
گزینهی دیگر این دستور، استفاده از پرچم dry-run است:
> ng new my-app --dry-run
>ng new my-app --dry-run installing ng You specified the dry-run flag, so no changes will be written. create .editorconfig create README.md create src\app\app.component.css create src\app\app.component.html . . . Project 'my-app' successfully created.
گزینهی دیگر دستور ng new را که در قسمت قبل ملاحظه کردید:
> ng new my-app --skip-install
برای مشاهدهی سایر پرچمهای مرتبط با دستور ng new میتوان از پرچم help استفاده کرد:
> ng new --help
بررسی فایل angular-cli.json.
فایل angular-cli.json. حاوی تنظیمات Angular CLI است.
در ابتدای این فایل، نام برنامهی جدید را مشاهده میکنید. این نام، همانی است که توسط دستور ng new my-app تعیین گردید.
"project": { "name": "my-app" },
"apps": [ { "root": "src", "outDir": "dist",
یکی از تنظیمات مهم این فایل، مقدار prefix است:
"prefix": "app",
@Component({ selector: 'app-root',
تغییر این مقدار صرفا بر روی کامپوننتهای جدید تولید شدهی توسط Angular CLI تاثیرگذار خواهند بود. اگر میخواهید در ابتدای کار تولید یک برنامه، این مقدار را مشخص کنید، میتوان از پرچم prefix استفاده کرد و در صورت عدم ذکر آن، مقدار پیش فرض app برای آن درنظر گرفته میشود:
> ng new my-project --skip-install --prefix myCompany
عدم ایجاد مخزن Git به همراه ng new
با صدور فرمان ng new، کار ایجاد یک مخزن Git نیز به صورت خودکار انجام خواهد شد. برای نمونه اگر خواستید برنامهای را بدون نصب وابستگیها، بدون ایجاد تستها و بدون ایجاد مخزن git آن تولید کنید، میتوان از دستور ذیل استفاده کرد:
> ng new my-project --skip-install --skip-git --skip-tests --skip-commit
استفادهی از sass بجای css توسط Angular CLI
سیستم Build همراه با Angular CLI مبتنی بر webpack است و به خوبی قابلیت پردازش فایلهای sass را نیز دارا است. اگر خواستید حالت پیش فرض تولید فایلهای css این ابزار را که در فایل angular-cli.json. نمونهای از آن ذکر شدهاست، به همراه فایلهایی مانند app.component.css، به sass تغییر دهید:
"styles": [ "styles.css" ], "defaults": { "styleExt": "css", "component": {} }
> ng new my-project --skip-install --style sass
"styles": [ "styles.sass" ], "defaults": { "styleExt": "sass", "component": {} }
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.sass'] })
انجام تنظیمات مسیریابی پیش فرض پروژه جدید توسط Angular CLI
حالت پیش فرض تولید برنامههای جدید Angular CLI به همراه تنظیمات مسیریابی آن نیست. اگر علاقمند هستید تا مبحث مسیریابی را خلاصه کرده و به سرعت تنظیمات ابتدایی مسیریابی را توسط این ابزار تولید کنید، میتوان پرچم routing را نیز در اینجا ذکر کرد:
> ng new my-project --skip-install --routing
const routes: Routes = [ { path: '', children: [] } ];
imports: [ BrowserModule, FormsModule, HttpModule, AppRoutingModule ],
اجرای ابتدایی یک برنامهی مبتنی بر Angular CLI
پس از انتخاب پرچمهای مناسب جهت ایجاد یک پروژهی جدید مبتنی بر Angular CLI و همچنین نصب وابستگیهای آنها و یا عدم ذکر پرچم skip-install، اکنون نوبت به اجرای این پروژهاست. به همین جهت از طریق خط فرمان به ریشهی پوشهی برنامهی جدید ایجاد شده، وارد شوید. سپس دستور ذیل را صادر کنید:
>ng serve -o
http://localhost:4200/
نکتهی جالب این وب سرور در این است که تغییرات شما را به صورت خودکار دنبال کرده و بلافاصله ارائه میدهد. برای مثال فایل src\app\app.component.html را گشوده و به صورت ذیل تغییر دهید:
<h1> Test {{title}} </h1>
تغییر پیش فرضهای عمومی Angular CLI
تا اینجا مشاهده کردیم که اگر بخواهیم مقدار prefix پیش فرض را که به app تنظیم شدهاست به myCompany تغییر دهیم، یا میتوان از پرچم prefix در ابتدای کار فراخوانی دستور ng new استفاده کرد و یا میتوان فایل angular-cli.json. را نیز دستی ویرایش نمود. برای تغییر عمومی و سراسری مقدار پیش فرض app میتوان از دستور ng set استفاده کرد:
>ng set apps.prefix myCompany >ng set apps.prefix myCompany -g
و یا اگر بخواهید نوع شیوهنامهی مورد استفاده را ویرایش کنید، میتوان از یکی از دو دستور ذیل استفاده کرد (اولی محلی است و دومی عمومی):
>ng set defaults.styleExt sass >ng set defaults.styleExt sass -g
اجرای امکانات Linting پروژههای مبتنی بر Angular CLI
برای بررسی کیفیت کدهای نوشته شده، میتوان از امکانات Linting استفاده کرد. برای این منظور تنها کافی است دستور ذیل را در ریشهی پروژه اجرا نمود:
> ng lint
> ng lint --help
> ng lint --format stylish
>ng lint --fix
یک مثال: فایل src\app\app.component.ts را باز کنید و به عمد تعدادی مشکل را در آن ایجاد نمائید. برای نمونه دو سطر ابتدایی آنرا به صورت ذیل تغییر دهید:
import { Component } from '@angular/core' let number = 10;
>ng lint --format stylish /src/app/app.component.ts[3, 5]: Identifier 'number' is never reassigned; use 'const' instead of 'let'. /src/app/app.component.ts[1, 42]: Missing semicolon Lint errors found in the listed files.
اکنون اگر دستور ng lint --fix را فراخوانی کنیم، تغییرات ذیل به فایل src\app\app.component.ts اعمال خواهند شد:
import { Component } from '@angular/core'; const number = 10;
همان طور که قبلا نیز اشاره شد اینجا به صورت خلاصه هدف FluentAPI فراهم آوردن روشی است که بتوان متدها را زنجیر وار فراخوانی کرد و به این ترتیب خوانایی کد نوشته شده را بالا برد.
متد استاتیک Configure همیشه به عنوان شروع کننده fluent و از اون برای ساختن یک وهله جدید از کلاس Attributes استفاده میشه و بقیه متدها هم کار اضافه کردن مقادیر رو مثل یک دیکشنری انجام میدهند.
با استفاده از کلاس tagbuilder تگ input را ساخته و ویژگیهای فرستاده شده به helper رو با اون ادغام میکنیم (با استفاده از MergeAttributes )
اما در این مقاله سعی شده تا کاربرد آن در یک برنامه MVC رو به صورت استفاده در helperها شرح دهیم.
در اینجا مثالی رو شرح میدهیم که در آن کنترل هایی از جنس input به صورت helper ساخته و برای فرستادن ویژگیهای اچ تی ام ال ( HTML Attributes) آن از Fluent Html Helpers بهره میگیریم بدیهی است که از این Fluent میتوان برای helperهای دیگر هم استفاده کرد.
در ابتدا یک نگاه کلی به کد ایجاد شده میاندازیم :
@Html.RenderInput( Attributes.Configure() .AddType("text") .AddId("UserId") .AddName("UserId") .AddCssClass("TxtBoxCssClass") )
که باعث ساخته شدن یک کنترل تکست باکس با مشخصات زیر در صفحه میشود ، همان طور که مشاهده میکنیم تمام ویژگیها برای کنترل ساخته شده اند.
<input class="TxtBoxCssClass" id="UserId" name="UserId" type="text">
در ادامه به سراغ پیاده سازی کلاس Attributes برای این مثال میرویم ، این کلاس باید از کلاس RouteValueDictionary ارث بری داشته باشد، این کلاس یک دیکشنری برای ما آماده میکند تا مقادیرمون رو در آن بریزیم در اینجا برای ویژگیها و مقادیرشون.
public class Attributes : RouteValueDictionary { /// <summary> /// Configures this instance. /// </summary> /// <returns></returns> public static Attributes Configure() { return new Attributes(); } /// <summary> /// Adds the type. /// </summary> /// <param name="value">The value.</param> public Attributes AddType(string value) { this.Add("type", value); return this; } /// <summary> /// Adds the name. /// </summary> /// <param name="value">The value.</param> public Attributes AddName(string value) { this.Add("name", value); return this; } /// <summary> /// Adds the id. /// </summary> /// <param name="value">The value.</param> public Attributes AddId(string value) { this.Add("id", value); return this; } /// <summary> /// Adds the value. /// </summary> /// <param name="value">The value.</param> public Attributes AddValue(string value) { this.Add("value", value); return this; } /// <summary> /// Adds the CSS class. /// </summary> /// <param name="value">The value.</param> public Attributes AddCssClass(string value) { this.Add("class", value); return this; } }
حالا به سراغ پیاده سازی helper extension مون میرویم
public static MvcHtmlString RenderInput(this HtmlHelper htmlHelper, Attributes attributes) { TagBuilder input = new TagBuilder("input"); input.MergeAttributes(attributes); return new MvcHtmlString(input.ToString(TagRenderMode.SelfClosing)); }
اشتراکها
تئوری رنگها برای برنامه نویسها
اشتراکها
کتابخانه angular-colorpicker
a color picker based on AngularJS Demo
نظرات مطالب
Lazy Loading در AngularJS
سلام.
ng-hello-directive در فایل app/helloDirective.js به این صورت تعریف شده است:
و در نهایت حالت sate3 را با آدرس state3/ در app.js تعریف کنید:
همانطور که میبینید یک ماژول جدید تعریف شده و دایرکتیو در آن ثبت شده است. برای استفاده از چنین دایرکتیوی باید ماژول ِ دایرکتیو را به وابستگیهای ماژول خودتان اضافه کنید:
در این حالت حتما باید فایل دایرکتیو را پیش از فایل app خود بارگذاری کرده باشید. یا اینکه تعریف دایرکتیو را تغییر دهید و بجای تعریف ماژول جدید، آن را به همان ماژول خودتان اضافه کنید. یعنی تعریف دایرکتیو را به این شکل تغییر دهید:
حالا این دایرکتیو را هم میتوانید تنبلانه! بارگذاری کنید.
یک لینک به index.html اضافه کنید:
<div style="direction: rtl"> <a href="#/state1">حالت 1</a> | <a href="#/state2">حالت 2</a> | <a href="#/state3">حالت 3</a> <div ui-view style="font-weight:bold; text-align:center;"></div> </div>
فرض کنید محتویات مورد نظر برای این حالت که در فایل app/state3.html قرار دارد، شامل یک دایرکتیو است:
state3.html:
تگ زیر یک دایرکتیو دارد: <br/> <div ng-hello-directive></div>
angular.module('app').lazy.directive('ngHelloDirective', function () { return function (scope, elem, attr) { elem.html('سلام دایرکتیو تنبل!'); }; });
.state('state3', { url: '/state3', templateUrl: 'app/state3.html', resolve: { fileDeps: ['$q', '$rootScope', function ($q, $rootScope) { var deferred = $q.defer(); var deps = ['app/helloDirective.js']; $script(deps, function () { $rootScope.$apply(function () { deferred.resolve(); }); }); return deferred.promise; }] } });
دقت کنید که در این حالت، این دایرکتیو تنها در ماژولی با نام app که خصوصیتی به نام lazy به صورت توضیح داده شده دارد ثبت میشود.
اگر تابحال دایرکتیو آمادهای را دریافت کرده باشید، دیدهاید که این دایرکتیوها به این صورت تعریف میشوند:
angular.module('moduleOfDirective', []).directive('ngDirectiveName', ...
app = angular.module("app", ['ui.router', 'moduleOfDirective']);
angular.module('app', []).lazy.directive('ngDirectiveName', ...
مطالب
Html Encoding
.
.
مقدمه
در دنیای وب دو انکدینگ معروف داریم: Url Encoding و Html Encoding. در هر کدام از این انکدینگها یک عملیات کلی صورت میگیرد: تبدیل کاراکترهای غیرمجاز به عبارات معادل مجاز.
Url Encoding همانطور که از نامش پیداست روشی برای کدکردن Url هاست. مثل عبارت کدشده زیر:
Hello%20world%20,%20hi
درواقع کاراکتر مشخصکننده رشتهای که Url Encoding احتمالا در آن اعمال شده است، همان کاراکتر % است. بحث درباره این نوع انکدینگ کمی مفصل است که خود مطلب جداگانهای میطلبد. (اطلاعات بیشتر)
Html Encoding نیز با توجه به نامش برای انکدینگ عبارات HTML استفاده میشود. مثلا عبارت زیر را درنظر بگیرید:
<html>encoding</html>
این عبارت پس از اعمال عملیات Html Encoding به صورت زیر در خواهد آمد:
<html>encoding</html>
میبینید که در اینجا کاراکترهای > و < به صورت عبارات ;lt& و ;gt& در آمدهاند. شرح کاملی درباره این عبارات معادل (که اصطلاحا به آنها character entity میگویند) در اینجا آورده شده است.
در حالت کلی Html Encoding شامل کدکردن 5 کاراکتر زیر است:
.
کاراکتر | عبارت معادل | توضیحات |
> | > | |
< | < | |
" | " | |
' | ' | یا ;apos& به غیر از IE |
& | & |
نکته: در برخی استانداردها (بیشتر برای XML) برای کاراکتر ' از عبارت ;apos& استفاده میشود. این عبارت جایگزین به غیر از IE در بقیه مرورگرها درست کار میکند.
این کاراکترها درواقع از عناصر اصلی تشکیلدهنده ساختار Html هستند، بنابراین وجود آنها درون یک متن میتواند در روند رندر صفحات html اختلال ایجاد کند. بنابراین با استفاده از Html Encoding و تبدیل این کاراکترها به معادلشان (عباراتی که مرورگرها آنها را میشناسند)، میتوان از نمایش درست این کاراکترها مطمئن شد. البته یکی دیگر از دلایل مهم اعمال این انکدینگ، افزایش امنیت و جلوگیری از حملات XSS است.
فرمت این عبارات معادل به صورت ;entity_name& است. به کل این عبارت اصطلاحا Character Entity گفته میشود. این عبارات با کاراکتر & شروع شده و به یک کاراکتر ; ختم میشوند. کلمه میان این دو کاراکتر نیز عبارت جایگزین (یا همان entity name) هر یک از این کاراکترهاست که در لینک بالا به همراه بسیاری دیگر از کاراکترها اشاره شده است (^).
روش دیگری نیز برای کدکردن کاراکترها با فرمت ;entity_number#& وجود دارد. این entity_number درواقع کد کاراکتر مربوطه در جدول کاراکترست جاری مرورگر است. معمولا این کدها منطبق بر جدول ASCII هستند. برای کاراکترهای خارج از جدول اسکی هم از سایر جداول (مثلا یونیکد) استفاده میشود. عملیات انکدینگ برای کاراکترهای با کد 160 تا 255 (براساس استاندارد ISO-8859-1) با این روش انجام میشود (^). اطلاعات بیشتر راجع به این کدها در اینجا آورده شده است.
خوشبختانه در سمت سرور، در داتنت روشهای گوناگون و قابل اطمینانی برای اعمال این انکدینگ وجود دارد. اما متاسفانه در سمت کلاینت چنین امکاناتی اصلا فراهم نیست و برنامه نویسان خود باید دست به کار شوند. ازآنجاکه امروزه قسمتهای بیشتری از اپلیکیشنهای تحت وب در سمت کلاینت پیاده میشوند و کتابخانههای سمت کلاینت روز به روز پرطرفدارتر میشوند وجود نمونههای مشابه از این متدها در سمت کلاینت میتواند بسیار مفید باشد.
بنابراین تمرکز اصلی ادامه این مطلب بیشتر بر نحوه اعمال این انکدینگ در سمت کلاینت با استفاده از زبان جاوا اسکریپت است.
.
.
Html Encoding در داتنت
در داتنت متدهای متعددی برای اعمال Html Encoding وجود دارد. برخی از آنها صرفا برای اسناد HTML طراحی شدهاند و برخی دیگر یک پیادهسازی کلی دارند و بعضی نیز برای فایلهای XML ارائه شدهاند. این متدها عبارتند از:
- متد System.Security.SecurityElement.Escape: این متد بیشتر برای اعمال این انکدینگ در XML بهکار میرود. در این متد 5 کاراکتر اشاره شده در بالا به عبارات معادل انکد میشوند. البته برای کاراکتر ' از عبارت ;apos& استفاده میشود.
- متدهای موجود در System.Net.WebUtility: متدهای HtmlEncode و HtmlDecode موجود در این کلاس عملیات انکدینگ را انجام میدهند. این کلاس از داتنت 4 اضافه شده است.
- متدهای کلاس System.Web.HttpUtility: در این کلاس از متدهای موجود در کلاس System.Web.Util.HttpEncoder استفاده میشود. در پیادهسازی پیشفرض، متدهای این کلاس از متدهای موجود در کلاس WebUtility استفاده میکنند. البته میتوان با فراهم کردن یک Encoder سفارشی و تنظیم آن در فایل کانفیگ (خاصیت encoderType در قسمت HttpRuntime) این رفتار را تغییر داد. دلیل اصلی جابجایی مکان پیادهسازی این متدها از دات نت 4 به بعد نیز به همین دلیل است. (اطلاعات بیشتر ^ و ^).
- متدهای موجود در System.Web.HttpServerUtility: متدهای HtmlEncode و HtmlDecode موجود در این کلاس مستقیما از متدهای موجود در کلاس HttpUtility استفاده میکنند. خاصیت Server موجود در HttpContext یا در کلاس Page از نوع این کلاس است.
- متدهای موجود در کلاس System.Web.Security.AntiXss.AntiXssEncoder: این کلاس از دات نت 4.5 اضافه شده است. همانطور که از نام این کلاس بر میآید، از HttpEncoder مشتق شده است که در متدهای مرتبط با html encoding تغییراتی در آن اعمال شده است. متدهای این کلاس برای امنیت بیشتر به جای استفاده از Black List از یک White List استفاده میکنند.
درحال حاضر بهترین گزینه موجود برای عملیات انکدینگ، متدهای موجود در کلاس WebUtility هستند. ازآنجاکه این کلاس در فضای System.Net و در کتابخانه System.dll قرار دارد (کتابخانهای که معمولا برای تمام برنامههای داتنتی نیاز است)، بنابراین بارگذاری آن در برنامه نیز بار اضافی بر حافظه تحمیل نمیکند.
پیادهسازی عملیات HtmlEncode کار سختی نیست. مثلا میتوان برای سادگی از متد Replace استفاده کرد. اما برای رشتههای طولانی این متد کارایی مناسبی ندارد. به همین دلیل در تمام پیادهسازیها، معمولا از یک حلقه بر روی تمام کاراکترهای رشته موردنظر برای یافتن کاراکترهای غیرمجاز استفاده میشود. در کدهای متدهای موجود، برای افزایش سرعت حتی از اشارهگر و کدهای unsafe نیز استفاده شده است.
برای افزایش کارایی در تولید رشته نهایی تبدیلشده، بهتر است از یک StringBuilder استفاده شود. در پیادهسازیهای متدهای بالا برای اینکار معمولا از یک TextWriter استفاده میشود. TextWriterهای موجود از کلاس StrigBuilder برای دستکاری رشتهها استفاده میکنند.
صرفا جهت آشنایی بیشتر، پیادهسازی خلاصهشده متد HtmlEncode در کلاس WebUtility در زیر آورده شده است:
public static unsafe void HtmlEncode(string value, TextWriter output) { int index = IndexOfHtmlEncodingChars(value, 0); if (index == -1) { output.Write(value); return; } int cch = value.Length - index; fixed (char* str = value) { char* pch = str; while (index-- > 0) { output.Write(*pch++); } while (cch-- > 0) { char ch = *pch++; if (ch <= '>') { switch (ch) { case '<': output.Write("<"); break; case '>': output.Write(">"); break; case '"': output.Write("""); break; case '\'': output.Write("'"); break; case '&': output.Write("&"); break; default: output.Write(ch); break; } } else if (ch >= 160 && ch < 256) { // The seemingly arbitrary 160 comes from RFC output.Write("&#"); output.Write(((int)ch).ToString(NumberFormatInfo.InvariantInfo)); output.Write(';'); } else { output.Write(ch); } } } } private static unsafe int IndexOfHtmlEncodingChars(string s, int startPos) { int cch = s.Length - startPos; fixed (char* str = s) { for (char* pch = &str[startPos]; cch > 0; pch++, cch--) { char ch = *pch; if (ch <= '>') { switch (ch) { case '<': case '>': case '"': case '\'': case '&': return s.Length - cch; } } else if (ch >= 160 && ch < 256) { return s.Length - cch; } } } return -1; }
در ابتدا بررسی میشود که آیا اصلا متن ورودی حاوی کاراکترهای غیرمجاز است یا خیر. درصورت عدم وجود چنین کاراکترهایی، کار متد با برگشت خود متن ورودی پایان مییابد. درغیراینصورت عملیات انکدینگ آغاز میشود.
همانطور که میبینید عملیات انکدینگ برای 5 کاراکتر اشاره شده به صورت جداگانه انجام میشود و برای کاراکترهای با کد 160 تا 255 (با توجه به توضیحات موجود در مقدمه) نیز با استاندارد ;code#& عملیات تبدیل انجام میشود.در سمت دیگر، پیادهسازی بهینه متد HtmlDecode چندان ساده نیست. چون به جای یافتن یک کاراکتر غیرمجاز باید به دنبال عبارات چند کاراکتری معادل گشت که کاری نسبتا پیچیده است.
اطلاعات و پیادهسازی نسبتا کاملی درباره Html Encoding در سمت سرور در اینجا قابل مشاهده است.
نکته: درصورت نیاز به کدکردن سایر کاراکترها (مثلا کاراکترهای یونیکد) پیادهسازیهای موجود کارا نخواهند بود. بنابراین باید encoder سفارشی خود را تهیه کنید. مثلا میتوانید شرط دوم در بررسی کد کاراکترها را بردارید (منظور قسمت ch < 256) که در اینصورت متد شما محدوده وسیعی را پوشش میدهد. اما دقت کنید که با این تغییر متدی سفارشی برای عملیات decode نیز باید تهیه کنید!
.
.
Html Encoding در جاوا اسکریپت
برای انجام عملیات Url Encoding در جاوا اسکریپت چند متد توکار وجود دارد، که فرایند کلی عملیات همه آنها تقریبا یکسان است. اما متاسفانه برای انجام عملیات Html Encoding متدی در جاوا اسکریپت وجود ندارد. بنابراین متدهای مربوطه باید توسط خود برنامهنویسان پیادهسازی شوند.
یک روش برای اینکار استفاده از لیست اشارهشده در بالا و انجام عملیات replace برای تمام این کاراکترهاست (5 کاراکتر اصلی و درصورت نیاز سایر کاراکترها). این کار میتواند کمی سخت باشد و درواقع پیادهسازی چنین متدی نسبتا مشکل نیز هست (مخصوصا عملیات decode).
اما خوشبختانه امکانی در اسناد html وجود دارد که این کار (مخصوصا Decode کردن) را آسان میکند.
این روش جالب برای انجام عملیات Html Encoding در جاوا اسکریپت، استفاده از یک قابلیت توکار در مرورگرهاست. عناصر DOM (مانند div) دو خاصیت innerText و innerHTML دارند که مرورگرها با توجه به مقادیر تنظیمشده برای هر یک، عملیات coding و decoding مربوطه را به صورت کاملا خودکار انجام داده و مقدار خاصیت دیگر را بهروزرسانی میکنند (دقت کنید که در این دو پراپرتی، کلمه HTML کاملا با حروف بزرگ است، برخلاف Text که تنها حرف اول آن بزرگ است).
برای روشنتر شدن موضوع به مثال زیر برای عملیات encode توجه کنید:
<div id="log"></div> <script type="text/javascript"> var element = document.getElementById('log'); element.innerText = '<html> encoding </html>'; console.log(element.innerHTML); </script>
<html> encoding </html>
عکس این عملیات یعنی decoding نیز با استفاده از کدی مثل زیر امکانپذیر است:
<div id="log"> </div> <script type="text/javascript"> var element = document.getElementById('log'); element.innerHTML = "<html> encoding </html>"; console.log(element.innerText); </script>
<html> encoding </html>
..
.
متد htmlEncode
برای پیادهسازی این متد برای حالت استفاده مستقیم داریم:
String.htmlEncode = function (s) { var el = document.createElement("div"); el.innerText = s || ''; return el.innerHTML; };
در اینجا با استفاده از متد createElement شی document یک المان DOM (در اینجا div) ایجاد شده و سپس با توجه به توضیحات بالا خاصیت innerText آن به مقدار ورودی تنظیم میشود. استفاده از عبارت '' || s در اینجا برای جلوگیری از برگشت عبارات ناخواسته (مثل undefined یا null) برای ورودیهای غیرمجاز است. درنهایت خاصیت innerHTML این المان به عنوان رشته انکدشده برگشت داده میشود.
console.log(String.htmlEncode("<html>")); //result: <html>
و برای حالت استفاده از خاصیت prototype داریم:
String.prototype.htmlEncode = function () { var el = document.createElement("div"); el.innerText = this.toString(); return el.innerHTML; };
console.log("<html>".htmlEncode()); //result: <html>
.
.
متد htmlDecode
با استفاده از مطالب اشارهشده در بالا، پیادهسازی این متد به صورت زیر است:
String.htmlDecode = function (s) { var el = document.createElement("div"); el.innerHTML = s || ''; return el.innerText; };
String.prototype.htmlDecode = function () { var el = document.createElement("div"); el.innerHTML = this.toString(); return el.innerText; };
نحوه استفاده از این متدها هم به صورت زیر است:
console.log(String.htmlDecode("<html>")); console.log("<html>".htmlDecode());
.
.
پیادهسازی با استفاده از jQuery
درصورت در دسترس بودن کتابخانه jQuery، کار پیادهسازی این متدها بسیار سادهتر خواهد شد. برای اینکار میتوان از متدهای زیر استفاده کرد:
.
- متد htmlEncode:
String.htmlEncode = function (s) { return $('<div/>').text(value).html(); }; String.prototype.htmlEncode = function () { return $('<div/>').text(this.toString()).html(); };
- متد htmlDecode:
String.htmlDecode = function (s) { return $('<div/>').html(s).text(); }; String.prototype.htmlDecode = function () { return $('<div/>').html(this.toString()).text(); };
.
.
نکات پایانی
1. با اینکه به نظر میرسد در متدهای ارائه شده در بالا، بین نسخههای معمولی و نسخه مخصوص jQuery تفاوتی وجود ندارد اما تست زیر نشان میدهد که نکات ریزی باعث بهوجود آمدن برخی تفاوتها میشود. رشته زیر را درنظر بگیرید:
var value = "a \n b";
با استفاده از متد htmlEncode معمولی نشان داده شده در بالا، عبارت انکدشده رشته فوق به صورت زیر خواهد بود:
"a <br> b"
میبینید که به صورت هوشمندانهای! مقدار n\ به تگ <br> انکد شده است. اما اگر با استفاده از متد نوشته شده با jQuery سعی به انکدکردن این رشته کنیم، میبینیم که مقدار n\ بدین صورت انکد نمیشود! حال کدام روش درست و استاندارد است؟
در ابتدای این مطلب هم اشاره شده بود که Html Encoding برای کدکردن یکسری کاراکتر غیرمجاز در متون موجود در صفحات HTML بکار میرود و معمولا همان 5 کاراکتر اشارهشده در بالا به عنوان کاراکترهای اصلی غیرمجاز به حساب میآیند. کاراکتر n\ از این نوع کاراکترها محسوب نمیشود. همچنین ازآنجاکه عملیات عکس این تبدیل در Decode مربوطه صورت نمیگیرد، تبدیل این کاراکتر به معادلش در html اصلا کاری منطقی نیست و باعث خراب شدن متن موردنظر میشود.
با استفاده از متدهای HtmlEncode موجود در کلاسهای دات نت (WebUtility و HtmlUtility که در بالا به آنها اشاره شده بود) عملیات انکدینگ برای این رشته تکرار شد و نتیجه حاصله نشان داد که عبارت n\ در خروجی این متدها نیز انکد نمیشود. بنابراین متد نوشته شده با استفاده از jQuery خروجیهای استانداردتری ارائه میدهد.
با کمی تحقیق و بررسی کدهای jQuery مشخص شد که دلیل این تفاوت، در استفاده از متد createTextNode از شی document در متد ()text است. بنابراین برای بهبود متد htmlEncode اولیه داریم:
String.htmlEncode = function (s) { var el = document.createElement("div"); var txt = document.createTextNode(s); el.appendChild(txt); return el.innerHTML; };
.
.
2. نکته مهم دیگری که باید بدان توجه داشت برقراری اصل مهم زیر در عملیات انکدینگ است:
String.htmlDecode(String.htmlEncode(myString)) === myString;
var myString = "<HTML>"; String.htmlDecode(String.htmlEncode(myString)) === myString; // result: true // -------------------------------------------------------------------------- myString = "<اچ تی ام ال>"; String.htmlDecode(String.htmlEncode(myString)) === myString; // result: true
myString = "a \r b"; String.htmlDecode(String.htmlEncode(myString)) === myString; // result: false
پس از کمی تحقیق و بررسی بیشتر مشخص شد که مرورگرها در تبدیل کاراکترها، کاراکتر carriage return (یا CR یا همان r\ با کد اسکی 13 یا 0D) را تبدیل به کاراکتر line feed (یا LF یا n\ با کد اسکی 10 یا 0A) میکنند. برای آزمایش این نکته میتوانید از سه خط زیر استفاده کنید:
console.log(escape(String.htmlDecode('\r'))); // result: %0A : it is url encode of character '\n' console.log(escape(String.htmlDecode('\n'))); // result: %0A console.log(escape(String.htmlDecode('\r\n'))); // result: %0A
با بررسی بیشتر مشخص شد که این تبدیل به محض مقداردهی به یکی از خاصیتهای یک عنصر DOM صورت میگیرد. برای مثال کد زیر را در مرورگرهای مختلف امتحان کنید:
var el = document.createElement('div'); el.innerText = '\r'; console.log(escape(el.innerText)); // result: %0A el.innerHTML = '\r'; console.log(escape(el.innerHTML)); // result: %0A console.log(escape('\r')); // result: %0D
با بررسی هایی که من کردم دلیل و یا راهحلی برای این مشکل پیدا نکردم!
بنابراین در استفاده از این متدها باید این نکته را مدنظر قرار داد. ازآنجاکه این مشکل حالتی به خصوص دارد نمیتوان راهحلی کلی برای آن ارائه داد. پس برای موقعیتهای گوناگون با توجه به زوایای روشنشده از این مشکل باید به دنبال راهحل مناسب بود.
البته ممکن است این اشکال درمورد کاراکترهای دیگری هم وجود داشته باشد که من به آن برخورد نکرده باشم (با درنظر گرفتن تفاوت میان مرورگرهای مختلف ممکن است پیچیدهتر هم باشد).
نکته: ازآنجاکه برای رفع این مشکل، پیادهسازی متد htmlDecode به این کاملی، با عدم استفاده از ویژگی پراپرتیهای innerHTML و innerText، کاری نسبتا سخت و پیچیده و طولانی است، بنابراین در بیشتر حالات میتوان از این مشکل صرفنظر کرد! به همین دلیل در اینجا نیز متد دیگری برای رفع این مشکل ارائه نمیشود!
.
.
3. یک مشکل دیگر که این متدها دارند این است که متاسفانه در متد htmlEncode، از 5 کاراکتر معروف بالا، کاراکترهای ' و " در این متدها اصلا تبدیل نمیشوند. همچنین سایر کاراکترهای عنواندار یا کاراکترهای خارج از جدول ASCII (مثلا کاراکترهای با کد 160 تا 255 یا کاراکترهای یونیکد) نیز که معمولا انکد میشوند در این متد تغییری نمیکنند و به همان صورت برگشت داده میشوند.
هرچند متد htmlDecode نشان داده شده در این مطلب، بهدرستی تمامی عبارات معادل (حتی عبارات معادل غیر از 5 کاراکتر نشان داده شده در بالا با هر دو استاندارد ;character-entity& و ;code#&) را تبدیل کرده و کاراکتر درست را برمیگرداند.
برای اصلاح این مشکل میتوان متد htmlEncode را کاملا به صورت دستی و مستقیم نوشت و اعمال انکدینگهای موردنیاز را با استفاده یک حلقه روی تمام کاراکترها متن موردنظر انجام داد. چیزی شبیه به کد زیر:
String.htmlEncode = function (text) { text = text || ''; var encoded = ''; for (var i = 0; i < text.length; i++) { var c = text[i]; switch (c) { case '<': encoded += '<'; break; case '>': encoded += '>'; break; case '&': encoded += '&'; break; case '"': encoded += '"'; break; case "'": encoded += '''; break; default: // the upper limit can be removed to support more chars... var code = c.charCodeAt(); if (code >= 160 & code < 256) encoded += '&#' + code + ';'; else encoded += c; } } return encoded; };
.
.
کتابخانههای موجود
هرچند توضیحات ارائه شده در این مطلب کافی هستند، اما صرفا برای آشنایی با سایر کتابخانههای موجود، روشهای استفادهشده در آنها و نقایص و مزایای آنها این قسمت اضافه شده است.
.
Prototype: این کتابخانه شامل مجموعهای از متدهای کمکی برای راحتی کار در سمت کلاینت است. برای عملیات html encoding دو متد escapeHTML و unescapeHTML دارد که به صورت زیر پیاده شدهاند:
function escapeHTML() { return this.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); } function unescapeHTML() { return this.stripTags().replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); }
همانطور که میبینید در این متدها از replace استفاده شده است که برای متنهای طولانی کندتر از روشهای نشان دادهشده در این مطلب است. همچنین عملیات انکد و دیکد را تنها برای 3 کاراکتر < و > و & انجام میدهد که نقص بزرگی محسوب میشود.
.
jQuery.string: این پلاگین حاوی چند متد برای کار با رشتههاست که یکی از این متدها با نام htmlspecialchars مخصوص عملیات انکدینگ است. در این متد تنها همان 5 کاراکتر اصلی تبدیل میشوند. متاسفانه متدی برای decode در این پلاگین وجود ندارد. پیادهسازی خلاصهشده این کتابخانه تنها برای نمایش نحوه عملکرد متد فوق به صورت زیر است:
var andExp = /&/g, htmlExp = [/(<|>|")/g, /(<|>|')/g, /(<|>|'|")/g], htmlCharMap = { '<': '<', '>': '>', "'": ''', '"': '"' }, htmlReplace = function (all, $1) { return htmlCharMap[$1]; }; $.extend({ // convert special html characters htmlspecialchars: function (string, quot) { return string.replace(andExp, '&').replace(htmlExp[quot || 0], htmlReplace); } });
$.htmlspecialchars("<div>");
string.$: پلاگین دیگری برای jQuery که عملیات مربوط به رشتهها را دربر دارد. در این پلاگین برای عملیات انکدینگ دو متد escapeHTML و unescapeHTML به صورت زیر تعریف شدهاند:
this.escapeHTML = function (s) { this.str = this.s(s) .split('&').join('&') .split('<').join('<') .split('>').join('>'); return this; }; this.unescapeHTML = function (s) { this.str = this.stripTags(this.s(s)).str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); return this; };
همانطور که میبنید در متد encode این پلاگین از یک روش جالب اما به نسبت ناکارآمد در رشتههای طولانی، برای استخراج کاراکترهای غیرمجاز استفاده شده است. در این متدها هم تنها 3 کاراکتر & و < و > انکد و دیکد میشوند.
.
encoder.js: کتابخانه نسبتا کاملی برای عملیات انکدینگ رشتهها در سمت کلاینت. این کتابخانه علاوه بر encode و decode رشتهها متدهایی برای تبدیل html entityها به فرمت عددیشان و برعکس، حذف کاراکترهای یونیکد، بررسی اینکه رشته ورودی شامل کاراکترهای انکد شده است، جلوگیری از انکدینک مجدد یک رشته و ... نیز دارد.
. .
htmlEncode: این متد پیادهسازی کاملی برای اجرای عملیات Html Encode دارد و محدوده وسیعی از کاراکترها را نیز تبدیل میکند. مشاهده عملیات موجود در این متد برای آشنایی با مطالب ظریفتر پیشنهاد میشود.
.
.
منابع برای مطالعه بیشتر