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

خیر از Httpget و NonAction استفاده نکردم ولی از ChildActionOnly بالای اکشنها استفاده کردم
در کنترلر به صورت زیر اکشنها رو نوشتم:
        [Authorize(Roles = "Administrator")]
        public ActionResult AddUserRole()
        {
            return View();
        }
       
         //////////////////////////////////////////////////////////

        //اضافه کردن نقش////////////
        [Authorize(Roles = "Administrator")]
        [HttpGet]
        [ChildActionOnly]
        
        public ActionResult AddRole()
        {
          
            return PartialView("PartialRole");
        }

        [ChildActionOnly]
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult AddRole(tblRole rl, string submitValue)
        {
            if (submitValue == "ذخیره نقش")
            {
                //تبدیل تاریخ میلادی به شمسی
                rl.RoleDateCreate = Utility.ToPersianDate(DateTime.Now);

                UserRepository user = new UserRepository();
                //برای وارد کردن خودکار شناسه کاربر وارد شده
                rl.UserIDCreate_FK = Convert.ToInt64(user.FindUserID(User.Identity.Name));

                if (ModelState.IsValid)
                {
                    RoleRepository acr = new RoleRepository();
                    var model = acr.Add(rl);
                    ViewBag.Message = "نقش با موفقیت ثبت شد";
                    ModelState.Clear();
                }
            }
            else
            {
                ModelState.Clear();
            }
            return PartialView("PartialRole", rl);

        }
        //////////////////////////////////////////////////////////

        //اضافه کردن کاربر//////////
        [Authorize(Roles = "Administrator")]
        [HttpGet]
        [ChildActionOnly]
       
        public ActionResult AddUser()
        {
            return PartialView("PartialUser");
        }
        
        [ChildActionOnly]
        [HttpPost]
     
        public ActionResult AddUser(tblUser rl, string submitValue)
        {
            if (submitValue == "ثبت نام")
            {
                if (ur.ExistUserName(rl.UserName))//برای اینکه نام کاربری تکراری نباشد
                {
                    ViewBag.Warning = "این نام کاربری تکراری میباشد";
                 
                }
                else
                {
                   
                    //برای وارد کردن خودکار شناسه کاربر وارد شده
                    rl.UserIDCreate = Convert.ToInt64(ur.FindUserID(User.Identity.Name));
                   
                    //تبدیل میلادی به شمسی
                    rl.UserDateCreate = Utility.ToPersianDate(DateTime.Now);

                    if (ModelState.IsValid)
                    {
                        UserRepository acr = new UserRepository();
                        var model = acr.Add(rl);
                        ViewBag.Message = "کاربر با موفقیت ثبت شد";
                        ModelState.Clear();
                    }
                }
            }
            else
            {
                ModelState.Clear();
            }
            return PartialView("PartialUser", rl);

        }

        //////////////////////////////////////////////////////////

        // دادن نقش به کاربر///////

        [ChildActionOnly]
        
        [HttpGet]
        [Authorize(Roles = "Administrator")]
        public ActionResult RoleOfUser()
        {
            var blUser = new UserRepository();
            var blRole = new RoleRepository();
            var model = new RoleUserViewModel();

            model.username = blUser.Select().ToList();
            model.rolename = blRole.Select().ToList();
           
            return PartialView("PartialRoleOfUser", model);
        }

        [ChildActionOnly]
        [HttpPost]      
        public ActionResult RoleOfUser(tblRoleUser rl, string submitValue)
        {
            if (submitValue == "ذخیره نقش کاربر")
            {
                //برای وارد کردن خودکار شناسه کاربر وارد شده
                rl.UserIDCreate_FK = Convert.ToInt64(ur.FindUserID(User.Identity.Name));

                //تبدیل میلادی به شمسی
                rl.RoleUserDateCreate = Utility.ToPersianDate(DateTime.Now);

                if (ModelState.IsValid)
                {
                    RoleUserRepository acr = new RoleUserRepository();
                    var model = acr.Add(rl);
                    ViewBag.Message = "نقش کاربر با موفقیت ثبت شد";

                    ModelState.Clear();
                }
            }
            else
            {
                ModelState.Clear();
            }
            return PartialView("PartialRoleOfUser", rl);

        }
در پارشیال ویو Role
@model MeterControl.ViewModels.RoleUserViewModel
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

@using (Ajax.BeginForm("AddUserRole", "Home", new AjaxOptions { HttpMethod = "Post", Url = "/Home/AddUserRole" })){ 
    <fieldset>
        <legend>tblRole</legend>

        

        <div>
            @Html.LabelFor(model => model.role.RoleName)
        </div>
        <div>
            @Html.EditorFor(model => model.role.RoleName)
            @Html.ValidationMessageFor(model => model.role.RoleName)
        </div>

       

        @ViewBag.Message
        <p>
            <input type="submit" value="ذخیره نقش" name="submitValue" />
        </p>
    </fieldset>
}

و در ویو کلی هم به صورت زیر اکشنها صدا زده شده اند:
@{
    ViewBag.Title = "AddUserRole";
}

<h2>AddUserRole</h2>
<div>
    <div>
        @Html.Action("AddRole", "Home")
    </div>
    <div>
        @Html.Action("AddUser", "Home")
        @Html.Partial("PartialUser")
    </div>
    <div>
        @Html.Action("RoleOfUser","Home")
    </div>
</div>

به خط زیر خطا میدهد
@Html.Action("AddRole", "Home")
اشتراک‌ها
بازنویسی ng-cordova با پشتیبانی از ES6 و Typescript

ng-cordova با ترکیب انگولار جی اس و کوردوا به منظور پیاده سازی خواص بومی بستر‌های مختلف موبایل برای فریم ورک آیونیک بکارگرفته می‌شود.حال با آمدن ES6 دوباره باز نویسی شده است و از تایپ اسکریپت به صورت کامل پشتیبانی می‌کند.

بازنویسی  ng-cordova با پشتیبانی از ES6 و Typescript
نظرات مطالب
Angular CLI - قسمت اول - نصب و راه اندازی
یک نکته‌ی تکمیلی: پشتیبانی توکار از به روز رسانی بسته‌های Angular
به همراه Angular CLI نگارش 1.7، دستور جدیدی به نام ng update اضافه شده‌است که تمام وابستگی‌های مرتبط به Angular را به صورت خودکار به روز رسانی می‌کند.
نظرات مطالب
مسیریابی در Angular - قسمت اول - معرفی
صرفا جهت اطلاع خودم سوالی داشتم ، در این حالت با استفاده از command‌های در دسترس مثل ng serve -o قادر به اجرای پروژه هستیم . اگر بعد از اتمام پروژه قصد داشته باشیم فایل index.html را مستقیم open in browser بزنیم ، در این حالت باید به صورت دستی کتابخانه‌های و تنظیمات ابتدایی همانند قسمت اول درج شود ؟
نظرات مطالب
فراخوانی یک تابع بعد از اتمام Render در AngularJS
در Directive ng-include علاوه بر بحث Render شما بحث اتمام بارگیری محتواها را هم دارید که برای این کار یعنی تشخیص اتمام بارگیری محتوا باید از دستور زیر استفاده نمایید.
$scope.$on('$includeContentLoaded', function () {        
        //دستورات بعد از اتمام بارگیری محتوا
    });
بعد از این مرحله هست که ngFinishRender وارد صحنه می‌شود. :)
نظرات مطالب
خواندن اطلاعات از سرور و نمایش آن توسط Angular در ASP.NET MVC
در روش بالا اگر چه اطلاعات از سرور دریافت میشود اما حالتی استاتیک دارد چون لیست محصولات در زمان رندر شدن صفحه تولید و در ng-init قرار می‌گیرد . و در نتیجه برای بروزکردن حتما باید صفحه مجددا درخواست بشود (رفرش). این روش برای لیست هایی که تغییراتی ندارند یا به ندرت تغییر می‌کنند مناسب است.
و بله یک راه برای حل این مشکل استفاده از سرویس http می‌تواند باشد .
مطالب
جزئیات برنامه نویسی افزونه فارسی به پارسی

این افزونه با استفاده از ابزار Visual Studio Tools for Office که به VSTO مشهور شده است، تهیه شد. در بسته به روز رسانی سیستم که در ذیل (معرفی افزونه) نیز معرفی شد نگارش sp1 vsto3.0 آن به صورت خودکار نصب خواهد شد.
برای ایجاد این پروژه در VS.Net 2008 ، تنها کافی است یک پروژه جدید Word add-in را آغاز نمائیم. (شکل زیر)





قبل از ادامه بحث، بهتر است در مورد بانک اطلاعاتی مورد استفاده نیز توضیح داده شود. در اینجا از SQLite استفاده شد. (بسیار سبک، کم حجم و سریع است و اساسا یک کاربر نهایی برای تنظیمات آن نیازی نیست اطلاعاتی داشته باشد). بسته به روز رسانی سیستم (در مطلب قبلی)، این مورد را نیز به صورت خودکار نصب خواهد کرد (در GAC باید نصب شود وگرنه افزونه قادر به یافتن آن نخواهد شد).
برای ایجاد این بانک اطلاعاتی، از افزونه SQLite manager برای فایرفاکس استفاده شد. (این افزونه رایگان شما را از هر ابزار جانبی برای مدیریت یک بانک اطلاعاتی SQLite بی‌نیاز می‌کند)
برای مثال فایل ErrorsBank.sqlite برنامه افزونه فارسی به پارسی را توسط افزونه SQLite manager فایرفاکس باز کنید (این فایل را در محل نصب افزونه می‌توانید پیدا کنید). در اینجا می‌توان جداول جدید را ایجاد کرد، کوئری‌های دلخواه را اجرا نمود و یا اطلاعات را مرور کرده، حذف یا ویرایش کرد (شکل زیر).




و خوشبختانه این بانک اطلاعاتی و محصور کننده‌های آن با اطلاعات یونیکد فارسی هیچ مشکلی ندارند و برای کارهایی با وسعت کم و تعداد رکورد پائین یکی از بهترین انتخاب‌ها به‌شمار می‌روند.
نحوه استفاده از SQLite نیز در دات نت بسیار ساده است. اگر با ADO.Net کار کرده باشید، پس از افزودن ارجاعی از اسمبلی System.Data.SQLite.DLL به پروژه و معرفی فضای نام آن به پروژه، تنها کافی است در کدهای قبلی خود برای مثال SqlConnection را به SQLiteConnectionتغییر دهید و امثال آن. یعنی دانش ADO.Net شما در اینجا نیز کاملا قابل استفاده خواهد بود و نیازی نیست مدتی را صرف آشنا شدن با کلاس‌ها و مفاهیم جدید نمائید (البته این تنها زمانی معنا خواهد داشت که به ویزاردها عادت نکرده باشید و کارهای خود را با کد نویسی انجام داده باشید).
تنها یک نکته را باید به‌خاطر داشت و آن هم مربوط است به ساز و کار درونی SQLite . هنگام انجام عملیات update یا insert حتما از transaction استفاده کنید تا سرعت کوئری‌های شما در SQLite به نحو شگفت انگیزی افزایش یابد. مثالی در این مورد را در فایل chm راهنمای SQLite.NET می‌توانید پیدا کنید.

مطلب دیگری که پیش از پرداختن به کد نویسی افزونه باید با آن آشنا شویم، مفهوم smart tags در مجموعه آفیس است که در این پروژه از آن استفاده گردید.
smart tags در مجموعه آفیس برچسب‌هایی هستند که به صورت خودکار توسط یکی از محصولات آفیس مثلا ورد یا اکسل و امثال آن، پس از تشخیص یک کلمه خاص ایجاد می‌شوند و می‌توان اعمالی را به این برچسب ایجاد شده انتساب داد. برای مثال در اینجا امکان جایگزین کردن کلمه فارسی با معادل پارسی در نظر گرفته شد.
ویدیویی در مورد نحوه ایجاد اسمارت تگ‌ها در VS.Net و یا مثالی پیشرفته‌تر در مورد تشخیص دمای فارنهایت در یک متن و ایجاد smart tag مخصوص به آن برای تبدیل به سلسیوس. (از regular expressions جهت یافتن یک الگو در متن استفاده شده است)

در این پروژه، حدود 3800 واژه فارسی به‌ یک smart tag انتساب داده می‌شود (در روال استاندارد ThisAddIn_Startup). سپس در هنگام نمایش آن، معادل پارسی کلمه نیز به منوی باز شده افزوده گشته و در روال رخداد کلیک آن، تعویض کلمه تشخیص داده شده با واژه پیدا شده صورت خواهد گرفت.

در ادامه فرض بر این است که یک پروژه جدید word add-in را در VS.Net ایجاد کرده‌اید و همچنین ارجاعی را به فایل System.Data.SQLite.DLL افزوده‌اید.

using System;
using System.Diagnostics;
using Microsoft.Office.Tools.Word;
using Action = Microsoft.Office.Tools.Word.Action;

private SmartTag _st;
private void init()
{
try
{
//Enable Smart Tags in Word
if (!Application.Options.LabelSmartTags)
{
//ممکن است اسمارت تگ‌ها در ورد غیرفعال باشند. به این صورت می‌شود آنها را فعال کرد
Application.Options.LabelSmartTags = true;
}

_st = new SmartTag(@"www.microsoft.com/Demo#FarsiSmartTag", @"فارسی به پارسی");

//دریافت واژه‌های فارسی از دیتابیس و افزودن خودکار آنها به اسمارت تگ‌ها
if (!DBhelper.AddSmartTagItems(_st, "select distinct farsi from tblFarsiToParsi")) return;

Action stActions = new Action("تبدیل");//تعریف یک اکشن جدید
stActions.Click += stActions_Click;//انتساب روال‌های رخداد گردان
stActions.BeforeCaptionShow += stActions_BeforeCaptionShow;
_st.Actions = new[] { stActions };
VstoSmartTags.Add(_st);//افزودن اسمارت تگ به مجموعه
}
catch (Exception ex)
{
EventLog.WriteEntry("FarsiToParsi", ex.ToString(), EventLogEntryType.Error, 7);
}
}

private void ThisAddIn_Startup(object sender, EventArgs e)
{
init();
}

دو روال رخداد گردان زیر نیز جهت تغییر عنوان پیش فرض به واژه یافته شده در لحظه نمایش منو و روال کلیک نیز ایجاد خواهد شد:

static void stActions_BeforeCaptionShow(object sender, ActionEventArgs e)
{
try
{
Action clickedAction = sender as Action;
if (clickedAction != null)
{
string parsi = DBhelper.FindParsi(e.Text);//معادل پارسی از دیتابیس دریافت می‌شود
clickedAction.Caption = (parsi == string.Empty ? e.Text : parsi);
}
}
catch (Exception ex)
{
EventLog.WriteEntry("FarsiToParsi", ex.ToString(), EventLogEntryType.Error, 7);
}
}

static void stActions_Click(object sender, ActionEventArgs e)
{
try
{
Action clickedAction = sender as Action;
if (clickedAction != null)
{
e.Range.Text = clickedAction.Caption;//جایگزینی متن موجود با عنوانی که پیشتر پارسی شده است
}
}
catch (Exception ex)
{
EventLog.WriteEntry("FarsiToParsi", ex.ToString(), EventLogEntryType.Error, 7);
}
}

نکته‌ای را که در اینجا باید حتما رعایت کرد بحث exception handling‌ است. خصوصا در روال استاندارد ThisAddIn_Startup . اگر در این روال خطایی مدیریت نشده رخ دهد، word افزودنی شما را به صورت غیرفعال به مجموعه اضافه خواهد کرد و فعال سازی بعدی آن پس از اصلاح کد واقعا مشکل خواهد بود. همانطور که ملاحظه می‌کنید تمامی خطاها در event log‌ ویندوز نوشته می‌شوند.
همچنین باید دقت داشت که اگر متغیری در سطح کلاس تعریف نشود به احتمال زیاد تا دقایقی بعد توسط garbage collector به دیار باقی خواهد شتافت (تعریف st_ در اینجا). اینجاست که شاید ساعت‌ها وقت صرف کنید که چرا روال‌های رخ‌داد گردان دیگر اجرا نمی‌شوند. چرا افزونه دیگر کار نمی‌کند.

همین! کل سورس این add-in منهای بحث دریافت اطلاعات از دیتابیس همین بود! وظیفه‌ی تشخیص کلمات معرفی شده به ms-word به‌عهده‌ی خود آن است و این‌کار را نیز به‌خوبی انجام می‌دهد. در گذشته‌های نچندان دور ایجاد یک افزونه برای word واقعا مشکل بود که با این روش بسیاری از موانع برطرف شده است.

کلاس DBHelper که کار دریافت اطلاعات واژه‌ها را از دیتابیس SQLite انجام می‌دهد به شرح زیر است:

using System;
using System.Data.SQLite;
using System.Diagnostics;
using System.Reflection;
using Microsoft.Office.Tools.Word;

namespace Farsi2Parsi
{
class DBhelper
{
#region Methods (2)

// Public Methods (2)

public static bool AddSmartTagItems(SmartTag st, string strSQL)
{
SQLiteDataReader myReader = null;
SQLiteCommand sqlCmd = null;
bool ret = false;
try
{
SQLiteConnection sqlCon = new SQLiteConnection
{
ConnectionString = "Data Source=" + ConStr.ConnectionString
};
sqlCon.Open();
sqlCmd = new SQLiteCommand(strSQL, sqlCon);
myReader = sqlCmd.ExecuteReader();

if (myReader != null)
while (myReader.Read())
{
if (myReader.GetValue(0) != DBNull.Value)
st.Terms.Add(myReader.GetValue(0).ToString());
}

ret = true;
}
catch (Exception ex)
{
EventLog.WriteEntry("FarsiToParsi", ex + "\n" + Environment.CurrentDirectory + "\n" +
Assembly.GetExecutingAssembly().Location, EventLogEntryType.Error, 7);
}
finally
{
if (myReader != null)
myReader.Close();

if (sqlCmd != null)
sqlCmd.Connection.Close();
}
return ret;
}

public static string FindParsi(string farsi)
{
SQLiteDataReader myReader = null;
SQLiteCommand sqlCmd = null;
string ret = string.Empty;
string strSQL = "select parsi from tblFarsiToParsi where farsi='" + farsi.Replace("'", "''") + "'";
try
{
SQLiteConnection sqlCon = new SQLiteConnection
{
ConnectionString = "Data Source=" + ConStr.ConnectionString
};
sqlCon.Open();
sqlCmd = new SQLiteCommand(strSQL, sqlCon);
myReader = sqlCmd.ExecuteReader();

if (myReader != null)
{
myReader.Read(); //اولین مورد کافی است
if (myReader.GetValue(0) != DBNull.Value)
ret = myReader.GetValue(0).ToString();
}
}
catch (Exception ex)
{
EventLog.WriteEntry("FarsiToParsi", ex + "\n" + Environment.CurrentDirectory + "\n" +
Assembly.GetExecutingAssembly().Location, EventLogEntryType.Error, 8);
}
finally
{
if (myReader != null)
myReader.Close();

if (sqlCmd != null)
sqlCmd.Connection.Close();
}
return ret;
}
#endregion Methods
}
}

همانطور که پیشتر نیز عنوان شد اگر با ADO.net آشنایی داشته باشید، هیچ نکته‌ی خاص جدیدی را در اینجا مشاهده نخواهید کرد و تنها یک سری امور روزمره کاری با ADO.net مطرح شده است، باز کردن کانکشن، اجرای کوئری، دریافت اطلاعات و پاکسازی نهایی. (قسمت finally را با استفاده از عبارت using می‌شود حذف کرد)

هنگام نصب برنامه، مسیر پوشه نصب در رجیستری ویندوز توسط نصاب نوشته خواهد شد. از همین مورد برای ایجاد رشته اتصالی به دیتابیس استفاده گردید.

class ConStr
{
public static string ConnectionString
{
get
{
return Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\FarsiToParsi").GetValue("folder") + "\\ErrorsBank.sqlite";
}
}
}

سورس کامل این افزونه را به صورت یک پروژه VS.Net 2008 SP1 از اینجا می‌توانید دریافت کنید.
نصاب برنامه با استفاده از NSIS ایجاد شده که در روزی دیگر درباره‌ی آن توضیح خواهم داد.
اگر قصد داشته باشید از روش‌های متداول استفاده کنید، مشاهده ویدیوی زیر توصیه می‌شود:
http://msdn.microsoft.com/en-us/office/bb851702.aspx

برای توزیع این نوع افزونه‌ها علاوه بر دات نت فریم ورک، به چهار به روز رسانی دیگر نیز نیاز خواهد بود:
به روز رسانی نصاب ویندوز (که احتمالا نصب هست)
WindowsInstaller-KB893803-v2-x86.exe
Microsoft Office System Update: Redistributable Primary Interop Assemblies :
o2007pia.msi
نصب vsto و همچنین sp1 آن
vstor30.exe
vstor30sp1-KB949258-x86.exe

این موارد را من در بسته به روز رسانی سیستم قرار داده‌ام که به صورت خودکار و یکی پس از دیگری اجرا و نصب خواهند شد.
پس از آن با کلیک بر روی فایلی با پسوند vsto که در پوشه build برنامه موجود است، می‌توان افزونه را نصب کرد (click once installation).




سایر اطلاعات در مورد پروژه‌های VSTO را می‌توان از طریق وبلاگ رسمی آنها دنبال کرد:
http://blogs.msdn.com/vsto/

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


نظرات مطالب
ASP.NET MVC #12
سلام آقای نصیری
با تشکر از توضیحاتتون
من کد زیر رو که خودتون واسه شمسی کردن تاریخ گذاشته بودین، به نام datetime.cshtml و صورت PartialView داخل پوشه DisplayTemplates قرار دادم. اما همچنان DateTime‌ها رو به فرمت میلادی نمایش میده. میشه بگین اشکال کارم کجاست ؟
@using System.Globalization
@model Nullable<DateTime>
           
@helper ShamsiDateTime(DateTime info, string separator = "/", bool includeHourMinute = true)
{
    int ym = info.Year;
    int mm = info.Month;
    int dm = info.Day;
    var sss = new PersianCalendar();
    int ys = sss.GetYear(new DateTime(ym, mm, dm, new GregorianCalendar()));
    int ms = sss.GetMonth(new DateTime(ym, mm, dm, new GregorianCalendar()));
    int ds = sss.GetDayOfMonth(new DateTime(ym, mm, dm, new GregorianCalendar()));    
    if (includeHourMinute)
    {
        @(ys + separator + ms.ToString("00") + separator + ds.ToString("00") + " " + info.Hour + ":" + info.Minute)
    }
    else
    {
        @(ys + separator + ms.ToString("00") + separator + ds.ToString("00"))
    }
}

@if (@Model.HasValue)
{
  @ShamsiDateTime(@Model.Value , separator: "/", includeHourMinute: false)
}

نظرات مطالب
احراز هویت و اعتبارسنجی کاربران در برنامه‌های Angular - قسمت ششم - کار با منابع محافظت شده‌ی سمت سرور
یک نکته‌ی تکمیلی: بهبود کنترل نمایش و مخفی سازی قسمت‌های مختلف

یک روش «نمایش و یا مخفی کردن قسمت‌های مختلف صفحه بر اساس نقش‌های کاربر وارد شده‌ی به سیستم» را در مطلب جاری مطالعه کردید. روش دیگر اینکار، تهیه‌ی یک دایرکتیو و سپس اعمال آن به المان‌های مختلف صفحه است. به علاوه با توجه به اینکه Auth Service ما رخ‌داد خروج کاربر را نیز گزارش می‌کند، روش ارائه شده‌ی در اینجا نیاز به اندکی بهبود هم دارد:
  ngOnInit() {
    this.isAdmin = this.authService.isAuthUserInRole("Admin");
    this.isUser = this.authService.isAuthUserInRole("User");
  }
نتیجه‌ی این بررسی، حتی با خروج کاربر نیز تغییری نخواهد کرد و ثابت است. بنابراین بهتر است مشترک this.authService.authStatus شد و نسبت به رخ‌دادهای صادر شده‌ی توسط سرویس اعتبارسنجی، همانند کامپوننت هدر، واکنش نشان داد.
برای پیاده سازی آن و همچنین کپسوله سازی این عملیات تکراری، دایرکتیو جدیدی را در مسیر src\app\shared\directives\is-visible-for-auth-user.directive.ts ایجاد می‌کنیم:
import { Directive, ElementRef, Input, OnDestroy, OnInit } from "@angular/core";
import { Subscription } from "rxjs/Subscription";

import { AuthService } from "../../core/services/auth.service";

@Directive({
  selector: "[isVisibleForAuthUser]"
})
export class IsVisibleForAuthUserDirective implements OnInit, OnDestroy {

  private subscription: Subscription;

  @Input() isVisibleForRoles: string[];

  constructor(private elem: ElementRef, private authService: AuthService) { }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }

  ngOnInit(): void {
    this.subscription = this.authService.authStatus$.subscribe(status => this.changeVisibility(status));
    this.changeVisibility(this.authService.isAuthUserLoggedIn());
  }

  private changeVisibility(status: boolean) {
    const isInRoles = !this.isVisibleForRoles ? true : this.authService.isAuthUserInRoles(this.isVisibleForRoles);
    this.elem.nativeElement.style.display = isInRoles && status ? "" : "none";
  }
}
در اینجا علاوه بر بررسی isAuthUserLoggedIn و isAuthUserInRoles، نسبت به تغییرات this.authService.authStatus نیز واکنش نشان داده می‌شود.

سپس تعریف آن‌را به قسمت‌های declarations و exports مربوط به SharedModule اضافه می‌کنیم:
import { IsVisibleForAuthUserDirective } from "./directives/is-visible-for-auth-user.directive";

@NgModule({
  declarations: [
    IsVisibleForAuthUserDirective
  ],
  exports: [
    IsVisibleForAuthUserDirective
  ]
})
export class SharedModule {}

اکنون ماژول Dashboard برای استفاده‌ی از این امکانات تنها کافی است SharedModule را دریافت کند (یا هر ماژول دیگری نیز به همین ترتیب است):
import { SharedModule } from "../shared/shared.module";

@NgModule({
  imports: [
    SharedModule
  ]
})
export class DashboardModule { }

پس از آن برای مخفی سازی یک المان از دید کاربران وارد نشده‌ی به سیستم، فقط کافی است دایرکتیو isVisibleForAuthUser را به المان اعمال کنیم:
<div class="alert alert-info" isVisibleForAuthUser>
      Is-Visible-For-AuthUser
</div>
و یا اگر نیاز به اعمال نقش‌ها نیز وجود داشت می‌توان از خاصیت isVisibleForRoles آن استفاده کرد:
<div class="alert alert-success" isVisibleForAuthUser [isVisibleForRoles]="['Admin','User']">
      Is-Visible-For-Roles = ['Admin','User']
</div>

خلاصه‌ی این تغییرات به کدهای نهایی این سری اعمال شده‌اند.
مطالب
الگوی PRG در ASP.NET MVC
تا حالا با این پنجره حتما مواجه شدین:


دارید اطلاعات یک فرم داخل صفحه رو به سمت سرور میفرستید و پس از اتمام عملیات، صفحه دوباره نمایش داده میشه. در این حالت اگه دکمه F5 یا دکمه Refresh مرور گر رو بزنید، با این پنجره مواجه میشید که میگه دارید اطلاعات قبلی رو دوباره به سمت سرور میفرستید. بعضی وقت‌ها کاربران به هر دلیل دوباره صفحه رو Refresh میکنند و با این پنجره روبرو میشن بدون اینکه بدونن جریان از چه قراره، دوباره اطلاعات رو به سمت سرور میفرستن و این کار باعث ثبت اطلاعات تکراری میشه. برای جلوگیری از این کار الگویی به نام Post/Redirect/Get هست که راه حلی رو برای اینکار پیشنهاد میده. 

راه حل به این صورت هست که پس از پست شدن فرم به سمت سرور و انجام عملیات، بجای اینکه صفحه، دوباره با استفاده از متد GET به کاربر نشون داده بشه، کاربر Redirect بشه به صفحه. برای توضیح این مسئله به سراغ AccountController که بصورت پش فرض وقتی یک پروژه ASP.NET MVC رو از نوع Internet ایجاد میکنید، وجود داره. 

 اکشن Register از نوع GET صفحه ثبت نام کاربر رو نمایش میده. 
[HttpGet]
[AllowAnonymous]
public ActionResult Register()
{
      return View();
}
پس از اینکه کاربر اطلاعات داخل فرم رو پر کرد و به سمت سرور فرستاد و صحت اطلاعات فرستاده معتبر و عمل ثبت موفقیت آمیز بود برای ادامه کار به دو روش میتوان عمل کرد:
 ١- کاربر به صفحه دیگری منتقل بشه و در اون صفحه پیام موفقیت آمیز بودن عملیات نشون داده بشه. مثلا معمولا پس از انجام عمل ثبت نام، کاربر به صفحه شخصی یا صفحه اصلی سایت منتقل میشه  و یا در موقع ویرایش اطلاعات پش از انجام عمل ویرایش کاربر به صفحه دیگری که لیستی از آیتمها که کاربر یکی از آنها را ویرایش کرده باز گردانده میشه.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        try
        {
            WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
            WebSecurity.Login(model.UserName, model.Password);
            ViewBag.Message = "Successfully Registered!";

            // PRG has been maintained
            return RedirectToAction("Index", "Home");
        }
        catch (MembershipCreateUserException e)
        {
            ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}
٢- نمایش دوباره صفحه ولی با تغییر هدر صفحه به کد 303 . کد 303 به مروگر اعلام میکنه صفحه ریدایرکت شده است
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        try
        {
            WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
            WebSecurity.Login(model.UserName, model.Password);
            ViewBag.Message = "Successfully Registered!";

            // PRG has been maintained
            return RedirectToAction("Register");
        }
        catch (MembershipCreateUserException e)
        {
            ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}
در این حالت دوباره صفحه ثبت نام نمایش داده میشه ولی با زدن دکمه رفرش، اطلاعات دوباره به سمت سرور فرستاده نمیشه