نظرات مطالب
ASP.NET MVC #21
عذرخواه. ولی متوجه نمیشم در شکل بایناری یعنی چی؟
نظرات مطالب
نحوه ایجاد Sequence و استفاده آن در Sql Server 2012
سلام
خیلی ممنون از پاسخی که دادین. مفید واقع شد.
البته فکر کنم برای گارانتی کردن ترتیب sequence بر اساس ستون id می‌تونیم از OVER(ORDER BY نیز استفاده کنیم.
به این شکل:
SET IDENTITY_INSERT table_3 on
INSERT INTO table_3 (ID, Descritp)
SELECT
      NEXT VALUE FOR testEventCounter (OVER ORDER BY ID) AS id
    , Descritp
FROM T
به هر شکل با مفهوم جدیدی نیز آشنا شدیم :)

نظرات مطالب
کار با کلیدهای اصلی و خارجی در EF Code first
مثال شما رو به صورت مستقل اجرا کردم، جداول ساخته شدند، رکوردها در هر سه جدول ثبت شدند و مشکلی مشاهده نمیشه.
اگر برای دیتابیس موجود قصد دارید mapping تعریف کنید ممکن است کلیدهای تعریف شده در آن کم یا زیاد باشند. بهتر است یک خروجی مستقل از کلاس‌های فوق تهیه کنید (اجازه بدید EF دیتابیس را تولید کند) و بعد با کار خودتون مقایسه کنید که چه چیزهایی را کم و زیاد دارد.
اگر code-first عمل می‌کنید و دیتابیس قرار است از روی کدهای فوق تهیه شود، تمام نگاشت‌ها را حذف کنید (کلاس‌های Map تعریف شده را)، EF به راحتی روابط man-to-many را تشخیص داده و کلیدهای خارجی و جدول واسط را تهیه می‌کند. نام‌های پیش فرض آن هم از نظر من بسیار مناسب است و نیازی به تغییر ندارند. (تنها تغییری که با بودن کلاس‌های Map فوق حاصل میشه، تعیین نام فیلدهای جدول واسط است و زمانیکه code-first کار می‌کنید این نام‌ها مهم نیستند؛ چون با LINQ نهایتا قرار است کار کنید و خواص کلاس‌ها)
مطالب
ساخت یک Form Generator ساده در MVC
در ادامه می‌خواهیم نحوه‌ی ایجاد یک فرم‌ساز ساده را ASP.NET MVC بررسی کنیم.
مدل‌های برنامه ما به صورت زیر می‌باشند:
namespace SimpleFormGenerator.DomainClasses
{
    public class Form
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public virtual ICollection<Field> Fields { get; set; }
    }
    public class Field
    {
        public int Id { get; set; }
        public string TitleEn { get; set; }
        public string TitleFa { get; set; }
        public FieldType FieldType { get; set; }
        public virtual Form Form { get; set; }
        public int FormId { get; set; }

    }
    public enum FieldType
    {
        Button,
        Checkbox,
        File,
        Hidden,
        Image,
        Password,
        Radio,
        Reset,
        Submit,
        Text
    }
    
}
توضیح مدل‌های فوق:
همانطور که مشاهده می‌کنید برنامه ما از سه مدل تشکیل شده است. اولین مورد آن کلاس فرم است. این کلاس در واقع بیانگر یک فرم است که در ساده‌ترین حالت خود از یک Id، یک عنوان و تعدادی از فیلدها تشکیل می‌شود. کلاس فیلد نیز بیانگر یک فیلد است که شامل: آی‌دی، عنوان انگلیسی فیلد، عنوان فارسی فیلد، نوع فیلد (که در اینجا از نوع enum انتخاب شده است که خود شامل چندین آیتم مانند Text, Radioو... است) و کلید خارجی کلاس فرم می‌باشد. تا اینجا مشخص شد که رابطه فرم با فیلد، یک رابطه یک به چند است؛ یعنی یک فرم می‌تواند چندین فیلد داشته باشد.
کلاس کانتکست برنامه نیز به این صورت می‌باشد:
namespace SimpleFormGenerator.DataLayer.Context
{
    public class SimpleFormGeneratorContext : DbContext, IUnitOfWork
    {
        public SimpleFormGeneratorContext()
            : base("SimpleFormGenerator") {}
        public DbSet<Form> Forms { get; set; }
        public DbSet<Field> Fields { get; set; }
        public DbSet<Value> Values { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Value>()
    .HasRequired(d => d.Form)
    .WithMany()
    .HasForeignKey(d => d.FormId)
    .WillCascadeOnDelete(false);

        }
        
    }
}
 همانطور که مشاهده می‌کنید مدل‌های برنامه را در معرض دید EF قرار داده‌ایم. تنها نکته‌ایی که در کلاس فوق مهم است متد OnModelCreating است. از آنجائیکه رابطه کلاس Field و Value یک رابطه یک‌به‌یک است باید ابتدا و انتهای روابط را برای این دو کلاس تعیین کنیم.
 
 تا اینجا می‌توانیم به کاربر امکان ایجاد یک فرم و همچنین تعیین فیلد‌های یک فرم را بدهیم. برای اینکار ویو‌های زیر را در نظر بگیرید:
ویو ایجاد یک فرم:
@model SimpleFormGenerator.DomainClasses.Form

@{
    ViewBag.Title = "صفحه ایجاد یک فرم";
}


@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div>
            <span>عنوان</span>
            <div>
                @Html.EditorFor(model => model.Title, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Title, "", new { @class = "text-danger" })
            </div>
        </div>

        
        <div>
            <div>
                <input type="submit" value="ذخیره" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("بازگشت", "Index")
</div>
ویوی ایجاد فیلد برای هر فرم:
@model SimpleFormGenerator.DomainClasses.Field

@{
    ViewBag.Title = "CreateField";
}

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div>
            <span>عنوان انگلیسی</span>
            <div>
                @Html.EditorFor(model => model.TitleEn, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.TitleEn, "", new { @class = "text-danger" })
            </div>
        </div>

        <div>
            <span>عنوان فارسی</span>
            <div>
                @Html.EditorFor(model => model.TitleFa, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.TitleFa, "", new { @class = "text-danger" })
            </div>
        </div>

        <div>
            <span>نوع فیلد</span>
            <div>
                @Html.EnumDropDownListFor(model => model.FieldType, htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.FieldType, "", new { @class = "text-danger" })
            </div>
        </div>

        <div>
            <span>فرم</span>
            <div>
                @Html.DropDownList("FormId", (SelectList)ViewBag.FormList)
                @Html.ValidationMessageFor(model => model.FormId, "", new { @class = "text-danger" })
            </div>
        </div>

        <div>
            <div>
                <input type="submit" value="ذخیره" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("بازگشت ", "Index")
</div>
در ویوی فوق کاربر می‌تواند برای فرم انتخاب شده فیلدهای موردنظر را تعریف کند:


ویوی نمایش فرم تولید شده برای کاربر نهایی:
@using SimpleFormGenerator.DomainClasses
@model IEnumerable<SimpleFormGenerator.DomainClasses.Field>

@{
    ViewBag.Title = "نمایش فرم";
}

<div>
    <div>
        <div>
            @using (Html.BeginForm())
            {
                @Html.AntiForgeryToken()
                for (int i = 0; i < Model.Count(); i++)
                {
                    if (Model.ElementAt(i).FieldType == FieldType.Text)
                    {
                        <text>
                            <input type="hidden" name="[@i].FieldType" value="@Model.ElementAt(i).FieldType" />
                            <input type="hidden" name="[@i].Id" value="@Model.ElementAt(i).Id" /> 
                            <input type="hidden" name="[@i].FormId" value="@Model.ElementAt(i).FormId" /> 
                            <div>
                                <label>@Model.ElementAt(i).TitleFa</label>
                                <div>
                                    <input type="text" name="[@i].TitleEn" />
                                </div>
                            </div>

                        </text>

                    }
                }
                <div data-formId ="@ViewBag.FormId">
                    <div>
                        <input type="submit" value="ارسال فرم" />
                    </div>
                </div>
            }
        </div>
        <div>
            @Html.ActionLink("بازگشت", "Index")
        </div>
    </div>
</div>
همانطور که در کدهای فوق مشخص است از اکشن متدی که در ادامه مشاهده خواهید کرد لیستی از فیلدهای مربوط به یک فرم را برای کاربر به صورت رندر شده نمایش داده‌ایم. در اینجا باید براساس فیلد FieldType، نوع فیلد را تشخیص دهیم و المنت متناسب با آن را برای کاربر نهایی رندر کنیم. برای اینکار توسط یک حلقه for در بین تمام فیلدها پیمایش می‌کنیم:
for (int i = 0; i < Model.Count(); i++)
{
     // code
}
سپس در داخل حلقه یک شرط را برای بررسی نوع فیلد قرار داده‌ایم:
if (Model.ElementAt(i).FieldType == FieldType.Text)
{
     // code
}
بعد از بررسی نوع فیلد، خروجی رندر شده به این صورت برای کاربر نهایی به صورت یک عنصر HTML نمایش داده می‌شود:
<input type="text" name="[@i].TitleEn" />
همانطور که در کدهای قبلی مشاهده می‌کنید یکسری فیلد را به صورت مخفی بر روی فرم قرار داده‌ایم زیرا در زمان پست این اطلاعات به سرور از آنجائیکه مقادیر فیلدهای فرم تولید شده ممکن است چندین مورد باشند، به صورت آرایه‌ایی از عناصر آنها را نمایش خواهیم داد:
[@i].FieldTyp
خوب، تا اینجا توانستیم یک فرم‌ساز ساده ایجاد کنیم. اما برای ارسال این اطلاعات به سرور به یک مدل دیگر احتیاج داریم. این جدول در واقع محل ذخیره‌سازی مقادیر فیلدهای یک فرم و یا فرم‌های مختلف است. 
public class Value
{
        public int Id { get; set; }
        public string Val { get; set; }
        public virtual Field Field { get; set; }
        [ForeignKey("Field")]
        public int FieldId { get; set; }
        public virtual Form Form { get; set; }
        [ForeignKey("Form")]
        public int FormId { get; set; }
        
}
این جدول در واقع شامل: آی‌دی، مقدار فیلد، کلید خارجی فیلد و کلید خارجی فرم می‌باشد. بنابراین برای ارسال ویو قبلی به سرور اکشن‌متد ShowForm را در حالت Post به این صورت خواهیم نوشت:
[HttpPost]
        public ActionResult ShowForm(IEnumerable<Field> values)
        {

            if (ModelState.IsValid)
            {
                foreach (var value in values)
                {
                    _valueService.AddValue(new Value { Val = value.TitleEn, FormId = value.FormId, FieldId = value.Id});
                    _uow.SaveAllChanges();
                }
            }
            return View(values);
        }
سورس مثال جاری را نیز می‌توانید از اینجا دریافت کنید.
مطالب
CoffeeScript #4

Syntax

Loops

for name in ["Vahid", "Hamid", "Saeid"]
  alert "Hi #{name}"
نتیجه‌ی کامپایل کد بالا می‌شود:
var i, len, name, ref;

ref = ["Vahid", "Hamid", "Saeid"];
for (i = 0, len = ref.length; i < len; i++) {
  name = ref[i];
  alert("Hi " + name);
}
درصورتیکه نیاز به شمارنده‌ی حلقه داشته باشید، کافیست یک آرگومان اضافه را ارسال کنید. برای نمونه:
for name, i in ["Vahid", "Hamid", "Saeid"]
  alert "#{i} - Hi #{name}"
همچنین می‌توانید حلقه را به صورت یک خطی نیز بنویسید:
alert name for name in ["Vahid", "Hamid", "Saeid"]
همچنین مانند Python نیز می‌توانید از فیلتر کردن در حلقه، استفاده کنید.
names = ["Vahid", "Hamid", "Saeid"]
alert name for name in names when name[0] is "V"
و نتیجه کامپایل کد بالا می‌شود:
var i, len, name, names;

names = ["Vahid", "Hamid", "Saeid"];

for (i = 0, len = names.length; i < len; i++) {
  name = names[i];
  if (name[0] === "V") {
    alert(name);
  }
}
شما می‌توانید حلقه را برای یک object نیز استفاده کنید. به جای استفاده از کلمه‌ی کلیدی in، از کلمه کلیدی of استفاده کنید.
names = "Vahid": "Mohammad Taheri", "Ali": "Ahmadi"
alert("#{first} #{last}") for first, last of names
پس از کامپایل نتیجه می‌شود:
var first, last, names;

names = {
  "Vahid": "Mohammad Taheri",
  "Ali": "Ahmadi"
};

for (first in names) {
  last = names[first];
  alert(first + " " + last);
}
حلقه while در CoffeeScript به مانند جاوااسکریپت عمل می‌کند؛ ولی مزیتی نیز به آن اضافه شده است که آرایه‌ای از نتایج را بر می‌گرداند. به عنوان مثال مانند تابع ()Array.prototype.map .
num = 6
minstrel = while num -= 1
  num + " Hi"
نتیجه‌ی کامپایل آن می‌شود:
var minstrel, num;
num = 6;
minstrel = (function() {
  var _results;
  _results = [];
  while (num -= 1) {
    _results.push(num + " Hi");
  }
  return _results;
})();


Arrays

CoffeeScript با الهام گرفتن از Ruby، به وسیله تعیین محدوده، آرایه را ایجاد می‌کند. محدوده آرایه به وسیله دو عدد تعیین می‌شوند که با .. یا ... از هم جدا می‌شوند.
range = [1..5]
نتیجه‌ی کامپایل می‌شود:
var range;
range = [1, 2, 3, 4, 5];
در صورتی که محدوده‌ی آرایه بلافاصله بعد از یک متغیر بیاید CoffeeScript، کد نوشته شده را به تابع ()slice تبدیل می‌کند.
firstTwo = ["one", "two", "three"][0..1]
نتیجه کامپایل می‌شود:
var firstTwo;
firstTwo = ["one", "two", "three"].slice(0, 2);
در مثال بالا محدوده تعیین شده سبب می‌شود که یک آرایه جدید با دو عنصر "one" و "two" ایجاد شود. همچنین می‌توانید برای جایگزینی مقادیر جدید، در یک آرایه از قبل تعریف شده نیز از روش زیر استفاده کنید.
numbers = [0..9]
numbers[3..5] = [-3, -4, -5]
نکته: در صورتیکه متغیری قبل از تعریف محدوده آرایه قرار گیرد، اگر رشته باشد، نتیجه‌ی خروجی، آرایه‌ای از کاراکترهای آن می‌شود.
my = "my string"[0..2]
چک کردن وجود یک مقدار در آرایه، یکی از مشکلاتی است که در جاوااسکریپت وجود دارد (عدم پشتیبانی از ()indexOf در IE کمتر از 9). CoffeeScript با استفاده از کلمه‌ی کلیدی in این مشکل را برطرف کرده است.
words = ["Vahid", "Hamid", "Saeid", "Ali"]
alert "Stop" if "Hamid" in words
نکات مهم
  1. در صورت تعریف محدوده آرایه به صورت [..3]numbers (که آرایه numbers از قبل تعریف شده باشد)، خروجی، آرایه‌ای از مقادیر موجود در numbers را از خانه شماره 4 تا انتهای آن برمی گرداند.
  2. در صورت تعریف محدوده آرایه به صورت [..3-]numbers (که آرایه numbers از قبل تعریف شده باشد)، خروجی، آرایه‌ای از مقادیر موجود در numbers را از خانه انتهایی به میزان 3 خانه به سمت ابتدای آرایه برمیگرداند.
  3. در صورت عدم تعریف محدوده آرایه و فقط استفاده از [..] یا [...] (یک شکل عمل می‌کنند)، کل مقادیر آرایه اصلی (که از قبل تعریف شده باشد)، برگردانده می‌شود.
  4. تفاوت .. و ... در حالتی که دو عدد برای محدوده تعریف شود، در این است که ... آرایه به صورت عدد انتهایی - 1 تعریف می‌شود. مثلا [3...0] یعنی خانه‌های آرایه از 0 تا 2 را به عنوان خروجی برگردان.

Aliases

CoffeeScript شامل یک سری نام‌های مستعار است که برای خلاصه نویسی بیشتر بسیار مفید هستند. یکی از آن نام ها، @ است که به جای نوشتن this به کار می‌رود.
@name = "Vahid"
نتیجه کامپایل آن می‌شود:
this.name = "Vahid";
یکی دیگر از این نام ها، :: می‌باشد که به جای نوشتن prototype به کار می‌رود.
User::first = -> @records[0]
نتیجه کامپایل آن می‌شود:
User.prototype.first = function() {
  return this.records[0];
};
یکی از عمومی‌ترین شرط هایی که در جاوااسکریپت استفاده می‌شود، شرط چک کردن not null است. CoffeeScript این کار را با استفاده از ? انجام می‌دهد و در صورتی که متغیر برابر با null یا undefined نباشد، مقدار true را برمی گرداند. این ویژگی همانند ?nil در Ruby است.
alert "OK" if name?
نتیجه‌ی کامپایل آن می‌شود:
if (typeof name !== "undefined" && name !== null) {
  alert("OK");
}
از ? به جای || نیز می‌توانید استفاده کنید.
name = myName ? "-"
نتیجه‌ی کامپایل آن می‌شود:
var name;

name = typeof myName !== "undefined" && myName !== null ? myName : "-";
در صورتیکه بخواهید به یک property از یک شیء دسترسی داشته باشید و بخواهید null نبودن آن را قبل از دسترسی به آن چک کنید، می‌توانید از ? استفاده کنید.
user.getAddress()?.getStreetName()
نتیجه‌ی کامپایل آن می‌شود:
var ref;

if ((ref = user.getAddress()) != null) {
  ref.getStreetName();
}
همچنین در صورتیکه بخواهید چک کنید یک property در واقع یک تابع است یا نه (مثلا برای مواقعی که می‌خواهید callback بسازید) و سپس آن را فراخوانی کنید، نیز از ? می‌توانید استفاده کنید.
user.getAddress().getStreetName?()
و نتیجه‌ی کامپایل آن می‌شود:
var base;

if (typeof (base = user.getAddress()).getStreetName === "function") {
  base.getStreetName();
}
نظرات مطالب
ذخیره‌ی سوابق کامل تغییرات یک رکورد در یک فیلد توسط Entity framework Core
سلام خواهش می‌کنم، ببینید بستگی به سناریو و پروژه‌ی ما این مورد می‌تونه متغیر باشه طوری که برای پروژه‌های سبک با بار کم سناریوی بالا می‌تونه خیلی مفید و دم دستی باشه ولی برای پروژه‌های سنگین‌تر می‌تونیم از روش جدول جداگانه استفاده کنیم که هم نیاز به  عملیات Serialize/Deserialize نخواهیم داشت هم این جدول می‌تونه اصلا توی یک دیتابیس دیگ و روی یه سرور دیگ باشه. برای برنامه‌های خیلی سنگین هم میشه از سناریوهای پیشرفته‌تر مثل ذخیره در دیتابیس‌های غیر SQL Server مثل Elasticsearch و با روش‌های ایجاد صف و غیره استفاده کرد.
نظرات مطالب
بررسی خطاهای ممکن در حین راه اندازی اولیه برنامه‌های ASP.NET Core در IIS
- IIS APPPOOL\TestAppPool به عنوان یک User برای SQL Server تعریف شده و دسترسی کامل به دیتابیس مورد نظر براش قرار داده شده!
- همچنین Query دریافت اطلاعات جدول که لاگ میشود بدون مشکل در سرور اجرا می‌شود و در واقع اسکیمای بانک اطلاعاتی با دستور SQL مورد نظر یکی است!
نظرات مطالب
EF Code First #9
برای حالت TPH فقط باید این یک تعریف را داشته باشید public DbSet<Person> People { set; get; } و دسترسی به سایر مشتقات Person، از طریق کوئری‌های ()<db.People.OfType<Coach انجام می‌شود و نه ارجاع مستقیمی به DbSet ایی که نباید داشته باشند. وجود DbSet در Context، یعنی الزام به ساخت جدول معادل.
نظرات مطالب
Globalization در ASP.NET MVC - قسمت ششم
مهندس بک سوال؟
مشکلی نداره ما سه جدول:
1- جدولی برای ذخیره نام کالچرها
2-جدولی برای ذخیره عنوان کلیدهای اصلی
3- جدولی برای ذخیره مقادیر یک کالچر برای یک کلید خاص

تعریف کنیم؟
اگر درست فهمیده باشم فقط باید بخش بازیابی کلیدها تغییر کنه درسته؟
مطالب
تعامل و انتقال اطلاعات بین کامپوننت‌ها در Angular – بخش دوم
در قسمت قبل نحوه انتقال اطلاعات از کامپونت پدر به فرزند را از طریق متادیتای Input@ برسی کردیم. در اینجا نکات تکمیلی را مورد بحث قرار خواهیم داد.
همانطور که قبلا مشاهده کردید، نام متغیر تعریف شده در کامپوننت فرزند (FormIsReadOnly) به عنوان یک خصوصیت در هنگام استفاده از کامپوننت ظاهر شده و عمل انقیاد از طریق این خصوصیت FormIsReadOnly صورت می‌گیرد. در صورتیکه قصد دارید نام خصوصیت ظاهر شده در کامپوننت، با نام متغیر تعریف شده در کامپوننت فرزند متفاوت باشد، به شکل زیر عمل کنید. 
@Input('readOnly') FormIsReadOnly: boolean;
در این حالت عمل انقیاد از طریق خصوصیتی با نام readOnly صورت خواهد گرفت.

ردیابی تغییرات اعمال شده بر روی خصوصیت 

در برخی موارد لازم است بعد از انتساب مقداری از سمت کامپوننت پدر به کامپوننت فرزند و قبل از استفاده کامپوننت فرزند از آن مقدار، تغییرات یا اعتبار سنجی بر روی مقدار منتسب شده اعمال کنیم. مثلا فرض کنید کامپوننتی را به نام LeftSideMenu تعریف کرده‌اید که باز بودن یا بسته بودن آن توسط کامپوننت پدر تنظیم میشود. در اینجا لازم است همواره منتظر تغییر این خصوصیت از سمت کامپوننت پدر بود تا بلافاصله بعد از تنظیم این خصوصیت، کامپوننت فرزند نسبت به باز شدن یا بسته ماندن، عکس العمل نشان داده و بلافاصله تغییرات را اعمال کند (منو را باز کند یا ببندد). لازمه این کار ردیابی تغییرات اعمال شده از سمت کامپوننت پدر می‌باشد تا به محض تغییر، اصلاحات یا اعتبار سنجی‌های لازم بر روی آن اعمال شود. برای این کار دو راه حل وجود خواهد داشت. 
  1. ردیابی تغییرات صورت گرفته از طریق تنظیم setter به متغیر تعریف شده با متادیتای Input@
  2. پیاده سازی onChanges توسط کامپوننت فرزند جهت ردیابی تغییرات کامپوننت 


ردیابی تغییرات از طریق تنظیم setter

همانطور که گفته شد استفاده از کامپوننت فرزند به شکل زیر:
<app-customer-info FormIsReadOnly="true"></app-customer-info>
باعث خواهد شد مقدار انتساب یافته به FormIsReadOnly از جنس رشته‌ای باشد (یعنی "true"). در اینجا می‌خواهیم قبلا از اینکه مقدار، از طریق کامپوننت پدر به فرزند مقدار دهی شود، برسی کنیم در صورتیکه مقدار انتسابی از جنس boolean نبود، خطایی را صادر و برنامه نویس را برای این استفاده نادرست از کامپوننت هشیار کنیم: 
@Component({
    selector: 'app-customer-info',
    templateUrl: './customer-info.component.html',
    styleUrls: ['./customer-info.component.css']
})
export class CustomerInfoComponent implements OnInit {
    private _formIsReadOnly: boolean;

    @Input()
    set FormIsReadOnly(value: boolean) {
        if (typeof (value) != 'boolean')
            throw new Error(`${value} type is not boolean.`);
        this._formIsReadOnly = value;
    }

    get FormIsReadOnly(): boolean { return this._formIsReadOnly; }

    constructor() { }

    ngOnInit() {
    }
}
با تنظیم setter بر روی متغیر FormIsReadOnly، لازمه‌ی تمامی تغییرات بر روی این متغیر، اجرای آن setter خواهد بود. در اینجا برسی کردیم در صورتیکه نوع مقدار (typeof(value)) از جنس boolean نباشد، خطایی صادر شود. 


پیاده سازی onChanges توسط کامپوننت فرزند جهت ردیابی تغییرات کامپوننت 

یکی دیگر از راه‌های تشخیص تغییرات اعمال شده بر روی کامپوننت، پیاده سازی اینترفیس onChanges توسط کامپوننت و پیاده سازی متد تعریف شده در این اینترفیس به نام ngOnChanges می‌باشد. 
import { Component, OnInit, Input, OnChanges, SimpleChange } from '@angular/core';
import { ICustomerInfo } from '../../core/model/ICustomerInfo';

@Component({
    selector: 'app-customer-info',
    template: '<ul>< li *ngFor="let change of changeLog">{{change }}</li></ul>',
    styleUrls: ['./customer-info.component.css']
})
export class CustomerInfoComponent implements OnChanges {
    changeLog: string[] = [];

    @Input() FormIsReadOnly: boolean;

    ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
        let log: string[] = [];
        for (let propName in changes) {
            let changedProp = changes[propName];
            let to = JSON.stringify(changedProp.currentValue);
            if (changedProp.isFirstChange()) {
                log.push(`Initial value of ${propName} set to ${to}`);
            } else {
                let from = JSON.stringify(changedProp.previousValue);
                log.push(`${propName} changed from ${from} to ${to}`);
            }
        }
        this.changeLog.push(log.join(', '));
    }

    constructor() { }
}
این کامپوننت تمامی تغییرات اعمال شده بر روی FormIsReadOnly را ردیابی کرده و نمایش خواهد داد. نمونه خروجی به شکل زیر خواهد بود. 
•    Initial value of FormIsReadOnly set to true
•    FormIsReadOnly changed from true to "trued"
•    FormIsReadOnly changed from "trued" to "true"
•    FormIsReadOnly changed from "true" to "truef"
•    FormIsReadOnly changed from "truef" to "true"
•    FormIsReadOnly changed from "true" to "tru"
•    FormIsReadOnly changed from "tru" to "tr"
•    FormIsReadOnly changed from "tr" to "t"
•    FormIsReadOnly changed from "t" to ""
•    FormIsReadOnly changed from "" to "t"
•    FormIsReadOnly changed from "t" to "tr"
•    FormIsReadOnly changed from "tr" to "tru"
•    FormIsReadOnly changed from "tru" to "true"

در ادامه عنوان «به‌جریان انداختن رخدادها از کامپوننت فرزند و گرفتن آن‌ها را از طریق کامپوننت پدر» را مورد برسی قرار خواهیم داد.


ادامه دارد/