آموزش ساخت ToolTip با استفاده از Web API در MVC

در این مقاله، روش های داینامیک افزودن جعبه راهنما در کنار عناصری مثل textbox، label، پاراگراف، کلید و ...، آموزش داده می شود. این جعبه راهنما، زمانی که کاربر روی آیکون راهنما، کلیک کند توسط jQuery UI modal یا Bootstrap popover باز می شود. تمامی متن های راهنما ها در یک مکان نگه داری می شود و با کلیک روی هر آیکون راهنما، متن مربوط به آن راهنما به کمک Web API دریافت می شود و به کاربر نمایش داده می شود.

آموزش ساخت ToolTip با استفاده از Web API در MVC

مقدمه

ایده کلی مثالی که در این آموزش ساخته می شود از موارد ذکر شده در پایین، الهام گرفته شده است.

1- اکثر راهنما ها خیلی زود ناپدید می شوند، مثلا بعد از اینکه کاربر شروع به تایپ می کند یا مثلا کاربر وارد عنصر بعدی می شود، راهنمای نمایش داده شده، ناپدید می شود. ما دنبال یک راهنما هستیم که کاربر برای خواندن متن داخل راهنما، زمان کافی داشته باشد و به راحتی بتواند اطلاعات لازم دیگر را بررسی کند. اگر از jQuery UI Modal استفاده کنیم، کاربر قادر خواهد بود راهنمای نمایش داده شده را در صفحه به راحتی جابجا کند و هر وقت نیاز داشت با زدن کلید ضربدر، این راهنما را ببندد.

2- محتویات راهنما باید از داخل دیتابیس به کمک WebAPI بارگزاری شود. یکی از هدف های ما این است که در قسمت مدیریت سایت، یک صفحه داشته باشیم که در داخل این صفحه تمامی راهنما ها را بتوانیم ویرایش کنیم و برای ویرایش هر راهنما مجبور نباشیم یک صفحه جداگانه را ویرایش کنیم. در این مثال از WebAPI استفاده می شود که با اینکار می توان از این راهنماها در برنامه های دیگر هم استفاده کرد.

3- استفاده آسان، با فرض اینکه Web API آماده باشد و اسکریپت ها به درستی ارجاع داده شده باشند، تنها کاری که لازم است برای استفاده از راهنما، انجام دهیم افزودن یک صفت به عنصر های HTML است البته اگر بخواهیم از Bootstrap popover استفاده کنیم باید به عنصر های HTML دو صفت اضافه کنیم.

در ابتدا هدف این بود که با استفاده از jQuery UI این مثال را بسازیم ولی در آخر تصمیم گرفتیم که علاوه بر این روش، نحوه استفاده از Bootstrap popover را هم در این مثال اضافه کنیم. البته لازم به ذکر هست که اگر از popover استفاده کنیم، کاربر دیگر توانایی تغییر سایز و تغییر جای راهنما را نخواهد داشت و تصمیم این قضیه که از کدام مدل استفاده شود برعهده برنامه نویس خواهد بود.

در فهرست 1 و فهرست 2، نحوه استفاد از jQuery UI و Bootstrap popover در عناصر HTML نشان داده شده است.

فهرست 1

<input type="text" class="form-control" data-yourtooltipctrl="lookupKey1" />  

فهرست 2

<input type="text" class="form-control" data-yourtooltipctrl="lookupKey2" data-yourtooltiptype="1" />  

پیاده سازی Web API

Web API مورد استفاده در این مثال بسیار ساده است و کدهای آن را در فهرست 3 می توانید ببینید. به منظور ساده سازی این مثال، در Web API به جای اتصال به دیتابیس، چند داده برای تست برنامه به صورت دستی وارد کرده ایم. ولی در برنامه های واقعی این داده ها باید از داخل دیتابیس دریافت شود. در این مثال، برنامه ما از متد POST برای دریافت داده های داخل راهنما استفاده می کند. البته هیچ گونه اشکالی برای کار با متد Get وجود ندارد ولی هدف ما در این مثال نحوه ارسال AntiForgeryToken و چند پارامتر به صورت Post است.

برای جزئیات بیشتر در مورد چگونگی اجرای  AntiForgeryToken و استفاده از برنامه، به مقاله آموزش ساخت بنر هشدار دهنده به کمک Bootstrap و AngularUI Bootstrap در ASP.net MVC مراجعه کنید. در پایین تر یک بخش جاوا اسکریپت وجود دارد که نحوه ارسال چند پارامتر به همراه AntiForgeryToken را از طریق AJAX post نشان می دهد.

فهرست 3

public class ToolTipController : BaseApiController  
{  
    //this is just a sample, in reality, the data should be originated from a repository  
    IList<tooltip> toolTips = new List<tooltip>  
    {  
        new ToolTip { Id = 1, Key ="registerEmail", Title = "Email",  
            Description ="Enter your Email Address", LastUpdatedDate = DateTime.Now },  
        new ToolTip { Id = 2, Key ="registerPassword", Title = "Password policy",  
            Description ="some policy...", LastUpdatedDate = DateTime.Now}  
    };  
…  
    [Route("{key}")]  
    public IHttpActionResult Get(string key)  
    {  
        var toolTip = toolTips.FirstOrDefault((p) => p.Key.ToLower() == key.ToLower());  
        if (toolTip == null)  
        {  
            return NotFound();  
        }  
        return Ok(toolTip);  
    }  
    [HttpPost]  
    [Route("GetWithPost")]  
    [AjaxValidateAntiForgeryToken]  
    public IHttpActionResult GetWithPost(Dummy dummy)  
    {  
        var toolTip = toolTips.FirstOrDefault((p) => p.Key == dummy.Key);  
        if (toolTip == null)  
        {  
            return NotFound();  
        }  
        return Ok(toolTip);  
    }  
}  

فهرست 4، محتویات کنترلر BaseApiController را نشان می دهد. این کنترلر از ApiController ارث بری کرده است و در داخل آن یک struct  وجود دارد. در اینجا بجای کلاس از struct  استفاده کرده ایم به این دلیل که در اینجا فقط property داریم و نیازی به کلاس نیست. البته هر وقت بخواهید می توانید این struct را به کلاس تبدیل کنید تا نیازهای تان را برطرف کند.

فهرست 4

public class BaseApiController : ApiController  
{  
    public struct Dummy  
    {  
        public string Key { get; set; }  
        public string Other { get; set; }  
    }  
}  

Web API - فعال کردن درخواستهای متقابل (CORS)

به لحاظ فنی می توان Web API را در همان فضای کنار سایت یا در یک فضای کاملا جداگانه، نگهداری کرد. در این مثال، برنامه ما و Web API از هم جدا هستد. همانطور که گفته شد، سایت و Web API می توانند در دو فضای متفاوت حتی با دامنه های متفاوت نگه داری شوند. بر اساس طراحی مرورگرها که دارای سیاست های مشترکی در زمینه امینت هستند، از ارسال دستورات Ajax به یک دامنه یا زیر دامنه دیگر جلوگیری خواهد شد. با این حال، با فعال کردن درخواست متقابل (CORS)، این مسئله را می توان کنترل کرد و به برنامه اجازه دسترسی به Web API را داد، حتی اگر دامنه های متفاوتی داشته باشند.

فهرست 5، کدهای داخل فایل Global.asax را نشان می دهد که با این کدها، CORS فعال شده است. هدف متد SetAllowOrigin بررسی این است که آیا URL درخواست کننده در لیست سفید وجود دارد یا خیر. اگر جواب مثبت بود مقدار Access-Control-Allow-Origin و مقدار URL را در هدر درخواست قرار می دهد. این هدر به مرورگر نشان میدهد که URL درخواست کننده، اجازه دسترسی به منابع سرور را دارد. علاوه براین، قبل از ارسال درخواست های واقعی، مرورگر به کمک یکی از متد های HTTP، مقدار preflight request را به سرور ارسال می نماید. جواب دریافتی از سرور مشخص می کند که چه متد ها و هدر هایی اجازه ارسال درخواست به سرور را دارند. در این مثال، X-Requested-With و requestverificationtoken به هدر اضافه شده اند. هدر قبلی توسط AngularJS $http service function AJAX Request استفاده می شد در حالی که هدر جدید مقدار antiforgerytoken که از برنامه وب دریافت می کند را نگه داری می کند. برای کسب اطلاعات بیشتر در مورد نحوه کار CORS و Preflight Requests، می توانید مقاله موجود در سایت مایکروسافت را مطالعه نمایید.

فهرست 5

internal void SetAllowOrigin(string url)  
{  
    //get the allow origin url from appsetting  
    string allowOrigin = System.Configuration.ConfigurationManager.AppSettings["AllowWebApiCallURL"];  
    if (allowOrigin.Split(';').Select(s=>s.Trim().ToLower()).Contains(url.ToLower()))  
    {  
        HttpContext.Current.Response.Headers.Remove("Access-Control-Allow-Origin");  
        //http://domain.com or * to allow all caller  
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", url.ToLower());  
    }  
}  
protected void Application_BeginRequest(object sender, EventArgs e)  
{  
    SetAllowOrigin(HttpContext.Current.Request.Headers["Origin"] == null ?  
        string.Format("{0}://{1}", HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Authority)  
        : HttpContext.Current.Request.Headers["Origin"]);  
              
    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")  
    {  
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, PUT, POST");  
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, content-type, Accept, requestverificationtoken");  
        HttpContext.Current.Response.End();  
    }  
}  

Web APIMachine key

همانطور که قبلا هم اشاره شد، در این مثال سایت و Web API به صورت جداگانه از یکدیگر نگه داری می شوند. متد های Post موجود در Web API، انتظار دریافت توکن anti-forgery از ارسال کنند درخواست را دارند. برای اینکه Web API بتواند توکن anti-forgery را decrypt کند باید برنامه و Web API هر دو از یک machine key استفاده نمایند. اگر برنامه شما نیاز به Machine key داشت، می توانید از این لینک برای ساخت Machine key استفاده نمایید.

اسکریپت ها

در زمان بارگزاری صفحه، اسکریپ های سمت کاربر، عناصر HTML دارای صفت data-yourtooltipctr را پیدا می کنند. در کنار هرکدام از این عناصر یک image button ایجاد می شود. مقدار صفت data-yourtooltipctr در دکمه ها، برای دریافت محتویات راهنما از Web API استفاده می شود. با استفاده از این مقدار، صفت داده ایی data-yourtooltipctr ساخته می شود، پس حتما بررسی کنید که این مقدار منحصربفرد باشد. هدف اصلی استفاده از صفتdata-yourtooltiptype ، آن است که اگر راهنما به کمک jQuery یا Bootstrap نمایش داده شد، علامت گذاری شود. اگر این صفت موجود باشد، راهنما به کمک Bootstrap Popover نمایش داده می شود در غیر این صورت توسط افزونه jQuery UI Dialog نمایش داده خواهد شد. برای مشاهده جزئیات بیشتر، به فهرست 6 مراجعه کنید. ما از تصویری که در آدرس /content/info_16x16.png قرار دارد استفاده کرده ایم، شما می توانید آدرس تصویر مورد نظر خودتان را انتخاب نمایید.

فهرست 6

$('[data-yourtooltipctrl]').each(function (index) {  
    var toolTipType = '';  
    //get tooltiptype  
    if ($(this).data('yourtooltiptype')) {  
        toolTipType = $(this).data('yourtooltiptype');  
    }  
    var container = $("<a href='#' class='yourToolTipLink' data-yourtooltiptype='" + toolTipType  
        + "' data-yourtooltipid='" + $(this).data(' yourtooltipctrl')  
        + "'><img alt='Click for detail' src='/content/info_16x16.png'/>" )  
        .css({  
        cursor: 'help' ,  
        'padding-left' : '2px'  
        });  
    $(this).css("display", "inline-block" );  
    if ($(this).is("label")) {  
        $(this).append(container);  
    }  
    else {  
        $(this).after(container);  
    }  
});  

در فهرست 7، کدهای مربوط به رویداد کلیک برای عناصرHTML  که دارای کلاس yourToolTipLink  هستند را مشاهده می کنیم. در این کدها، ابتدا بررسی می شود که اگر راهنمای دیگری باز باشد، ابتدا آن راهنما ها را می بندیم و سپس به کمک یک تابع jQuery AJAX به روش Post، به Web API متصل می شویم. Web API دو پارامتر ورودی با نام های Key  و Other دریافت می کند. مقدار Key از صفت داده ایی yourtooltipid بدست می آید و پارامتر دیگر با نام Other، یک مقدار ساختگی را نگه می دارد. همانطور که قبلا اشاره کرده بودیم، Web API از Key برای دریافت محتویات راهنما استفاده می کند. تابع AJAX ما از تابع beforeSend برای اضافه کردن AntiForgerytoken به هدر استفاده می کند.

اگر درخواست موفقیت آمیز بود، اسکریپت ها محتوای dialog را پر خواهند کرد. Modal متناسب با مقدار صفت data-yourtooltiptype، به کمک یکی از کتابخانه های jQuery UI Modal  یا Bootstrap Popover نمایش داده خواهد شد. بخش چالش برانگیز این اسکریپت، آن است که برای مقدار API URL نمی توانیم به راحتی از یک relative path استفاده کنیم، چراکه API را در یک فضای جدا از برنامه، قرار داده بودیم. اگر مشکلی پیش آمد، بهتر است که اسکریپت را جوری تغییر دهید که مقدار API URL را از یک متغییر global بخواند. خوبی استفاده از متغییر global این است که این متغییر را می توان از داخل کدهای برنامه تغییر داد. در این لینک چند مثال در مورد ارسال داده های Server-side به توابع جاوا اسکریپت و چند مثال در مورد نحوه تغییر URL در هنگام deploy برنامه در یک محیط دیگر، وجود دارد.

فهرست 7

$(".yourToolTipLink").on('click', function (e) {  
        e.stopPropagation();  
        e.preventDefault();  
  
        var o = $(this);  
        var toolTipTypeSpecified = $(this).attr('data-yourtooltiptype');  
  
        //close the dialog or tooltip, to make sure only one at a time  
        $(".yourToolTipLink").not(this).popover('hide');  
  
        if ($("#yourTooltipPanel").dialog('isOpen') === true) {  
            $("#yourTooltipPanel").not(this).dialog('close');  
        }  
  
        var Dummy = {  
            "Key": $(this).data('yourtooltipid'),  
            "Other": "dummy to show Posting multiple parameters to API"  
        };  
  
        jQuery.ajax({  
            type: "POST",  
            url: "http://localhost:47503/api/tooltip/GetWithPost",  
            data: JSON.stringify(Dummy),  
            dataType: "json",  
            contentType: "application/json;charset=utf-8",  
            beforeSend: function (xhr) { xhr.setRequestHeader('RequestVerificationToken', $("#antiForgeryToken").val()); },  
            headers: {  
                Accept: "application/json;charset=utf-8",  
                "Content-Type": "application/json;charset=utf-8"  
            },  
            accepts: {  
                text: "application/json"  
            },  
            success: function (data) {  
  
                if (toolTipTypeSpecified) {  
                    o.popover({  
                        placement: 'right',  
                        html: true,  
                        trigger: 'manual',  
                        title: data.Title + '<a href="#" class="close" data-dismiss="alert">×</a>',  
                        content: '<div class="media"><div class="media-body"><p>' + data.Description + '</p></div></div>'  
                    }).popover('toggle');  
                    $('.popover').css({ 'width': '100%' });  
                }  
                else {  
                    $("#yourTooltipPanel p").html(data.Description);  
                    $("span.ui-dialog-title").text(data.Title);  
                    $("#yourTooltipPanel").dialog("option", "position", {  
                        my: "left top",  
                        at: "left top",  
                        of: o  
                    }).dialog("open");  
                }  
            }  
        });  
    });  

نحوه یکپارچه سازی

Web API آماده و در حال اجرا است. تمامی style ها و جاوا اسکریپت های نشان داده شده در شکل 1 را به صفحه _Layout  در برنامه اضافه می نماییم. به نظر تعداد این کتابخانه ها زیاد است ولی در حالت کلی مقدار زیادی از این کتابخانه ها در حافظه مرورگر شما موجود است مثلا jQuery و Bootstrap در اکثر مرورگر ها وجود دارد. partial view با نام _AntiforgeryToken.cshtml، شامل یک hidden field برای نگه داری antiforgerytoken است. دیگر partial view برنامه با نام _WebToolTip.cshtml شامل یک عنصر Div است که در این عنصر id="yourTooltipPanel" است و یک ارجاع به فایل yourSimpleTooltip.js داریم. مقدار Web API URL موجود در جاوا اسکریپت را تغییر دهید و مقدار آن را متناسب با آدرس Web API مورد نظرتان، تعیین نمایید.

عکس 1

درکل استفاده از Antiforgerytoken باعث می شود که ادغام برنامه ما با پلتفرم های دیگری مثل ASP.NET Webform، PHP، Classic ASP و ... بسیار سخت و دشوار شود. در این مثال ما یک صفحه HTML برای نمایش نحوه استفاده از اسکریپت راهنما بدون نیاز به token ساخته ایم. در اصل یک متد API که به Token نیاز نداشته باشد، ساخته ایم و این اسکریپت را در فایل yourSimpleTooltipNoToken.js ذخیره کرده ایم. این فایل باعث می شود که در هنگام استفاده از متد POST به روش jQuery AJAX، دیگر از تابع beforeSend استفاده نشود. برای سادگی بیشتر این کار را به شکل dynamic طراحی نکرده ایم. برای بررسی بیشتر به عکس شماره 2 رجوع شود.

عکس 2

نکته

برای اینکه پس از تغییر اندازه مرورگر، مکان آیکون راهنما ها درست نمایش داده شود، در این مثال از متد jQuery after استفاده شده است که به کمک این متد، آیکون ها بعد از عناصر به صفحه اضافه می شوند. با این روش آیکون ها همیشه در جای درست خود قرار خواهند گرفت.

خروجی

آموزش asp.net mvc

فایل های ضمیمه
دانلود نسخه ی PDF این مطلب