مطالب
راهنمای رایگان 60 صفحه‌ای نصب SharePoint 2007 64-bit

راهنمای رایگان زیر نحوه نصب ویندوز سرور 2003 تا اس کیوال سرور 2005 ، تنظیمات IIS و نصب SharePoint و ابزارهای لازم برای برنامه نویسی آن‌را در یک ماشین مجازی (این‌بار در VMware) به صورت مصور و قدم به قدم توضیح داده است.

فهرست مطالب آن:

- Introduction
- Enabling 64-bit Guest OS Support on your Motherboard (if required)
- Creating the Virtual Machine
- Editing the Virtual Machine Settings
- Building Windows
- Configuring Windows
- Patch and Backup
- Domain Promotion and Configuration
- Moss Domain Accounts Creation
- Pop 3 Email Service Configuration
- Moss 2007 Base Installation
- SQL Server 2005 64-bit Enterprise Installation and Configuration
- Create MossSetup login
- Patch and Backup
- Office Enterprise 2007 Installation and Configuration
- SharePoint Designer 2007 Installation
- Visual Studio 2005 Team Developer Edition Installation
- Development Tools Installation
- Force IIS to use 64 bit mode and disable 32 bit mode
- Complete Moss 2007 Installation
- MOSS 2007 Basic Configuration Guide
- Check Search Services and Event Log
- Backup

Download


به گفته مایکروسافت، ویندوز سرور 2008 آخرین سروری است که هم 32 بیتی ارائه شده و هم 64 بیتی.
یا برای مثال این روند از اکسچنج سرور 2007 شروع شد (میل سرور مایکروسافت). نسخه سازمانی این محصول فقط 64 بیتی است.

مطالب
تاریخ شمسی برای blogger !

تاریخ میلادی بلاگر واقعا روی اعصاب بود! این مشکل با استفاده از jQuery به صورت زیر قابل حل است.

تاریخ انگلیسی بلاگر به صورت زیر است:
البته در قسمت تنظیمات تاریخ بلاگ ، فرمت را به این صورت انتخاب کردم تا بدون مشکل تبدیل شود.
<h2 class='date-header'>2008/12/17</h2>

یعنی ما باید متن هرچی heading شروع شده با h2 و دارای کلاس date-header را پیدا کنیم و بعد معادل فارسی آن‌را جایگزین کنیم.
این‌کار را با استفاده از jQuery به صورت زیر می‌توان انجام داد:
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js' type='text/javascript'></script>
<script src='http://vahid.nasiri.googlepages.com/farsidate.js' type='text/javascript'></script>
<script type='text/javascript'>
$(document).ready(function() {
$("h2.date-header").each(function() {
var obj = $(this);
obj.html(ToPersianDate(new Date(obj.text())));
});

$("a[title='comment permalink']").each(function(){
var obj = $(this);
obj.html(ToPersianDateLong(obj.text()));
});

$("a.post-count-link").each(function() {
var obj = $(this);
obj.html(getBloggerPMonthNames(obj.text()));
});

$("a.post-count-link").each(function() {
var obj = $(this);
obj.html(getBloggerPYear(obj.text()));
});
});
</script>

قسمت ویرایش html چیدمان وبلاگ را باید ویرایش و چند سطر بالا را به آن اضافه کرد (بعد از title صفحه).

پ.ن.
Farsidate.js برای تبدیل تاریخ میلادی به شمسی جاوا اسکریپتی از اینجا قرض گرفته شد.

به روز رسانی،
  • نام ماه و سال سمت راست صفحه هم فارسی شد.
  • تاریخ کامنت‌ها هم فارسی شد.
مسیرراه‌ها
ASP.NET MVC
              مطالب
              آموزش فایرباگ - #1
              فایرباگ نام ابزاری است که همه ما نام آن را شنیده ایم یا با آن کار کرده ایم .
              این ابزار امکانات و قابلیت‌های فراوانی دارد که فراگیری آنها برای یک توسعه گر یا طراح وب سایت ضروری به نظر می‌رسد .
              در سری مقالات آموزش فایرباگ ، با این ابزار محبوب و پرکاربرد بیشتر آشنا می‌شویم .


              نصب و اجرای فایرباگ :
              ابتدا وارد این صفحه شوید و ورژن مناسب مرورگرتان را نصب نمایید .
              پس از نصب و دوباره باز کردن مرورگر ، میتوانید با کلیک کردن بروی آیکون فایرباگ یا فشردن کلید F12 ، فایرباگ را اجرا کنید .


              تب‌ها :
              فایرباگ در حالت پیشفرض دارای 6 تب می‌باشد .
              • Console : در این تب دو بخش وجود دارد :
                در بخش Log هشدار‌ها ، پیغام‌ها ، درخواست‌های XHR و ... نمایش داده می‌شوند .
                بخش دیگر هم که در سمت راست قرار دارد ، مخصوص اجرای کدهای جاوا اسکریپت می‌باشد .



              • HTML : در این تب HTML صفحه را بصورت درختی و بصورت Live مشاهده می‌کنید .
                Live بودن به این معنی است دقیقا چیزی را مشاهده می‌کنید که مرورگر در حال تفسیر هست و نه چیزی که به مرورگر ارسال شده .
                همچنین در این تب می‌توانید محتوای HTML قسمت Body صفحه را ویرایش کنید .


              • CSS : در این تب می‌توانید تعاریف CSS موجود در صفحه را مشاهده/ویرایش/حذف/فعال-غیرفعال نمایید .
                همچنین می‌توانید تمام فایل‌های استایلی که به صفحه اضافه شده اند را مشاهده و تعیین کنید که تعاریف موجود در کدام فایل‌ها در این محیط نمایش داده بشوند .




              • Script : در این تب می‌توانید به خطایابی کدهای جاوا اسکریپت بپردازید . همچنین مانند تب CSS می‌توانید یکی از فایل‌های اضافه شده به صفحه برای نمایش در این بخش ، چند مورد را انتخاب کنید .



              • DOM : در این تب می‌توانید ساختار DOM صفحه جاری را مشاهده نمایید .


              • Net : در این تب می‌توانید درخواست هایی که از صفحه ارسال می‌شوند را زیر نظر بگیرید .
                این تب اطلاعات کاملی درباره‌ی هر درخواست ارائه می‌کند .
                از جمله : نوع درخواست ( Post/Get ) ، حجم درخواست و پاسخ ، مدت زمان درخواست ، تعداد درخواست‌های ارسال شده ، وضعیت درخواست‌ها و ...



              در قسمت‌های بعدی هر تب را به صورت جداگانه بررسی می‌کنیم .
              مطالب
              ماژول‌ها در ES 6
              ماژول‌ها در ES 6

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


              روش IIFE Module

              الگوی ماژول‌ها، سال‌ها است که در جاوا اسکریپت مورد استفاده‌است:
              (function(target){
                var privateDoWork = function(name) {
                  return name +" is working";
                };
              
                var Employee = function(name) {
                    this.name = name;
                }
              
                Employee.prototype = {
                   doWork: function() {
                     return privateDoWork(this.name);
                   }
                }
              
                target.Employee = Employee;
              
              }(window));
              IIFE یا immediately invoked function expression، متدی خود اجرا شونده است. به صورت پیش فرض هر متغیری که داخل IIFE تعریف می‌شود، سطح دید آن محدود است به بدنه‌ی همان ماژول و دیگر دسترسی عمومی ندارد. برای مثال شیء Employee فقط داخل این ماژول قابل دسترسی است. تنها زمانیکه توسط target.Employee، این شیء را به window متصل می‌کنیم، خارج از این ماژول قابل دسترسی می‌شود.
              بنابراین با روش IIFE به مزیت‌های یک سیستم ماژول می‌رسیم:
              الف) امکان مدیریت کدها را به صورت یک unit و واحد فراهم می‌کند.
              ب) همچنین در اینجا امکان کنترل میدان دید متغیرها و متدها نیز میسر است.


              روش CommonJS

              از سال 2009 استفاده از جاوا اسکریپت به خارج از مرورگرها گسترش یافت؛ برای مثال نوشتن برنامه‌های سمت سرور NodeJS یا MongoDB با جاوا اسکریپت. در یک چنین حالتی برای مدیریت پیچیدگی برنامه‌های گسترده‌ی سمت سرور و پرهیز از متغیرها و اشیاء عمومی، پروژه‌ی CommonJS شکل گرفت. در CommonJS نحوه‌ی تعریف ماژول‌ها بسیار شبیه است به IIFE. با این تفاوت که دیگر خبری از متد خود اجرا شونده وجود ندارد و همچنین بجای target از exports، جهت درمعرض دید قرار دادن اشیاء استفاده می‌کند.
                var privateDoWork = function(name) {
                  return name +" is working";
                };
              
                var Employee = function(name) {
                    this.name = name;
                }
              
                Employee.prototype = {
                   doWork: function() {
                     return privateDoWork(this.name);
                   }
                }
              
              exports.Employee = Employee;
              این تعاریف در یک فایل مجزای JS قرار می‌گیرند. سپس برای دسترسی به آن‌ها در فایلی دیگر، از روش ذیل استفاده می‌کنند:
               var Employee = require("./Employee").Employee;
              var e1 = new Employee("Vahid");
              console.log(e1.doWork());
              در متد require، مسیر فایل و ماژول تعریف شده، مشخص می‌شود؛ بدون ذکر پسوند .js فایل.


              روش AMD

              از CommonJS بیشتر در برنامه‌های جاوا اسکریپتی که خارج از مرورگر اجرا می‌شوند، استفاده می‌شود. برای حالت‌های اجرای برنامه‌ها درون مرورگرها و خصوصا بلاک نشدن ترد نمایش صفحه در حین پردازش ماژول‌ها، روش دیگری به نام AMD API و یا Asynchronous module definition به وجود آمد. پیاده سازی محبوب این API عمومی، توسط کتابخانه‌ای به نام RequireJS انجام شده‌است.
              define(function(){
              
                var privateDoWork = function(name) {
                  // ...
                };
              
                var Employee = function(name) {
                  // ...
                }
              
                return Employee;
              });
              در اینجا یک ماژول تعریف شده‌ی در یک فایل مجزای جاوا اسکریپتی با define function شروع می‌شود و در نهایت یک return دارد.
              تفاوت مهم این روش با روش IIFE این است که در روش IIFE تمام کد باید مهیا بوده و همچنین بلافاصله قابل اجرا باشد. در اینجا تنها زمانیکه نیاز به کار با ماژولی باشد، اطلاعات آن بارگذاری شده و استفاده می‌شود.
              برای استفاده‌ی از این ماژول‌ها نیز از همان define استفاده می‌شود و پارامتر اول ارسالی، آرایه‌ای است که ماژول‌های مورد نیاز را تعریف می‌کند (تعریف وابستگی‌ها). برای مثال employee تعریف شده در اینجا سبب بارگذاری فایل employee.js می‌شود و سپس امکانات آن به صورت یک پارامتر، به متدی که به آن نیاز دارد ارسال می‌گردد:
              define(["employee"], function(Employee){
                    var e = new Employee("Vahid");
              });


              ماژول‌ها در ES 6

              سیستم تعریف ماژول‌ها در ES 6 بسیار شبیه است به روش‌های CommonJS و AMD API. در اینجا یک نمونه از روش تعریف ماژول‌ها را در نگارش جدید جاوا اسکریپت مشاهده می‌کنید:
              export class Employee {
                constructor(name) {
                  this[s_name] = name;
                }
              
                get name() {
                  return this[s_name];
                }
              
                doWork() {
                  return `${this.name} is working`;
                }
              }
              در اینجا واژه‌ی کلیدی export سبب در دسترس قرار گرفتن تعریف یک کلاس تعریف شده‌ی در این ماژول که در اینجا یک فایل جاوا اسکریپتی است، می‌شود. در یک فایل می‌توان چندین export را داشت؛ اما بهتر است یک export را به ازای هر فایل درنظر گرفت.
              پس از این export، اکنون برای استفاده‌ی از آن در یک فایل js دیگر، از واژه‌ی کلیدی import کمک گرفته می‌شود:
               import {Employee} from "./employee";
              var e1 = new Employee("Vahid");
              console.log(e1.doWork());
              در اینجا پس از from، مسیر فایل js، بدون ذکر پسوند آن مشخص می‌شود.

              و یا برای ارائه‌ی یک متد خروجی، به نحو ذیل عمل می‌شود:
              export function multiply (x, y) {
                 return x * y;
              };
              و اگر یک متغیر و یا متد تعریف شده‌ی در سطح ماژول را بخواهیم عمومی کنیم، باید از {} استفاده شود:
              var hello = 'Hello World',
              multiply = function (x, y) {
                 return x * y;
              };
              export { hello, multiply };


              حالت پیش فرض ماژول‌های ES 6 همان strict mode است

              در سیستم ماژول‌های ES 6، حالت strict به صورت پیش فرض روشن است. برای مثال متغیرها حتما باید تعریف شوند.


              امکان تعریف خروجی‌های متفاوت از یک ماژول در ES 6

              در همان فایلی که export class Employee فوق را در آن تعریف کرده‌ایم، یک چنین تعریف‌هایی را نیز می‌توان ارائه داد:
              export let log = function(employee) {
                 console.log(employee.name);
              }
              
              export let defaultRaise = 0.03;
              
              export let modelEmployee = new Employee("Vahid");
              در اینجا نحوه‌ی export متد log و یا متغیر defaultRaise و همچنین شیء modelEmployee را مشاهده می‌کنید. سپس برای استفاده‌ی از این خروجی‌ها، قسمت import نیز باید به نحو ذیل تغییر کند:
               import {Employee, log, defaultRaise, modelEmployee} from "./employee";
              log(modelEmployee);
              برای ساده سازی دریافت تمام خروجی‌های یک ماژول ES 6، می‌توان از واژه‌ی کلیدی module استفاده کرد:
               module m from "./employee";
              در اینجا متغیر m امکان دسترسی به Employee, log, defaultRaise, modelEmployee را بدون نیاز به ذکر آن‌ها در قسمت import میسر می‌کند. در یک چنین حالتی برای دسترسی به خروجی‌ها، از .m استفاده می‌شود. برای مثال:
               console.log(m.defaultRaise);
              و یا
               var e1 = new m.Employee("Vahid");
              console.log(e1.doWork());

              روش دیگر انجام اینکار، استفاده از * است برای درخواست تمام وابستگی‌های مورد نیاز:
               import * from "./employee";


              امکان استفاده از یک ماژول در ماژولی دیگر

              برای اینکه از امکانات یک ماژول در ماژولی دیگر استفاده کنیم نیز می‌توان از همان روش تعریف import در ابتدای ماژول استفاده کرد:
               import {Employee} from "./employee";


              امکان تعریف ماژول پیش فرض در ES 6

              اگر ماژول شما (همان فایل js) تنها دارای یک export است، می‌توانید آن‌را با واژه‌ی کلیدی default مشخص کنید:
                export default class Employee {
              به این ترتیب برای استفاده‌ی از این ماژول تنها کافی است بنویسیم:
               import factory from "./employee";
              var e1 = new factory("Vahid");
              console.log(e1.doWork());
              در اینجا factory یک نام متغیر دلخواه است و هر نام دیگری را نیز می‌تواند داشته باشد.

              البته باید دقت داشت که یک چنین تعریف‌هایی نیز مجاز است و می‌توان خروجی پیش فرض و همچنین نامداری را نیز با هم ترکیب کرد:
              export hello = 'Hello World';
              export default function (x, y) {
                 return x * y;
              };
              در این حالت تعریف ذیل به این معنا است که pow2 به متد پیش فرض بدون نام و hello به متغیر hello اشاره می‌کنند:
               import pow2, { hello } from 'modules';


              امکان مخفی سازی اطلاعات در ماژول‌های ES 6

              یکی از انتظارات از سیستم ماژول، امکان مخفی سازی اطلاعات است. در اینجا تنها کافی است شیء، متد و یا متغیر تعریف شده، با واژه‌ی کلیدی export مزین نشوند:
              let privateFunction = function() {
              
              }
              
               export default class Employee {
              در این مثال، متد privateFunction در ماژول employee تعریف شده‌است؛ اما چون دارای واژه‌ی کلیدی export نیست، سطح دسترسی آن خصوصی است.

              یک نکته: اگر در کلاس export شده، خواستید تا دسترسی به s_name را محدود کنید، از Symbol ها به نحو ذیل کمک بگیرید:
              let s_name = Symbol();
              
              export class Employee {
                constructor(name) {
                  this[s_name] = name;
                }
              
                get name() {
                  return this[s_name];
                }
              
                doWork() {
                 return `${this.name} is working`;
                }
              }
              مطالب
              مبانی TypeScript؛ تنظیمات کامپایلر
              برای کامپایل کدهای TypeScript به جاوا اسکریپت، علاوه بر پارامترهای کامپایلر، از تنظیمات فایل مخصوصی به نام tsconfig.json نیز استفاده می‌شود که این موارد را در قسمت جاری بررسی خواهیم کرد.


              نحوه‌ی اعمال تنظیمات کامپایلر TypeScript

              روش‌های متفاوتی جهت اعمال تنظیمات کامپایلر TypeScript وجود دارند:
              الف) ذکر پارامترها و سوئیچ‌های کامپایلر خط فرمان tsc به صورت مستقیم.
              ب) بعضی از ادیتور‌ها و IDEها این پارامترها را به صورت گزینه‌ها و دیالوگ‌هایی ارائه می‌دهند.
              ج) استفاده از یک Build task، همانند روشی که در تنظیمات VSCode در مطلب «چرا TypeScript» مشاهده کردید.
              د) ذکر تنظیمات کامپایلر، در فایل مخصوصی به نام tsconfig.json.


              گزینه‌های متداول کامپایلر TypeScript

              گزینه‌های کامپایلر TypeScript نسبتا قابل توجه هستند و لیست کامل و به روز آن‌ها را در هندبوک تایپ‌اسکریپت می‌توانید مشاهده کنید. در اینجا تعدادی از مهم‌ترین‌ها را بررسی خواهیم کرد:
              - سوئیچ module-- جهت مشخص سازی فرمت خروجی ماژول‌های TypeScript بکار می‌رود. در مطلب بررسی ماژول‌ها عنوان شد که TypeScript قادر است ماژول‌های تعریف شده را با سایر فرمت‌های متداول جاوا اسکریپت مانند common.js و amd نیز تولید کند. سوئیچ module-- جهت تنظیم این گزینه درنظر گرفته شده‌است. خلاصه‌ای این سوئیچ نیز m-- است. این سوئیچ یکی از مقادیر commonjs, amd, system, es2015 را می‌پذیرد. اگر es2015 را مشخص کردید، نیاز است target را نیز به ES6 تنظیم کنید.
              - سوئیچ moduleResolution-- نحوه‌ی یافتن ارجاعات به ماژول‌ها را مشخص می‌کند. در اینجا روش‌های node.js و کلاسیک را می‌توان قید کرد.
              - سوئیچ target-- برای تعیین نگارش خروجی جاوا اسکریپت تولیدی بکار می‌رود. حالت پیش فرض آن ES3 است و ES5 و ES6 را نیز پشتیبانی می‌کند.
              - گزینه‌ی watch-- کامپایلر را در حالت watch نگه می‌دارد. در این حالت تغییرات آخرین تاریخ نوشته شدن در فایل‌های ts بررسی شده و در صورت یافتن تغییری، بلافاصله خروجی js آن‌ها تهیه می‌شود.
              - سوئیچ outDir-- برای مشخص کردن پوشه‌ی فایل‌های تولیدی نهایی بکار می‌رود.
              - گزینه‌ی noImplicitAny-- برای ممنوع کردن نوع‌های Any متغیرها به صورت پیش فرض است و در این حالت خطای کامپایلری را مشاهده خواهید کرد. استفاده از این گزینه به این معنا نیست که دیگر نمی‌توان از نوع Any استفاده کرد؛ بلکه به این معنا است که در صورت نیاز باید آن‌را به صورت صریح قید کنید.

              یک مثال:
              در VSCode و در پوشه‌ی vscode. آن، در تنظیمات فایل tasks.json، چنین گزینه‌هایی را می‌توان برای کامپایلر tsc، ذکر کرد:
              "args": ["--target", "ES5",
                          "--outDir", "js",
                          "--module", "commonjs",
                          "--sourceMap",
                          "--watch",
                           "app.ts"],
              به این ترتیب خروجی جاوا اسکریپت آن با فرمت ES 5 بوده و فایل‌های نهایی آن در پوشه‌ی js، در ریشه‌ی پروژه نوشته خواهند شد. همچنین فرمت ماژول‌های خروجی آن نیز به commonjs تنظیم شده‌است. این کامپایلر sourceMapها را جهت امکان دیباگ بهتر کدها تولید کرده و در حالت watch قرار دارد.


              بررسی کاربرد فایل tsconfig.json

              فایل ویژه‌ی tsconfig.json در نگارش 1.5 تایپ‌اسکریپت معرفی گردید. هدف از این فایل، ساده کردن تعریف پارامترهای کامپایلر است؛ البته الزامی به استفاده‌ی از آن وجود ندارد.
              این فایل مزایای ذیل را به همراه دارد:
              الف) محل قرارگیری آن، ریشه‌ی پروژه‌ی TypeScript را مشخص می‌کند.
              ب) تنظیمات ذکر شده‌ی در این فایل، به تمام فایل‌های موجود در پوشه و زیر پوشه‌های محل قرارگیری آن به صورت پیش فرض اعمال می‌شوند.
              هنگامیکه در تنظیمات کامپایلر tsc، نام فایل یا فایل‌های ts ایی را ذکر نمی‌کنید، این کامپایلر در ابتدا به دنبال فایل tsconfig.json می‌گردد و بر این اساس فایل‌های ts را پردازش خواهد کرد. اگر مانند مثال فوق، در انتهای پارامترها، نام فایلی را ذکر کنید، از فایل tsconfig.json صرفنظر خواهد شد.
              یک نکته: برای ذکر صریح محل فایل tsconfig از پارامتر project استفاده کنید:
               tsc --project ./lib
              ج) امکان ذکر گزینه‌های کامپایلر را فراهم می‌کند.
              در این حالت می‌توان کامپایلر tsc را بدون پارامتری اجرا کرد و این برنامه اطلاعات مورد نیاز خود را از فایل tsconfig.json دریافت خواهد کرد. باید دقت داشت، هر سوئیچی که در خط فرمان ذکر شود، پارامترهای معادل ذکر شده‌ی در فایل tsconfig.json را بازنویسی می‌کند. بنابراین در صورت وجود این فایل، می‌توان خاصیت args مثال قبل را به یک آرایه‌ی خالی تنظیم کرد.
              د) امکان مشخص سازی الحاق و عدم الحاق فایل‌های ts را به همراه دارد.
               {
                   "compilerOptions": {
                                    "target": "es5",
                                    "outDir": "js",
                                    "module":"commonjs",
                                    "sourceMap":true
                   },
                   "files": [
                           "app.ts",
                           "classes.ts"
                   ]
              }
              نمونه‌ای از محتوای این فایل JSON را در مثال فوق مشاهده می‌کنید. در خاصیت compilerOptions آن، امکان تعریف پارامترهای کامپایلر وجود دارند؛ مانند تعیین نوع جاوا اسکریپت خروجی و پوشه‌ی نهایی آن. خاصیت آرایه‌ی files آن، برای ذکر لیست فایل‌هایی است که باید به کامپایلر ارسال شوند.
              کار خاصیت files الحاق و include است. اگر می‌خواهید از پوشه‌ها و یا فایل‌هایی صرفنظر شود، از خاصیت exclude استفاده کنید:
              {
                    "compilerOptions": {
                                   "target": "es5",
                                   "outDir": "js"
                    },
                    "exclude": [
                             "node_modules",
                             "lib"
                    ]
              }
              باید دقت داشت که در اینجا تنها یکی از خواص files و یا exclude را می‌توان ذکر کرد. اگر هر دو را با هم ذکر کنید، تنها از خاصیت files استفاده می‌شود.

              یک نکته
              در VSCode داخل فایل tsconfig.json با فشردن ctrl+space، به یک intellisense حاوی گزینه‌های تکمیل کننده‌ی آن خواهید رسید.


              ساده سازی الحاق فایل‌های تعاریف نوع‌ها

              در مطلب «مبانی TypeScript؛ تهیه فایل‌های تعاریف نوع‌ها» با فایل‌های ویژه‌ی d.ts. آشنا شدیم. استفاده‌ی از این فایل‌ها به همراه ذکر اجباری reference path مرتبط در ابتدای هر فایل ts است. این‌کار اضافی را با استفاده از فایل tsconfig.json می‌توان حذف کرد:
              {
                       "compilerOptions": {
                                "target": "es5",
                                "outDir": "js",
                                "module": "commonjs",
                                "sourceMap": true,
                                "watch": true
                       },
                       "files": [
                              "app.ts",
                              "typings/main.d.ts"
                       ]
              }
              در اینجا با ذکر typings/main.d.ts در قسمت files، اطلاعات موجود در این فایل d.ts. به صورت سراسری به تمام فایل‌های ts موجود در پروژه‌ی جاری اعمال می‌شود و دیگر نیازی به ذکر صریح reference path آن نیست.
              مطالب
              شروع کار با webpack - قسمت اول
              سیستم‌های مدیریت ماژول یا باندل کننده‌های جاوااسکریپتی، چندی است که دچار تنوع زیادی شده‌اند و هر از گاهی، چهره‌های جدیدی خود نمایی می‌کنند. اگر با انگولار 2 آشنا باشید قطعا با SystemJs که یکی دیگر از این گونه باندل کننده هاست آشنایید. در این سری قصد داریم که با یک باندل کننده‌ی تقریبا همه کاره با نام webpack آشنا شویم.


              مقدمه و توضیحی بر اینکه چه لزومی بر باندل کننده‌های جاوااسکریپتی هست؟
              زمانیکه جاوا اسکریپت پا به عرصه‌ی وجود گذاشت، در توسعه‌ی برنامه‌های کلاینت، از سیستم‌های بیلد استفاده‌ای نمیشد و شاید بتوان ساده‌ترین دلیل آن را عدم احتیاج جاوااسکریپت به کامپایل دانست. ولی با گذشت زمان و عوض شدن چهره‌ی برنامه‌های سمت کلاینت و بزرگ‌تر شدن آنها، برنامه نویسان با مشکلاتی از قبیل نگه داری و امنیت، در برنامه‌های بزرگ رو به رو بودند.
              در پاسخ به بزرگ شدن پروژه‌ها قطعا شما این پیشنهاد را خواهید داد که بایستی برنامه را به قسمت‌ها و یا ماژول‌های کوچک‌تری بشکنیم، تا هم نگه داری از آن ساده‌تر شود و هم احتمال بروز خطا در حین انجام پروژه کاهش یابد. اما باید به یاد داشت که این قسمت‌های کوچک شده به معنای یک تگ اسکریپت جدا در صفحات وب برنامه می‌باشند و این مساله به این معنا خواهد بود که برای  هر یک از آنها، مرورگر بایستی به میزبان، درخواستی را ارسال کرده و فایل‌ها را جداگانه دریافت کند. قطعا پاسخ به این مشکل دوباره چسباندن این ماژول‌ها به یکدیگر است تا مرورگر فقط یک درخواست را برای این فایل‌ها ارسال کند. این مسئله همچنین برای فایل‌های css و تصاویر نیز صادق می‌باشد. 
              دومین مشکلی که با ماژول سازی برنامه با آن روبه رو می‌شویم، بالا رفتن حجم  کد و درنتیجه بالا رفتن ترافیک مصرفی خواهد بود که این مسئله نیز بایستی توسط یک Minifier حل شود. مشکل بعدی، وابستگی ماژول‌ها به یکدیگر است .در صورتی که در اضافه کردن یک  ماژول به وابستگی‌های آن دقت نداشته باشیم، باعث بروز خطا در برنامه می‌شویم. با استفاده از یک باندلر می‌توانیم وابستگی‌های هر ماژول را تعریف کنیم تا این مسئله نیز حل شود. 
              آخرین  مساله‌ای که به ذهن می‌آید نیز می‌توان قابلیت‌های جدید ES6 را نام برد که به صورت سراسری در تمامی مرورگرها ممکن است هنوز قابل استفاده نباشند و شما به عنوان برنامه نویس قصد بهره بردن از آنها را داشته باشید. درنتیجه راهکار، استفاده از یک ترانسپایلر است که می‌توان از معروف‌ترین آنها تایپ اسکریپت و babel را نام برد .

              راه‌کارهای مختلف برای حل مشکلات ذکر شده
              در صورتی که با فریمورک‌های سمت سرور آشنایی داشته باشید، حتما با سیستم‌های باندل کننده و Minify کننده‌ی آنها برخورد داشته اید. به طور مثال فریمورک Asp.Net Mvc دارای یک باندل کننده‌ی توکار است که مشکل بسته بندی کردن کل ماژول‌ها و همچنین Minify کردن آنها را حل می‌کند. ولی تا آخرین اطلاعی که دارم، مشکل وابستگی ماژول‌ها به جز اینکه برنامه نویس به صورت دستی ترتیب اضافه شدن را رعایت نماید، قابل حل نیست. همچنین در اینجا استفاده از یک ترانسپایلر نیز مقدور نمی‌باشد.
              راه حل دیگر استفاده از Task Runner‌های جاوا اسکریپتی مانند گرانت و گالپ می‌باشد که تمامی مسائلی که پیش‌تر ذکر شد، به وسیله‌ی آنها قابل حل است؛ به جز مسئله‌ی وابستگی ماژول‌ها به یکدیگر که بایستی به صورت دستی توسط برنامه نویس ترتیب آنها رعایت شود یا از فریمورک هایی مانند browserify و ... استفاده شود.

              راه حل webpack
              تفاوت وب پک با TaskRunner‌های جاوا اسکریپتی را می‌توان در اینجا بیان کرد که وب پک در انجام یک وظیفه تخصص وافری دارد و آن وظیفه نیز پردازش فایل‌های ورودی و خروجی داده شده به آن است که با استفاده از کامپوننت‌هایی که با نام loader از آن نام می‌برد، این وظیفه را انجام می‌دهد. با استفاده از این لودرها شما نتیجه‌ای را که از یک TaskRunner انتظار دارید، خواهید گرفت؛ مانند ترنسپایل کردن ماژول‌ها، بسته بندی ماژول‌ها، Minify کردن آنها و در نهایت قابلیتی که معمولا در Task Runner‌ها موجود نیست و وب پک امکان انجام آن را دارد، ترکیب فایل‌های Css با فایل‌های جاوا اسکریپت برنامه است. این کار برای تصاویر و فونت‌های برنامه نیز قابل انجام است.

              پیش فرض‌های کار با webpack
              دو پیش فرض مهم در شروع به کار با وب پک از این قرارند:
              1. وب پک برای نصب Asset‌‌های سمت کلاینت شما از NPM استفاده می‌کند و انتظار دارد که شما نیز این پکیج منیجر بهره ببرید و به طور مثال از bower استفاده نکنید.
              2.استفاده از یک سیستم ماژولار ( اینکه از کدام یک استفاده می‌کنید مهم نیست Commonjs ، amd ، es6 و...)

              نصب webpack و شروع کار
              webpack یکی از صد‌ها ماژول‌های نوشته شده‌ی با استفاده از پلتفرم nodejs می‌باشد. پس اول از همه چیز در صورتیکه nodejs بر روی سیستم شما نصب نیست، آن را دریافت و نصب کنید.  
              قبل از شروع به کار بهتر است که یک محیط کار تمیز ( یک فولدر خالی) را آماده کنید و سپس با اجرای دستور npm init، یک بستر برای کار با npm را داشته باشیم. می‌توانید به صورت دستی نیز یک فایل package.json را اضافه کنید و گزینه‌های مدنظرتان را به آن اضافه کنید.
              من با اجرای این دستور و جواب دادن به سوالاتش یک خروجی فایل package.json با این محتوا را ایجاد کردم :
              {
                "name": "dntwebpack",
                "version": "1.0.0",
                "description": "a webpack tutorial",
                "main": "main.js",
                "scripts": {
                   
                },
                "author": "mehdi",
                "license": "MIT"
              }
              قدم دوم نصب webpack می‌باشد. برای نصب وب پک دو راه وجود دارد:
              1. نصب وب پک به صورت گلوبال ( سراسری ) با استفاده از دستور :npm install -g webpack  ، با اجرای این دستور قابلیت استفاده از وب پک را در همه جا با استفاده از خط فرمان، خواهید داشت.
              2. ایراد روش اول این است که ممکن است در آینده بخواهید در پروژه‌های گوناگون از دو نسخه‌ی متفاوت وب پک استفاده کنید و به خاطر نسخه‌ای که به طور سراسری نصب شده است به مشکل بر بخورید. پس با استفاده از دستور npm install -D webpack  یا npm install --save-dev webpack  وب پک را به صورت محلی برای پروژه نصب می‌کنیم ( کاربرد پرچم D- یا --save-dev این است که وب پک در قسمت وابستگی‌هایی که فقط جهت توسعه‌ی پروژه هستند، در فایل package.json اضافه می‌شود).
              در ادامه در محیط کاری که ایجاد کردیم، دو فایل دیگر را ایجاد می‌کنیم. اولی یک فایل ساده‌ی html جهت اینکه اسکریپت‌های پروژه را به آن اضافه کنیم و دیگری یک فایل اسکریپت جهت اینکه آن را به وب پک بدهیم.
              فایل html را index.html نام گذاری کردم و اسکریپت سمپل را نیز main.js. محتوای هر دوفایل به این صورت می‌باشد:
              <html>
                  <!-- index.html -->
                  <head>
                      first part of webpack tut!
                  </head>
                  <body>
                      <h1>webpack is awesome !</h1>
                      <script src="bundle.js"></script>
                  </body>
              </html>
              //main.js
              
              //start of the journey with webpack
              
              console.log(`i'm bundled by webpack`);
              اگر دقت کنید اسکریپتی با نام bundle.js در فایل html رجوع داده شده است که در پروژه وجود خارجی ندارد و قصد این است که این فایل را با استفاده از وب پک تولید کنیم.
              حالا نوبت به این می‌رسد که تک فایل main.js را به وب پک بدهیم.
              در صورتی که وب پک را به صورت سراسری نصب کرده باشید، این کار ساده است. در خط فرمان با فراخوانی وب پک با دستور webpack ./main.js bundle.js

              فایل bundle.js را تولید می‌کنیم.
              در صورتی که وب پک به صورت محلی در پروژه نصب شده باشد، فایل package.json را باز کرده و در قسمت scripts، یک ورودی جدید را به اسم webpack به همراه فرمان مورد نظر، به آن می‌دهیم. محتوای فایل package.json پس از این کار به صورت زیر خواهد بود:
              {
                "name": "dntwebpack",
                "version": "1.0.0",
                "description": "",
                "main": "main.js",
                "scripts": {
                  "test": "echo \"Error: no test specified\" && exit 1"
                  ,"webpack":"webpack"
                },
                "author": "mehdi",
                "license": "ISC",
                "devDependencies": {
                  "webpack": "^1.13.1"
                }
              }
              حال با استفاده از دستور npm run webpack ./main.js bundle.js  ، وب پک فراخوانی شده و تک فایل main.js را باندل می‌کند.
              در صورتی که اجرای دستور بالا موفقیت آمیز باشد، پاسخی مشابه به زیر را باید دریافت کنید:

              در قسمت بعدی با تنظیمات پیشرفته‌تر و loader‌های وب پک آشنا می‌شویم .
              فایل‌های پروژه dntwebpack.zip  (جهت اجرای آنی احتیاج به نصب وب‌پک را دارید که این کار با استفاده از دستور npm install در فولدر پروژه قابل انجام است).
              مطالب
              مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت ششم - MobX چیست؟
              پیش از بحث در مورد «مدیریت حالت»، باید با مفهوم «حالت» آشنا شد. «حالت» در اینجا همان لایه‌ی داده‌های برنامه است. زمانیکه بحث React و کتابخانه‌های مدیریت حالت آن مطرح می‌شود، می‌توان گفت حالت، شیءای است حاوی اطلاعاتی که برنامه با آن سر و کار دارد. برای مثال اگر برنامه‌ای قرار است لیستی از موارد را نمایش دهد، حالت برنامه، حاوی اشیاء متناظری خواهد بود. حالت، بر روی نحوه‌ی رفتار و رندر کامپوننت‌های React تاثیر می‌گذارد. بنابراین مدیریت حالت، روشی است برای ردیابی و مدیریت داده‌های مورد استفاده‌ی در برنامه و تقریبا تمام برنامه‌ها به نحوی نیاز به آن‌را خواهند داشت.
              داشتن یک کتابخانه‌ی مدیریت حالت برای برنامه‌های React بسیار مفید است؛ خصوصا اگر این برنامه پیچیده باشد و برای مثال در آن نیاز به اشتراک گذاری داده‌ها، بین دو کامپوننت یا بیشتر که در یک رده سلسه مراتبی قرار نمی‌گیرند، وجود داشته باشد. اما حتی اگر از یک کتابخانه‌ی مدیریت حالت استفاده شود، شاید راه حلی را که ارائه می‌کند آنچنان تمیز و قابل انتظار نباشد. با MobX می‌توان از ساختارهای پیچیده‌ی شیءگرا به سادگی استفاده کرد (mutation مستقیم اشیاء در آن مجاز است) و همچنین برای کار با آن به همراه React، نیاز به کدهای کمتری است نسبت به Redux. در اینجا از مفاهیم Reactive programming استفاده می‌شود؛ اما سعی می‌کند پیچیدگی‌های آن‌را مخفی کند. در نام MobX، حرف X به Reactive بودن آن اشاره می‌کند (مانند RxJS) و ob آن از observable گرفته شده‌است. M هم به حرف ابتدای نام شرکتی اشاره می‌کند که این کتابخانه را ایجاد کرده‌است.


              خواص محاسبه شده در جاوا اسکریپت

              برای کار با MobX، نیاز است تا ابتدا با یکسری از مفاهیم آن آشنا شد؛ مانند خواص محاسبه شده (computed properties). برای مثال در اینجا یک کلاس متداول جاوا اسکریپتی را داریم:
              class Person {
                  constructor(firstName, lastName) {
                     this.firstName = firstName;
                     this.lastName = lastName;
                  }
              
                  fullName() {
                    return `${this.firstName} ${this.lastName}`;
                  }
              }
              که در آن از طریق سازنده، دو پارامتر نام و نام خانوادگی دریافت شده و سپس به دو خاصیت جدید، نسبت داده شده‌اند. اکنون برای محاسبه‌ی نام کامل، که حاصل جمع این دو است، می‌توان متد fullName را به این کلاس اضافه کرد. روش کار با آن نیز به صورت زیر است:
              const person = new Person('Vahid', 'N');
              person.firstName; // 'Vahid'
              person.lastName; // 'N'
              person.fullName; // function fullName() {…}
              اگر بر اساس متغیر person که بیانگر وهله‌ای از شیء Person است، سعی در خواندن مقادیر خواص شیء ایجاد شده کنیم، آن‌ها را دریافت خواهیم کرد. اما ذکر person.fullName (بدون هیچ () در مقابل آن)، تنها اشاره‌گری را به آن متد بازگشت می‌دهد و نه نام کامل را که البته یکی از ویژگی‌های جالب جاوا اسکریپت است و امکان ارسال آن‌را به سایر متدها، به صورت پارامتر میسر می‌کند.
              در ES6 برای اینکه تنها با ذکر person.fullName بدون هیچ پرانتزی در مقابل آن بتوان به مقدار کامل fullName رسید، می‌توان از روش زیر و با ذکر واژه‌ی کلیدی get، در پیش از نام متد، استفاده کرد:
              class Person {
                  constructor(firstName, lastName) {
                     this.firstName = firstName;
                     this.lastName = lastName;
                  }
              
                  get fullName() {
                    return `${this.firstName} ${this.lastName}`;
                  }
              }
              در اینجا هرچند fullName هنوز یک متد است، اما اینبار فراخوانی person.fullName، حاصل جمع دو خاصیت را بازگشت می‌دهد و نه اشاره‌گری به آن متد را.
              اگر شبیه به همین قطعه کد را بخواهیم در ES5 پیاده سازی کنیم، روش آن به صورت زیر است:
              function Person(firstName, lastName) {
                 this.firstName = firstName;
                 this.lastName = lastName;
              }
              
              Object.defineProperty(Person.prototype, 'fullName', {
                 get: function () {
                    return this.firstName + ' ' + this.lastName;
                 }
              });
              به این ترتیب می‌توان یک خاصیت محاسبه شده‌ی ویژه‌ی ES5 را تعریف کرد.

              اکنون فرض کنید قسمتی از state برنامه‌ی React، قرار است خاصیت ویژه‌ی fullName را نمایش دهد. برای اینکه UI برنامه با تغییرات نام و نام خانوادگی، متوجه تغییرات fullName که یک خاصیت محاسباتی است، شود و آن‌را رندر مجدد کند، باید در طی یک حلقه‌ی بی‌نهایت، مدام آن‌را فراخوانی کند و نتیجه‌ی جدید را با نتیجه‌ی قبلی محاسبه کرده و تغییرات را نمایش دهد. اینجا است که MobX یک چنین پیاده سازی‌هایی را به کمک مفهوم decorators، ساده می‌کند.


              Decorators در جاوا اسکریپت

              تزئین کننده‌ها یا decorators در سایر زبان‌های برنامه نویسی نیز وجود دارند؛ اما پیاده سازی آن‌ها در جاوا اسکریپت هنوز در مرحله‌ی آزمایشی است. Decorators در جاوا اسکریپت چیزی نیستند بجز بیان زیبای higher-order functions.
              higher-order functions، توابعی هستند که توابع دیگر را با ارائه‌ی قابلیت‌های بیشتری، محصور می‌کنند. به همین جهت هر کاری را که بتوان با تزئین کننده‌ها انجام داد، همان را با توابع معمولی جاوا اسکریپتی نیز می‌توان انجام داد. یک نمونه از این higher-order functions را در سری جاری تحت عنوان higher-order components با متد connect کتابخانه‌ی react-redux مشاهده کرده‌ایم. متد connect، متدی است که متدهای نگاشت state به props و نگاشت dispatch به props را دریافت کرده و سپس یک کامپوننت را نیز دریافت می‌کند و آن‌را به صورت محصور شده‌ای ارائه می‌دهد تا بجای کامپوننت اصلی مورد استفاده قرار گیرد؛ به یک چنین کامپوننت‌هایی، higher-order components گفته می‌شود.

              برای تعریف تزئین کننده‌ها، به نحوه‌ی پیاده سازی Object.defineProperty در مثال فوق دقت کنید:
              Object.defineProperty(Person.prototype, 'fullName', {
                  enumerable: false,
                  writable: false,
                  get: function () {
                    return this.firstName + ' ' + this.lastName;
                  }
              });
              در اینجا Person.prototype یک target است. ثابت fullName، یک کلید است. سایر خواص ذکر شده، مانند enumerable، writable و get، تحت عنوان Descriptor شناخته می‌شوند.
              در ذیل روش تعریف یک تزئین کننده را مشاهده می‌کنید که دقیقا از یک چنین الگویی پیروی می‌کند:
              function decoratorName(target, key, descriptor) {
               // …
              }
              برای مثال در اینجا روش پیاده سازی تزئین کننده‌ی readonly را ملاحظه می‌کنید:
              function readonly(target, key, descriptor) {
                 descriptor.writable = false;
                 return descriptor;
              }
              سپس روش اعمال آن به یک خاصیت محاسباتی در کلاس Person به صورت زیر است:
              class Person {
                  constructor(firstName, lastName) {
                     this.firstName = firstName;
                     this.lastName = lastName;
                  }
              
                  @readonly get fullName() {
                    return `${this.firstName} ${this.lastName}`;
                  }
              }
              ذکر یک تزئین کننده با @ شروع می‌شود. سپس متد fullName را دریافت کرده و نگارش جدیدی از آن‌را بازگشت می‌دهد؛ بطوریکه readonly باشد.


              مثال‌هایی از تزئین کننده‌ها

              برای نمونه می‌توان تزئین کننده‌ی bindThis@ را طراحی کرد تا کار bind شیء this را به متدهای کامپوننت‌های React انجام دهد و یا کتابخانه‌ای به نام core-decorators وجود دارد که به صورت زیر نصب می‌شود:
              > npm install core-decorators
              و به همراه این تزئین کننده‌ها می‌باشد:
              @autobind
              @deprecate
              @readonly
              @memoize
              @debounce
              @profile
              برای مثال autobind آن، همان کار bind شیء this را انجام می‌دهد. deprecate جهت نمایش یک اخطار، در کنسول توسعه دهندگان مرورگر، جهت گوشزد کردن منسوخ بودن قسمتی از برنامه، استفاده می‌شود.

              نمونه‌ی دیگری از این کتابخانه‌ها lodash-decorators است که تعدادی دیگر از تزئین کننده‌ها را ارائه می‌کند.


              MobX چگونه کار می‌کند؟

              انجام یکسری از کارها با Redux مشکل است؛ برای مثال تغییر دادن یک شیء تو در توی پیچیده که شامل تهیه‌ی یک کپی از آن، اعمال تغییرات و غیره‌است. اما با MobX می‌توان با اشیاء جاوا اسکریپتی به همان صورتی که هستند کار کرد. برای مثال آرایه‌ای را با متدهای push و pop تغییر داد (mutation اشیاء مجاز است) و یا خواص اشیاء را به صورت مستقیم ویرایش کرد، در این حالت MobX اعلام می‌کند که ... من می‌دانم که چه تغییری صورت گرفته‌است. بنابراین سبب رندر مجدد UI خواهم شد.


              ایجاد یک برنامه‌ی خالی React برای آزمایش MobX

              در اینجا برای بررسی MobX، یک پروژه‌ی جدید React را ایجاد می‌کنیم:
              > create-react-app state-management-with-mobx-part1
              > cd state-management-with-mobx-part1
              > npm start
              در ادامه کتابخانه‌ی mobx را نیز نصب می‌کنیم. برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
              > npm install --save mobx
              البته برای کار با MobX، الزاما نیازی به طی مراحل فوق نیست؛ ولی چون این قالب، یک محیط آماده‌ی کار با ES6 را فراهم می‌کند، به سادگی می‌توان فایل index.js آن‌را خالی کرد و سپس شروع به کدنویسی و آزمایش MobX نمود.


              مثالی از MobX، مستقل از React

              در اینجا نیز همانند روشی که در بررسی Redux در پیش گرفتیم، ابتدا MobX را به صورت کاملا مستقل از React، با یک مثال بررسی می‌کنیم و سپس در قسمت‌های بعد آن‌را به React متصل می‌کنیم. برای این منظور ابتدا فایل src\index.js را به صورت زیر تغییر می‌دهیم:
              import { autorun, observable } from "mobx";
              
              import React from "react";
              import ReactDOM from "react-dom";
              
              ReactDOM.render(
                <div>
                  <input type="text" id="text-input" />
                  <div id="text-display"></div>
                  <div id="text-display-uppercase"></div>
                </div>,
                document.getElementById("root")
              );
              
              const input = document.getElementById("text-input");
              const textDisplay = document.getElementById("text-display");
              const loudDisplay = document.getElementById("text-display-uppercase");
              
              console.log({ observable, autorun, input, textDisplay, loudDisplay });
              در اینجا یک text-box، به همراه دو div، در صفحه رندر خواهند شد که قرار است با ورود اطلاعاتی در text-box، یکی از آن‌ها (text-display) این اطلاعات را به صورت معمولی و دیگری (text-display-uppercase) آن‌را به صورت uppercase نمایش دهد. روش کار انجام شده هم مستقل از React است و به صورت مستقیم، با استفاده از DOM API و document.getElementById عمل شده‌است. همچنین در ابتدای این فایل، دو import را از کتابخانه‌ی mobx مشاهده می‌کنید.
              - با استفاده از observable می‌خواهیم تغییرات یک شیء جاوا اسکریپتی را تحت نظر قرار داده و هر زمانیکه تغییری در شیء رخ داد، از آن مطلع شویم.
              برای مثال شیء ساده‌ی جاوا اسکریپتی زیر را در نظر بگیرید:
              {
                value: "Hello world!",
                get uppercase() {
                  return this.value.toUpperCase();
                }
              }
              این شیء دارای دو خاصیت است که یکی معمولی و دیگری به صورت یک خاصیت محاسباتی، تعریف شده‌است. مشکلی که با این شیء وجود دارد این است که اگر مقدار خاصیت value آن تغییر کند، از آن مطلع نخواهیم شد تا پس از آن برای مثال در مورد رندر مجدد DOM، تصمیم گیری شود. چون از دیدگاه React، مقدار ارجاع به این شیء با تغییر خواص آن، تغییری نمی‌کند. به همین جهت اگر نحوه‌ی مقایسه، بر اساس مقایسه‌ی ارجاعات به اشیاء باشد (strict === reference check)، چون شیء تغییر یافته نیز به همان شیء اصلی اشاره می‌کند، بنابراین دارای ارجاع یکسانی خواهند بود و سبب رندر مجدد DOM نمی‌شوند.
              به همین جهت اینبار شیء فوق را توسط یک observable ارائه می‌دهیم، تا بتوانیم به تغییرات خواص آن گوش فرا دهیم:
              const text = observable({
                value: "Hello world!",
                get uppercase() {
                  return this.value.toUpperCase();
                }
              });
              در ادامه یک EventListener را به text-box تعریف شده اضافه کرده و در رخ‌داد keyup آن، سبب تغییر خاصیت value شیء text فوق، بر اساس مقدار تایپ شده می‌شویم:
              input.addEventListener("keyup", event => {
                 text.value = event.target.value;
              });
              اکنون چون شیء text، یک observable است، هر زمانیکه که خاصیتی از آن تغییر می‌کند، می‌خواهیم بر اساس آن، DOM را به صورت دستی به روز رسانی کنیم. برای اینکار نیاز به متد autorun دریافتی از mobx خواهیم داشت:
              autorun(() => {
                 textDisplay.textContent = text.value;
                 loudDisplay.textContent = text.uppercase;
              });
              هر زمانیکه شیء observable ای که داخل متد autorun تحت نظر قرار گرفته شده، تغییر کند، سبب اجرای callback method ارسالی به آن خواهد شد. برای مثال در اینجا چون text.value را به event.target.value متصل کرده‌ایم، هربار که کلیدی فشرده می‌شود، سبب بروز تغییری در خاصیت value خواهد شد. در نتیجه‌ی آن، autorun اجرا شده و مقادیر درج شده‌ی در divهای صفحه را بر اساس خواص value و uppercase شیء text، تغییر می‌دهد:

              برای آزمایش آن، برنامه را اجرا کرده و متنی را داخل textbox وارد کنید:


              نکته‌ی جالب اینجا است که هرچند فقط خاصیت value را تغییر داده‌ایم (تغییر مستقیم خواص یک شیء؛ بدون نیاز به ساخت یک clone از آن)، اما خاصیت محاسباتی uppercase نیز به روز رسانی شده‌است.

              زمانیکه mobx را به یک برنامه‌ی React متصل می‌کنیم، قسمت autorun، از دید ما مخفی خواهد بود. در این حالت فقط یک شیء معمولی جاوا اسکریپتی را مستقیما تغییر می‌دهیم و ... در نتیجه‌ی آن رندر مجدد UI صورت خواهد گرفت.


              یک observable چگونه کار می‌کند؟

              در اینجا یک شبه‌کد را که بیانگر نحوه‌ی عملکرد یک observable است، مشاهده می‌کنید:
              const onChange = (oldValue, newValue) => {
                // Tell MobX that this value has changed.
              }
              
              const observable = (value) => {
                return {
                  get() { return value; },
                  set(newValue) {
                    onChange(this.get(), newValue);
                    value = newValue;
                  }
                }
              }
              یک observable هنگامیکه شی‌ءای را در بر می‌گیرد. هر زمانیکه مقدار جدیدی را به خاصیتی از آن نسبت دادیم، سبب فراخوانی متد onChange شده و به این صورت است که کتابخانه‌ی MobX متوجه تغییرات می‌گردد و بر اساس آن امکان ردیابی تغییرات را میسر می‌کند.


              کدهای کامل این قسمت را می‌توانید از اینجا دریافت کنید: state-management-with-mobx-part1.zip
              مطالب
              جمع آوری آمار لینک‌های خروجی از سایت توسط Google analytics

              چندی قبل مطلب کوتاهی را در مورد Google analytics نوشتم. در حین جستجو درباره‌ی jQuery در وب، به نحوه ردیابی لینک‌های خروجی از سایت توسط Google analytics برخوردم که نحوه پیاده سازی آن به صورت زیر است.
              بدیهی است قبل از هر کاری باید اسکریپت مربوط به Google analytics را به انتهای صفحه و جایی که تگ body بسته می‌شود اضافه کنید (قابل دریافت درقسمت Add Website Profile . شماره این اسکریپت برای هر پروفایلی که ایجاد می‌کنید متفاوت است).
              سپس:
              الف) افزودن ارجاعی از کتابخانه jQuery به هدر صفحه که آن‌را در مطلب شمسی کردن تاریخ بلاگر ملاحظه کردید.
              ب) افزودن چند سطر زیر به هدر صفحه
              <script type="text/javascript">
              $(document).ready(function() {
              $("a").click(function() {
              var $a = $(this);
              var href = $a.attr("href");

              // see if the link is external
              if ( (href.match(/^http/)) && (! href.match(document.domain)) ) {

              // if so, register an event
              var category = "outgoing";
              var event = "click";
              var label = href;

              pageTracker._trackPageview('/outgoing/' + href);
              pageTracker._trackEvent(category, event, href);
              }
              });
              });
              </script>

              البته اگر قبلا اسکریپت شمسی کردن تاریخ بلاگر را اضافه کرده بودید فقط محتویات تابع document.ready را باید اضافه کنید (جهت مشاهده نمونه اعمال شده، روی صفحه جاری کلیک راست کنید و سورس صفحه را مشاهده نمائید).

              توضیحاتی در مورد کد فوق:
              این اسکریپت به روال رخ داد گردان onclick هر لینکی که به خارج از سایت ختم می‌شود (مثلا لینک به یک فایل یا یک سایت خارجی (خارج از سایت))، به صورت خودکار تابع trackPageview مربوط به Google analytics را اضافه می‌کند. این کار تاثیری در عملکرد سایت ندارد و کاربر چیزی را متوجه نخواهد شد، اما به این طریق لینک‌های خروجی در آمار Google analytics ظاهر می‌شوند (مطابق تصاویر زیر).





              از این پس آمار تمام لینک‌های خروجی از سایت ، متمایز شده با outgoing ، جمع آوری و نمایش داده خواهند شد.

              امکانات بیشتری مانند event tracking نیز قرار است به Google analytics اضافه شود که هنوز در مرحله آزمایشی است و بر روی تمامی اکانت‌ها فعال نشده است.

              مطالب
              نحوه اضافه کردن Auto-Complete به جستجوی لوسین در ASP.NET MVC و Web forms
              پیشنیازها:
              چگونه با استفاده از لوسین مطالب را ایندکس کنیم؟
              چگونه از افزونه jQuery Auto-Complete استفاده کنیم؟
              نحوه استفاده صحیح از لوسین در ASP.NET


              اگر به جستجوی سایت دقت کرده باشید، قابلیت ارائه پیشنهاداتی به کاربر توسط یک Auto-Complete به آن اضافه شده‌است. در مطلب جاری به بررسی این مورد به همراه دو مثال Web forms و MVC پرداخته خواهد شد.


              قسمت عمده مطلب جاری با پیشنیازهای یاد شده فوق یکی است. در اینجا فقط به ذکر تفاوت‌ها بسنده خواهد شد.

              الف) دریافت لوسین
              از طریق NuGet آخرین نگارش را دریافت و به پروژه خود اضافه کنید. همچنین Lucene.NET Contrib را نیز به همین نحو دریافت نمائید.

              ب) ایجاد ایندکس
              کدهای این قسمت با مطلب برجسته سازی قسمت‌های جستجو شده، یکی است:
              using System.Collections.Generic;
              using System.IO;
              using Lucene.Net.Analysis.Standard;
              using Lucene.Net.Documents;
              using Lucene.Net.Index;
              using Lucene.Net.Store;
              using LuceneSearch.Core.Model;
              using LuceneSearch.Core.Utils;
              
              namespace LuceneSearch.Core
              {
                  public static class CreateIndex
                  {
                      static readonly Lucene.Net.Util.Version _version = Lucene.Net.Util.Version.LUCENE_30;
              
                      public static Document MapPostToDocument(Post post)
                      {
                          var postDocument = new Document();
                          postDocument.Add(new Field("Id", post.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
                          var titleField = new Field("Title", post.Title, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS);
                          titleField.Boost = 3;
                          postDocument.Add(titleField);
                          postDocument.Add(new Field("Body", post.Body.RemoveHtmlTags(), Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
                          return postDocument;
                      }
              
                      public static void CreateFullTextIndex(IEnumerable<Post> dataList, string path)
                      {
                          var directory = FSDirectory.Open(new DirectoryInfo(path));
                          var analyzer = new StandardAnalyzer(_version);
                          using (var writer = new IndexWriter(directory, analyzer, create: true, mfl: IndexWriter.MaxFieldLength.UNLIMITED))
                          {
                              foreach (var post in dataList)
                              {
                                  writer.AddDocument(MapPostToDocument(post));
                              }
              
                              writer.Optimize();
                              writer.Commit();
                              writer.Close();
                              directory.Close();
                          }
                      }
                  }
              }
              تنها تفاوت آن اضافه شدن titleField.Boost = 3 می‌باشد. توسط Boost به لوسین خواهیم گفت که اهمیت عبارات ذکر شده در عناوین مطالب، بیشتر است از اهمیت متون آن‌ها.


              ج) تهیه قسمت منبع داده Auto-Complete

              namespace LuceneSearch.Core.Model
              {
                  public class SearchResult
                  {
                      public int Id { set; get; }
                      public string Title { set; get; }
                  }
              }

              using System.Collections.Generic;
              using System.IO;
              using Lucene.Net.Index;
              using Lucene.Net.Search;
              using Lucene.Net.Store;
              using LuceneSearch.Core.Model;
              using LuceneSearch.Core.Utils;
              
              namespace LuceneSearch.Core
              {
                  public static class AutoComplete
                  {
                      private static IndexSearcher _searcher;
              
                      /// <summary>
                      /// Get terms starting with the given prefix
                      /// </summary>
                      /// <param name="prefix"></param>
                      /// <param name="maxItems"></param>
                      /// <returns></returns>
                      public static IList<SearchResult> GetTermsScored(string indexPath, string prefix, int maxItems = 10)
                      {
                          if (_searcher == null)
                              _searcher = new IndexSearcher(FSDirectory.Open(new DirectoryInfo(indexPath)), true);
              
                          var resultsList = new List<SearchResult>();
                          if (string.IsNullOrWhiteSpace(prefix))
                              return resultsList;
              
                          prefix = prefix.ApplyCorrectYeKe();
              
                          var results = _searcher.Search(new PrefixQuery(new Term("Title", prefix)), null, maxItems);
                          if (results.TotalHits == 0)
                          {
                              results = _searcher.Search(new PrefixQuery(new Term("Body", prefix)), null, maxItems);
                          }
              
                          foreach (var doc in results.ScoreDocs)
                          {
                              resultsList.Add(new SearchResult
                              {
                                  Title = _searcher.Doc(doc.Doc).Get("Title"),
                                  Id = int.Parse(_searcher.Doc(doc.Doc).Get("Id"))
                              });
                          }
              
                          return resultsList;
                      }
                  }
              }
              توضیحات:
              برای نمایش Auto-Complete نیاز به منبع داده داریم که نحوه ایجاد آن‌را در کدهای فوق ملاحظه می‌کنید. در اینجا توسط جستجوی سریع لوسین و امکانات PrefixQuery آن، به تعدادی مشخص (maxItems)، رکوردهای یافت شده را بازگشت خواهیم داد. خروجی حاصل لیستی است از SearchResultها شامل عنوان مطلب و Id آن. عنوان را به کاربر نمایش خواهیم داد؛ از Id برای هدایت او به مطلبی مشخص استفاده خواهیم کرد.


              د) نمایش Auto-Complete در ASP.NET MVC

              using System.Text;
              using System.Web.Mvc;
              using LuceneSearch.Core;
              using System.Web;
              
              namespace LuceneSearch.Controllers
              {
                  public class HomeController : Controller
                  {
                      static string _indexPath = HttpRuntime.AppDomainAppPath + @"App_Data\idx";
              
                      public ActionResult Index(int? id)
                      {
                          if (id.HasValue)
                          {
                              //todo: do something
                          }
                          return View(); //Show the page
                      }
              
                      public virtual ActionResult ScoredTerms(string q)
                      {
                          if (string.IsNullOrWhiteSpace(q))
                              return Content(string.Empty);
              
                          var result = new StringBuilder();
                          var items = AutoComplete.GetTermsScored(_indexPath, q);
                          foreach (var item in items)
                          {
                              var postUrl = this.Url.Action(actionName: "Index", controllerName: "Home", routeValues: new { id = item.Id }, protocol: "http");
                              result.AppendLine(item.Title + "|" + postUrl);
                          }
              
                          return Content(result.ToString());
                      }
                  }
              }

              @{
                  ViewBag.Title = "جستجو";
                  var scoredTermsUrl = Url.Action(actionName: "ScoredTerms", controllerName: "Home");
                  var bulletImage = Url.Content("~/Content/Images/bullet_shape.png");
              }
              <h2>
                  جستجو</h2>
              
              <div align="center">
                  @Html.TextBox("term", "", htmlAttributes: new { dir = "ltr" })
                  <br />
                  جهت آزمایش lu را وارد نمائید
              </div>
              
              @section scripts
              {
                  <script type="text/javascript">
                      EnableSearchAutocomplete('@scoredTermsUrl', '@bulletImage');
                  </script>
              }

              function EnableSearchAutocomplete(url, img) {
                  var formatItem = function (row) {
                      if (!row) return "";
                      return "<img src='" + img + "' /> " + row[0];
                  }
              
                  $(document).ready(function () {
                      $("#term").autocomplete(url, {
                          dir: 'rtl', minChars: 2, delay: 5,
                          mustMatch: false, max: 20, autoFill: false,
                          matchContains: false, scroll: false, width: 300,
                          formatItem: formatItem
                      }).result(function (evt, row, formatted) {
                          if (!row) return;
                          window.location = row[1];
                      });
                  });
              }
              توضیحات:
              - ابتدا ارجاعاتی را به jQuery، افزونه Auto-Complete و اسکریپت سفارشی تهیه شده، در فایل layout پروژه تعریف خواهیم کرد.
              در اینجا سه قسمت را مشاهده می‌کنید: کدهای کنترلر، View متناظر و اسکریپتی که Auto-Complete را فعال خواهد ساخت.
              - قسمت مهم کدهای کنترلر، دو سطر زیر هستند:
              result.AppendLine(item.Title + "|" + postUrl);
              return Content(result.ToString());
              مطابق نیاز افزونه انتخاب شده در مثال جاری، فرمت خروجی مدنظر باید شامل سطرهایی حاوی متن قابل نمایش به همراه یک Id (یا در اینجا یک آدرس مشخص) باشد. البته ذکر این Id اختیاری بوده و در اینجا جهت تکمیل بحث ارائه شده است.
              return Content هم سبب بازگشت این اطلاعات به افزونه خواهد شد.
              - کدهای View متناظر بسیار ساده هستند. تنها نام TextBox تعریف شده مهم می‌باشد که در متد جاوا اسکریپتی EnableSearchAutocomplete استفاده شده است. به علاوه، نحوه مقدار دهی آدرس دسترسی به اکشن متد ScoredTerms نیز مهم می‌باشد.
              - در متد EnableSearchAutocomplete نحوه فراخوانی افزونه autocomplete را ملاحظه می‌کنید.
              جهت آن، به راست به چپ تنظیم شده است. با 2 کاراکتر ورودی فعال خواهد شد با وقفه‌ای کوتاه. نیازی نیست تا انتخاب کاربر از لیست ظاهر شده حتما با عبارت جستجو شده صد در صد یکی باشد. حداکثر 20 آیتم در لیست ظاهر خواهند شد. اسکرول بار لیست را حذف کرده‌ایم. عرض آن به 300 تنظیم شده است و نحوه فرمت دهی نمایشی آن‌را نیز ملاحظه می‌کنید. برای این منظور از متد formatItem استفاده شده است. آرایه row در اینجا در برگیرنده اعضای Title و Id ارسالی به افزونه است. اندیس صفر آن به عنوان دریافتی اشاره می‌کند.
              همچنین نحوه نشان دادن عکس العمل به عنصر انتخابی را هم ملاحظه می‌کنید (در متد result مقدار دهی شده).  window.location را به عنصر دوم آرایه row هدایت خواهیم کرد. این عنصر دوم مطابق کدهای اکشن متد تهیه شده، به آدرس یک صفحه اشاره می‌کند.


              ه) نمایش Auto-Complete در ASP.NET WebForms

              قسمت عمده مطالب فوق با وب فرم‌ها نیز یکی است. خصوصا توضیحات مرتبط با متد EnableSearchAutocomplete ذکر شده.
              <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="LuceneSearch.WebForms.Default" %>
              
              <!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">
                  <meta charset="utf-8" />
                  <meta name="viewport" content="width=device-width" />
                  <title>جستجو</title>
                  <link href="Content/Site.css" rel="stylesheet" type="text/css" />
                  <script src="Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
                  <script src="Scripts/jquery.autocomplete.js" type="text/javascript"></script>
                  <script src="Scripts/custom.js" type="text/javascript"></script>
              </head>
              <body dir="rtl">
                  <h2>
                      جستجو</h2>
                  <form id="form1" runat="server">
                  <div align="center">
                      <asp:TextBox runat="server" dir="ltr" ID="term"></asp:TextBox>
                      <br />
                      جهت آزمایش lu را وارد نمائید
                  </div>
                  </form>
                  <script type="text/javascript">
                      EnableSearchAutocomplete('Search.ashx', 'Content/Images/bullet_shape.png');
                  </script>
              </body>
              </html>

              using System.Text;
              using System.Web;
              using LuceneSearch.Core;
              
              namespace LuceneSearch.WebForms
              {
                  public class Search : IHttpHandler
                  {
                      static string _indexPath = HttpRuntime.AppDomainAppPath + @"App_Data\idx";
              
                      public void ProcessRequest(HttpContext context)
                      {
                          string q = context.Request.QueryString["q"];
                          if (string.IsNullOrWhiteSpace(q))
                          {
                              context.Response.Write(string.Empty);
                              context.Response.End();
                          }
              
                          var result = new StringBuilder();
                          var items = AutoComplete.GetTermsScored(_indexPath, q);
                          foreach (var item in items)
                          {
                              var postUrl = "Default.aspx?id=" + item.Id;
                              result.AppendLine(item.Title + "|" + postUrl);
                          }
              
                          context.Response.ContentType = "text/plain";
                          context.Response.Write(result.ToString());
                          context.Response.End();
                      }
              
                      public bool IsReusable
                      { get { return false; } }
                  }
              }

              در اینجا بجای Controller از یک Generic handler استفاده شده است (Search.ashx).
              result.AppendLine(item.Title + "|" + postUrl);
              context.Response.Write(result.ToString());
              در آن، عنوان مطالب یافت شده به همراه یک آدرس مشخص، تهیه و در Response نوشته خواهند شد.


              کدهای کامل مثال فوق را از اینجا می‌توانید دریافت کنید:
              همچنین باید دقت داشت که پروژه MVC آن از نوع MVC4 است (VS2010) و فرض براین می‌باشد که IIS Express 7.5 را نیز پیشتر نصب کرده‌اید.
              کلمه عبور فایل: dotnettips91