
Introduction
In this article, I will share tips on how to dynamically add a help/tooltip / ScreenTip icon next to form elements such as textbox, label, paragraph, button, etc. A message/tip/text will be displayed when the user clicks on the icon either through the jQuery UI modal or Bootstrap popover, depending on the setting. All the text will be stored in a centralized location and will be retrieved on demand by using a Web API call. The idea was inspired by the following criteria:
- Most tooltips will close or disappear after the focus leaves the icon or form element when the user starts to type or after a few seconds. I want a tooltip that will stay open on click so that the user can take their time reading through the message, look up other information if necessary, and then enter the data into the form element. The user can drag or resize the tooltip if using jQuery UI Modal and dismiss the tooltip by pressing the x button.
- The tooltip contents should come from a database and be accessed by using Web API. One of my goals is to build an Administrator interface to manage the tooltip metadata in one location instead of going through every single page. Web API is being used so that the common metadata and service can be exposed to other applications in the future.
- Easy to integrate. Assuming the Web API is ready and the scripts are being referenced correctly, all you have to do is just add an attribute to the form element, two attributes if you want to use the Bootstrap popover feature.
Initially, the idea was to create the solution using jQuery UI only, but then I decided to add some flavor to it by throwing in the Bootstrap Popover. The caveat of the popover is that the user will not be able to drag or resize the tooltip dialog. Is up to the developer to decide if they want to display both or one of the other tooltip dialog modals.
Shown in Listing 1 and Listing 2 are examples of how to add a tooltip icon using jQuery UI and Bootstrap popover respectively.
Listing 1
<input type="text" class="form-control" data-yourtooltipctrl="lookupKey1" />
Listing 2
<input type="text" class="form-control" data-yourtooltipctrl="lookupKey2" data-yourtooltiptype="1" />
Implementation Web API
The Web API in this article is very straightforward and listed in Listing 3. For the sake of simplification, the API return results were hardcoded from a data source. In reality, the result should originate from a data source/repository. In this demo, the Web application will be using the POST method to get the tooltip metadata by key. There is nothing wrong with using the GET method; the goal is to demonstrate how to pass in the AntiForgeryToken and multiple parameters to the API using the POST method. Refer to the article Asp.net MVC Warning Banner using Bootstrap and AngularUI Bootstrap for more details on how AntiForgeryToken is being implemented and utilized by the application. There will be a JavaScript section below to show how to pass in multiple parameters and AntiForgeryToken through the AJAX post.
Listing 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);
}
}
Listing 4
shows the contents of the BaseApiController. This base class is inherited from the ApiController and contains structs. The struct type is being used here instead of class because there are only a couple of properties, and it is short-lived. Please feel free to modify it to a class later to fulfill your requirements.
Listing 5
public class BaseApiController : ApiController
{
public struct Dummy
{
public string Key { get; set; }
public string Other { get; set; }
}
}
Web API Enabling Cross-Origin Requests (CORS)
Technically, the Web API can be hosted together with the Web Application or separately. In this sample application, the Web API and Web Application are being decoupled. That being said, the Web and API application could be hosted on a different subdomain or domain. By design, the web browser security same-origin policy will prevent a web page from making AJAX requests to another domain or subdomain. However, this issue can be overcome by Enabling Cross-Origin Requests (CORS) to explicitly allow cross-origin requests from the Web to the API application.
Listing 6
Shows the code in the Global.asax file to enable the CORS. The purpose of the SetAllowOrigin method is to check if the request URL is in the white list, if yes, set the Access-Control-Allow-Origin header value with the URL value. This response header will signal to the web browser that the requested domain/URL is permitted to access the resources on the server. In addition, before sending the actual requests, the web browser will issue a "preflight request" to the server by using the HTTP Options method. The response from the server will tell the browser what methods and headers are allowed by the request. In the current example, the X-Requested-With and request verification token were added to the header. The former header is utilized by the AngularJS $http service function AJAX Request, and the latter is the anti-forgery token value from the web application. Check out this article Enabling Cross-Origin Requests in ASP.NET Web API 2 to learn more about how CORS and Preflight Requests work.
Listing 7
internal void SetAllowOrigin(string url)
{
// Get the allow origin URL from app settings
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 callers
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();
}
}




Farhan AhmedPosted Jul 24, 2018, 4:57 AM
Nice like it....