This session will cover the strategic decisions you have to make as a developer when targeting multiples devices in your application. We will explore the tools and technologies that you have available in Visual Studio 2013 for both web and native applications that target Windows, iOS and Android devices, as well as best practices to reuse code and skills across them.
آیا میدانید چند درصد از کدهای یک پروژه شما در قسمتهای مختلف آن تکراری هستند و تا چه حد نیاز به refactoring کدهای موجود جهت مدیریت و نگهداری سادهتر از آن پروژه وجود دارد؟
اخیرا پروژه سورس بازی در سایت CodePlex به نام Clone detective ارائه شده است که این کار را به صورت خودکار با یکپارچه شدن با Visual studio برای شما انجام میدهد. این افزودنی از آدرس زیر قابل دریافت است:
http://www.codeplex.com/CloneDetectiveVS
بهترین آموزش نحوه استفاده از آن هم از طریق ویدیوی زیر در دسترس است:
مشاهده
در نگارش فعلی آن تنها پروژههای سی شارپ پشتیبانی میشوند و در نگارشهای آتی آن قرار است VB.net و CPP نیز افزوده شوند.
به چه دلیلی به این ابزار نیاز داریم؟
فرض کنید کلاسی را جهت انجام مقصودی خاص توسعه دادهاید. در کلاسی دیگر برای اتمام آن، 15 سطر از یکی از توابع کلاس اول را کپی کرده و مورد استفاده قرار دادهاید. در یک پروژه بزرگ از این موارد شاید زیاد رخ دهد (خصوصا در یک کار تیمی که ممکن است قسمتی از کار شما بهعنوان پایه اولیه کاری دیگر مورد استفاده قرار گیرد). پس از مدتی، تغییراتی را در کلاس اول ایجاد کرده و یک سری از عیوب آن 15 سطر را که جزئی از یک تابع است برطرف خواهید کرد. بسیار هم خوب! آیا این پایان کار است؟ خیر!
آیا این مورد به کل پروژه منتقل شده است؟ آیا نگهداری یک پروژه بزرگ که دارای قسمتهای تکراری زیادی است کار سادهای است؟
علاوه بر ابزار فوق، برنامه دیگری نیز جهت تشخیص کدهای تکراری در یک پروژه به نام Simian موجود است. Simian را از آدرس زیر میتوانید دریافت کنید:
http://www.redhillconsulting.com.au/products/simian/overview.html
این ابزار به صورت یک افزودنی VS.net ارائه نشده است اما میتوان از طریق منوی tools آنرا به مجموعه ابزارهای مورد استفاده اضافه کرد. نحوه انجام اینکار به صورت مصور در وبلاگ زیر بیان شده است:
مشاهده
همچنین از ابزارهای دیگری از این دست میتوان به برنامه رایگان CCFinder اشاره کرد: (ثبت نام دریافت آن رایگان است)
http://www.ccfinder.net
آکادمی آنلاین
یکپارچه سازی Visual Studio و زبان R
R is decidedly the most popular statistical/data analysis language in use today. R Tools for Visual Studio brings together the power of R and Visual Studio in a convenient and easy to use plug-in that’s free and Open Source
From today most of the Iranian developers who work on free/open-source projects get mail about suspending their accounts because of the U.S sanction over Iran , also they want a non-iranian passport to unlock accounts which means if your Iranian who works for a tech company remotely or using their service for developing free software are banned too!
also, some of Iranian couldn't clone projects from GitHub!
API Versioning
- URI-based versioning
- Header-based versioning
- Media type-based versioning
پیاده سازی URI-based versioning
public class ItemViewModel { public int Id { get; set; } public string Name { get; set; } public string Country { get; set; } } public class ItemV2ViewModel : ItemViewModel { public double Price { get; set; } }
public class ItemsController : ApiController { [ResponseType(typeof(ItemViewModel))] public IHttpActionResult Get(int id) { var viewModel = new ItemViewModel { Id = id, Name = "PS4", Country = "Japan" }; return Ok(viewModel); } } public class ItemsV2Controller : ApiController { [ResponseType(typeof(ItemV2ViewModel))] public IHttpActionResult Get(int id) { var viewModel = new ItemV2ViewModel { Id = id, Name = "Xbox One", Country = "USA", Price = 529.99 }; return Ok(viewModel); } }
config.Routes.MapHttpRoute("ItemsV2", "api/v2/items/{id}", new { controller = "ItemsV2", id = RouteParameter.Optional }); config.Routes.MapHttpRoute("Items", "api/items/{id}", new { controller = "Items", id = RouteParameter.Optional }); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } );
config.Routes.MapHttpRoute( "defaultVersioned", "api/v{version}/{controller}/{id}", new { id = RouteParameter.Optional }, new { version = @"\d+" }); config.Routes.MapHttpRoute( "DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional } );
با این تنظیمات فعلا به مسیریابی ورژن بندی شدهای دست نیافتهایم. زیرا فعلا به هیچ طریق به Web API اشاره نکردهایم که به چه صورت از این پارامتر version برای پیدا کردن کنترلر ورژن بندی شده استفاده کند و به همین دلیل این دو مسیریابی نوشته شده در عمل نتیجه یکسانی را خواهند داشت. برای رفع مشکل مطرح شده باید فرآیند پیش فرض انتخاب کنترلر را کمی شخصی سازی کنیم.
IHttpControllerSelector مسئول پیدا کردن کنترلر مربوطه با توجه به درخواست رسیده میباشد. شکل زیر مربوط است به مراحل ساخت کنترلر بر اساس درخواست رسیده:
به جای پیاده سازی مستقیم این اینترفیس، از پیاده سازی کننده پیش فرض موجود (DefaultHttpControllerSelector) اسفتاده کرده و HttpControllerSelector جدید ما از آن ارث بری خواهد کرد.
public class VersionFinder { private static bool NeedsUriVersioning(HttpRequestMessage request, out string version) { var routeData = request.GetRouteData(); if (routeData != null) { object versionFromRoute; if (routeData.Values.TryGetValue(nameof(version), out versionFromRoute)) { version = versionFromRoute as string; if (!string.IsNullOrWhiteSpace(version)) { return true; } } } version = null; return false; } private static int VersionToInt(string versionString) { int version; if (string.IsNullOrEmpty(versionString) || !int.TryParse(versionString, out version)) return 0; return version; } public static int GetVersionFromRequest(HttpRequestMessage request) { string version; return NeedsUriVersioning(request, out version) ? VersionToInt(version) : 0; } }
کلاس VersionFinder برای یافتن ورژن رسیده در درخواست جاری مورد استفاده قرار خواهد گرفت. با استفاده از متد NeedsUriVersioning بررسی صورت میگیرد که آیا در این درخواست پارامتری به نام version وجود دارد یا خیر که درصورت موجود بودن، مقدار آن واکشی شده و درون پارامتر out قرار میگیرد. در متد GetVersionFromRequest بررسی میشود که اگر خروجی متد NeedsUriVersioning برابر با true باشد، با استفاده از متد VersionToInt مقدار به دست آمده را به int تبدیل کند.
public class VersionAwareControllerSelector : DefaultHttpControllerSelector { public VersionAwareControllerSelector(HttpConfiguration configuration) : base(configuration) { } public override string GetControllerName(HttpRequestMessage request) { var controllerName = base.GetControllerName(request); var version = VersionFinder.GetVersionFromRequest(request); return version > 0 ? GetVersionedControllerName(request, controllerName, version) : controllerName; } private string GetVersionedControllerName(HttpRequestMessage request, string baseControllerName, int version) { var versionControllerName = $"{baseControllerName}v{version}"; HttpControllerDescriptor descriptor; if (GetControllerMapping().TryGetValue(versionControllerName, out descriptor)) { return versionControllerName; } throw new HttpResponseException(request.CreateErrorResponse( HttpStatusCode.NotFound, $"No HTTP resource was found that matches the URI {request.RequestUri} and version number {version}")); } }
متد GetControllerName وظیفه بازگشت دادن نام کنترلر را برعهده دارد. ما نیز با لغو رفتار پیش فرض این متد و تهیه نام ورژن بندی شده کنترلر و معرفی این پیاده سازی از IHttpControllerSelector به شکل زیر میتوانیم به Web API بگوییم که به چه صورت از پارامتر version موجود در درخواست استفاده کند.
config.Services.Replace(typeof(IHttpControllerSelector), new VersionAwareControllerSelector(config));
حال با اجرای برنامه به نتیجه زیر خواهیم رسید:
راه حل دوم: برای زمانیکه Attribute Routing مورد استفاده شما است میتوان به راحتی با تعریف قالبهای مناسب مسیریابی، API ورژن بندی شده را ارائه دهید.
[RoutePrefix("api/v1/Items")] public class ItemsController : ApiController { [ResponseType(typeof(ItemViewModel))] [Route("{id:int}")] public IHttpActionResult Get(int id) { var viewModel = new ItemViewModel { Id = id, Name = "PS4", Country = "Japan" }; return Ok(viewModel); } } [RoutePrefix("api/V2/Items")] public class ItemsV2Controller : ApiController { [ResponseType(typeof(ItemV2ViewModel))] [Route("{id:int}")] public IHttpActionResult Get(int id) { var viewModel = new ItemV2ViewModel { Id = id, Name = "Xbox One", Country = "USA", Price = 529.99 }; return Ok(viewModel); } }
اگر توجه کرده باشید در مثال ما، نامهای کنترلرها متفاوت از هم میباشند. اگر بجای در نظر گرفتن نامهای مختلف برای یک کنترلر در ورژنهای مختلف، آن را با یک نام یکسان درون namespaceهای مختلف احاطه کنیم یا حتی آنها را درون Class Libraryهای جدا نگهداری کنیم، به مشکل "یافت شدن چندین کنترلر که با درخواست جاری مطابقت دارند" برخواهیم خورد. برای حل این موضوع به راه حل سوم توجه کنید.
راه حل سوم: استفاده از یک NamespaceControllerSelector که پیاده سازی دیگری از اینترفیس IHttpControllerSelector میباشد. فرض بر این است که قالب پروژه به شکل زیر میباشد:
کار با پیاده سازی اینترفیس IHttpRouteConstraint آغاز میشود:
public class VersionConstraint : IHttpRouteConstraint { public VersionConstraint(string allowedVersion) { AllowedVersion = allowedVersion.ToLowerInvariant(); } public string AllowedVersion { get; private set; } public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection) { object value; if (values.TryGetValue(parameterName, out value) && value != null) { return AllowedVersion.Equals(value.ToString().ToLowerInvariant()); } return false; } }
کلاس بالا در واقع برای اعمال محدودیت خاصی که در ادامه توضیح داده میشود، پیاده سازی شده است.
متد Match آن وظیفه چک کردن برابری مقدار کلید parameterName موجود در درخواست را با مقدار allowedVersion ای که API از آن پشتیبانی میکند، برعهده دارد. با استفاده از این Constraint مشخص کردهایم که دقیقا چه زمانی باید Route نوشته شده انتخاب شود.
به روش استفاده از این Constraint توجه کنید:
namespace UriBasedVersioning.Namespace.Controllers.V1 { using Models.V1; RoutePrefix("api/{version:VersionConstraint(v1)}/items")] public class ItemsController : ApiController { [ResponseType(typeof(ItemViewModel))] [Route("{id}")] public IHttpActionResult Get(int id) { var viewModel = new ItemViewModel { Id = id, Name = "PS4", Country = "Japan" }; return Ok(viewModel); } } } namespace UriBasedVersioning.Namespace.Controllers.V2 { using Models.V2; RoutePrefix("api/{version:VersionConstraint(v2)}/items")] public class ItemsController : ApiController { [ResponseType(typeof(ItemViewModel))] [Route("{id}")] public IHttpActionResult Get(int id) { var viewModel = new ItemViewModel { Id = id, Name = "Xbox One", Country = "USA", Price = 529.99 }; return Ok(viewModel); } } }
با توجه به کد بالا میتوان دلیل استفاده از VersionConstraint را هم درک کرد؛ از آنجایی که ما دو Route شبیه به هم داریم، لذا باید مشخص کنیم که در چه شرایطی و کدام یک از این Routeها انتخاب شود. خوب، اگر برنامه را اجرا کرده و یکی از APIهای بالا را تست کنید، با خطا مواجه خواهید شد؛ زیرا فعلا این Constraint به سیستم Web API معرفی نشده است. تنظیمات زیر را انجام دهید:
var constraintsResolver = new DefaultInlineConstraintResolver(); constraintsResolver.ConstraintMap.Add(nameof(VersionConstraint), typeof (VersionConstraint)); config.MapHttpAttributeRoutes(constraintsResolver);
مرحله بعدی کار، پیاده سازی IHttpControllerSelector میباشد:
public class NamespaceControllerSelector : IHttpControllerSelector { private readonly HttpConfiguration _configuration; private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers; public NamespaceControllerSelector(HttpConfiguration config) { _configuration = config; _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary); } public HttpControllerDescriptor SelectController(HttpRequestMessage request) { var routeData = request.GetRouteData(); if (routeData == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } var controllerName = GetControllerName(routeData); if (controllerName == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } var version = GetVersion(routeData); if (version == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } var controllerKey = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", version, controllerName); HttpControllerDescriptor controllerDescriptor; if (_controllers.Value.TryGetValue(controllerKey, out controllerDescriptor)) { return controllerDescriptor; } throw new HttpResponseException(HttpStatusCode.NotFound); } public IDictionary<string, HttpControllerDescriptor> GetControllerMapping() { return _controllers.Value; } private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary() { var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase); var assembliesResolver = _configuration.Services.GetAssembliesResolver(); var controllersResolver = _configuration.Services.GetHttpControllerTypeResolver(); var controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver); foreach (var controllerType in controllerTypes) { var segments = controllerType.Namespace.Split(Type.Delimiter); var controllerName = controllerType.Name.Remove(controllerType.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length); var controllerKey = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", segments[segments.Length - 1], controllerName); if (!dictionary.Keys.Contains(controllerKey)) { dictionary[controllerKey] = new HttpControllerDescriptor(_configuration, controllerType.Name, controllerType); } } return dictionary; } private static T GetRouteVariable<T>(IHttpRouteData routeData, string name) { object result; if (routeData.Values.TryGetValue(name, out result)) { return (T)result; } return default(T); } private static string GetControllerName(IHttpRouteData routeData) { var subroute = routeData.GetSubRoutes().FirstOrDefault(); var dataTokenValue = subroute?.Route.DataTokens.First().Value; var controllerName = ((HttpActionDescriptor[])dataTokenValue)?.First().ControllerDescriptor.ControllerName.Replace("Controller", string.Empty); return controllerName; } private static string GetVersion(IHttpRouteData routeData) { var subRouteData = routeData.GetSubRoutes().FirstOrDefault(); return subRouteData == null ? null : GetRouteVariable<string>(subRouteData, "version"); } }
سورس اصلی کلاس بالا از این آدرس قابل دریافت است. در تکه کد بالا بخشی که مربوط به چک کردن تکراری بودن آدرس میباشد، برای ساده سازی کار حذف شده است. ولی نکتهی مربوط به SubRoutes که برای واکشی مقادیر پارامترهای مرتبط با Attribute Routing میباشد، اضافه شده است. روال کار به این صورت است که ابتدا RouteData موجود در درخواست را واکشی کرده و با استفاده از متدهای GetControllerName و GetVersion، پارامترهای controller و version را جستجو میکنیم. بعد با استفاده از مقادیر به دست آمده، controllerKey را تشکیل داده و درون کنترلرهای موجود در برنامه به دنبال کنترلر مورد نظر خواهیم گشت.
کارکرد متد InitializeControllerDictionary :
همانطور که میدانید به صورت پیشفرض Web API توجهی به فضای نام مرتبط با کنترلرها ندارد. از طرفی برای پیاده سازی اینترفیس IHttpControllerSelector نیاز است متدی با امضای زیر را داشته باشیم:
public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
لذا در کلاس پیاده سازی شده، خصوصیتی به نام _controllers را که از به صورت Lazy و از نوع بازگشتی متد بالا میباشد، تعریف کردهایم. متد InitializeControllerDictionary کار آماده سازی دادههای مورد نیاز خصوصیت _controllers میباشد. به این صورت که تمام کنترلرهای موجود در برنامه را واکشی کرده و این بار کلیدهای مربوط به دیکشنری را با استفاده از نام کنترلر و آخرین سگمنت فضای نام آن، تولید و درون دیکشنری مورد نظر ذخیره کردهایم.
حال تنظیمات زیر را اعمال کنید:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { var constraintsResolver = new DefaultInlineConstraintResolver(); constraintsResolver.ConstraintMap.Add(nameof(VersionConstraint), typeof (VersionConstraint)); config.MapHttpAttributeRoutes(constraintsResolver); config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceControllerSelector(config)); } }
این بار برنامه را اجرا کرده و APIهای مورد نظر را تست کنید؛ بله بدون مشکل کار خواهد کرد.
نکته تکمیلی: سورس مذکور در سایت کدپلکس برای حالتی که از Centralized Routes استفاده میکنید آماده شده است. روش مذکور در این مقاله هم فقط قسمت Duplicate Routes آن را کم دارد که میتوانید اضافه کنید. پیاده سازی دیگری را از این راه حل هم میتوانید داشته باشید.
پیاده سازی Header-based versioning
public class HeaderVersionConstraint : IHttpRouteConstraint { private const string VersionHeaderName = "api-version"; public HeaderVersionConstraint(int allowedVersion) { AllowedVersion = allowedVersion; } public int AllowedVersion { get; } public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection) { if (!request.Headers.Contains(VersionHeaderName)) return false; var version = request.Headers.GetValues(VersionHeaderName).FirstOrDefault(); return VersionToInt(version) == AllowedVersion; } private static int VersionToInt(string versionString) { int version; if (string.IsNullOrEmpty(versionString) || !int.TryParse(versionString, out version)) return 0; return version; } }
public sealed class HeaderVersionedRouteAttribute : RouteFactoryAttribute { public HeaderVersionedRouteAttribute(string template) : base(template) { Order = -1; } public int Version { get; set; } public override IDictionary<string, object> Constraints => new HttpRouteValueDictionary { {"", new HeaderVersionConstraint(Version)} }; }
[RoutePrefix("api/items")] public class ItemsController : ApiController { [ResponseType(typeof(ItemViewModel))] [HeaderVersionedRoute("{id}", Version = 1)] public IHttpActionResult Get(int id) { var viewModel = new ItemViewModel { Id = id, Name = "PS4", Country = "Japan" }; return Ok(viewModel); } } [RoutePrefix("api/items")] public class ItemsV2Controller : ApiController { [ResponseType(typeof(ItemV2ViewModel))] [HeaderVersionedRoute("{id}", Version = 2)] public IHttpActionResult Get(int id) { var viewModel = new ItemV2ViewModel { Id = id, Name = "Xbox One", Country = "USA", Price = 529.99 }; return Ok(viewModel); } }
پیاده سازی Media type-based versioning
GET /api/Items HTTP 1.1 Accept: application/vnd.mediatype.versioning-v1+json GET /api/Items HTTP 1.1 Accept: application/vnd.mediatype.versioning-v2+json
public class MediaTypeVersionConstraint : IHttpRouteConstraint { private const string VersionMediaType = "vnd.mediatype.versioning"; public MediaTypeVersionConstraint(int allowedVersion) { AllowedVersion = allowedVersion; } public int AllowedVersion { get; } public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection) { if (!request.Headers.Accept.Any()) return false; var acceptHeaderVersion = request.Headers.Accept.FirstOrDefault(x => x.MediaType.Contains(VersionMediaType)); //Accept: application/vnd.mediatype.versioning-v1+json if (acceptHeaderVersion == null || !acceptHeaderVersion.MediaType.Contains("-v") || !acceptHeaderVersion.MediaType.Contains("+")) return false; var version = acceptHeaderVersion.MediaType.Between("-v", "+"); return VersionToInt(version)==AllowedVersion; } }
روشهایی برای حذف کلاسهای utility
تایپوگرافی مقدماتی بوت استرپ 4
شیوهنامههای همراه با بوت استرپ، رفتار و تنظیمات پیشفرض مرورگر را بازنویسی میکنند. این بازنویسی با فایل node_modules\bootstrap\scss\_reboot.scss شروع میشود. اگر مجموعهی بوت استرپ را توسط روش معرفی شدهی در مطلب «روشهای مختلف دریافت و نصب بوت استرپ 4» دریافت کرده باشید، کدهای کامل SASS آن، در پوشهی scss این مجموعه، موجود هستند که یکی از آنها فایل reboot است. کار آن نرمال سازی شیوهنامهها، به نحوی است که در مرورگرها مختلف و همچنین وسایل نمایشی متفاوت، یکسان به نظر برسند:
- برای مثال در این فایل از روش اندازه گیری rem استفاده شدهاست تا مدیریت اندازههای آن در سکوهای کاری مختلف قابل کنترل شود.
- در اینجا از margin-top به طور کامل صرفنظر شدهاست، تا بتوان اندازهگیری فواصل بین عناصر را بهتر محاسبه کرد. بوت استرپ 4 تنها یک margin را در پایین تمام عناصر صفحه، تنظیم میکند. بنابراین آگاهی از وجود این پیشفرض، تنظیم فواصل بین عناصر را نیز سادهتر میکند.
- در این فایل در همهجا از خاصیت inherit استفاده شدهاست تا امکان بازنویسی شیوهنامههای آن توسط custom styles ما سادهتر شود.
- پیشفرض دیگری که در این نگارش از بوت استرپ تنظیم شدهاست، border-box میباشد. به این ترتیب اندازه گیری عرض عناصر سادهتر میشوند. برای مثال اگر عرض یک div را مساوی 200px قرار دهید، یک padding پیشفرض نیز برای آن درنظر گرفته شدهاست و padding سفارشی تنظیم شدهی برای آن بیاثر خواهد بود.
- در این نگارش، فونت پیشفرض صفحه، به فونت پیشفرض سیستم تنظیم شدهاست و نه فونت از پیش تعیین شدهی خاصی. از این جهت که این قلمهای سیستمی، دارای ویژگیهای خاصی هستند که آنها را برای سکوهای کاری مختلف، منحصربفرد میکنند.
مثال: نمایش تاثیر بوت استرپ 4 بر روی تایپوگرافی پیشفرض مرورگر
<body> <div class="container"> <section class="content" id="mission"> <h1>Our Commitment <small>to you</small></h1> <p>Wisdom Pet Medicine strives to blend the best in traditional and <em>alternative medicine</em> in the <strong>diagnosis and treatment</strong> of companion animals including dogs, cats, birds, reptiles, rodents, and fish. We apply the wisdom garnered in the <mark>centuries old tradition</mark> of veterinary medicine, to find the safest treatments and cures.</p> <p>We strive to be your pet's medical <del>staff</del> experts from youth through the senior years. <small>We build preventative health care plans for each and every one of our patients, based on breed, age, and sex, so that your pet receives the most appropriate care at crucial milestones.</small> We want to give your pet a long and healthy life.</p> </section> <section class="content" id="services"> <h2>Exotic Pets</h2> <p>We offer <strong>specialized</strong> care for <em>reptiles, rodents, birds,</em> and other exotic pets.</p> <h3>Grooming</h3> <p>Our therapeutic <span>grooming</span> treatments help battle fleas, allergic dermatitis, and other challenging skin conditions.</p> <h4>General Health</h4> <p>Wellness and senior exams, ultrasound, x-ray, and dental cleanings are just a few of our general health services.</p> <h5>Nutrition</h5> <p>Let our nutrition experts review your pet's diet and prescribe a custom nutrition plan for optimum health and disease prevention.</p> <h6>Pest Control</h6> <p>We offer the latest advances in safe and effective prevention and treatment of fleas, ticks, worms, heart worm, and other parasites.</p> <h2>Vaccinations</h2> <p>Our veterinarians are experienced in modern vaccination protocols that prevent many of the deadliest diseases in pets.</p> </section> </div> </body>
با اعمال بوت استرپ
بدون اعمال بوت استرپ
در اینجا دو تصویر راملاحظه میکنید؛ یکی با اعمال bootstrap.min.css به صفحهاست و دیگری با حذف آن از صفحه. به این ترتیب مشاهده میکنید که صرفا اعمال بوت استرپ به یک صفحهی متداول، کیفیت نمایش آنرا با بازنویسی شیوهنامهی پیشفرض مرورگر، به نحو قابل ملاحظهای بهبود بخشیدهاست و آنرا زیباتر کردهاست.
در اینجا تنها المان بوت استرپی که به صفحه اضافه شدهاست و جزو استانداردهای HTML نیست، یک div با کلاس container است:
<body> <div class="container">
در این مثال تاثیر بوت استرپ را بر روی شیوهنامههای پیشفرض خصوصا h1 تا h6، مشاهده میکنید.
روش دیگر تعریف headings در اینجا، استفاده از کلاسهایی با نامهای مشابه است:
<div class="h1">Test div class H1</div>
<div class="display-1">Test div class display-1</div>
همچنین اگر نیاز به بزرگتر نمایش دادن متن قسمت ابتدایی صفحه وجود داشت، میتوان از کلاس Lead استفاده کرد:
<p class="lead">Testing a lead class</p>
کلاسهای کمکی کار با متون در بوت استرپ 4
بوت استرپ 4 به همراه تعدادی کلاس کمکی کار با متون است که نیازهای متداول تایپوگرافی را برآورده میکنند:
1) کلاسهای کمکی محل قرارگیری متون
- کلاس text-justify کار کشیدن و متناسب کردن یک پاراگراف را با گوشههای سمت چپ و راست صفحه انجام میدهد.
- کلاس text-nowrap از شکسته شدن متن به چندین سطر جلوگیری میکند. برای مثال میتواند برای نمایش کدها مناسب باشد.
- کلاس متغیر text-xx-pos برای تعیین محل قرارگیری متن کاربرد دارد:
در اینجا ذکر xx اختیاری است و میتواند sm، برای اندازههای صفحهی بیشتر از 576px و یا md، برای اندازههای صفحهی بیشتر از 768px و یا lg، برای اندازههای صفحهی بیشتر از 992px و یا xl، برای اندازههای صفحهی بیشتر از 1200px باشد. این اندازههای یاد شده را در ادامه بیشتر مشاهده خواهید کرد.
همچنین pos میتواند left ،center و یا right باشد.
برای مثال کلاس text-sm-center به این معنا است که متن مدنظر در break-point ایی به نام sm، یعنی با اندازهی صفحهی بیشتر از 576px، در وسط صفحه نمایش داده خواهد شد.
2) کلاسهای نمایش upper-case و lower-case حروف
- کلاس text-lowercase کار نمایش lower-case کل یک پاراگراف اعمالی را انجام میدهد.
- کلاس text-uppercase کار نمایش upper-case کل یک پاراگراف اعمالی را انجام میدهد.
- کلاس text-capitalize، اولین حرف هر واژه را به صورت بزرگ نمایش میدهد.
3) کلاسهای شیوهی نمایش متون
کلاس font-weight-bold، کلاس font-weight-normal و کلاس font-italic نمایش ضخیم، عادی و یا italic متن را سبب میشوند.
مثال: بررسی تاثیر کلاسهای کمکی کار با متون در بوت استرپ 4
<body> <div class="container"> <section class="content" id="mission"> <h1 class="text-center text-sm-right text-md-left text-uppercase">Our Commitment</h1> <p class="lead text-justify">Wisdom Pet Medicine strives to blend the best in traditional and <em>alternative medicine</em> in the <strong>diagnosis and treatment</strong> of companion animals including dogs, cats, birds, reptiles, rodents, and fish. We apply the wisdom garnered in the <mark>centuries old tradition</mark> of veterinary medicine, to find the safest treatments and cures.</p> <p class="text-nowrap text-capitalize">We strive to be your pet's medical <del>staff</del> experts from youth through the senior years. <small>We build preventative health care plans for each and every one of our patients, based on breed, age, and sex, so that your pet receives the most appropriate care at crucial milestones.</small> We want to give your pet a long and healthy life.</p> </section> <section class="content" id="services"> <div class="display-4">Exotic Pets</div> <p>We <span class="font-weight-bold">offer</span> <strong class="font-weight-normal">specialized</strong> care for <em>reptiles, rodents, birds,</em> and other exotic pets.</p> <h3 class="text-left text-md-center text-sm-right">Grooming</h3> <p>Our therapeutic <span>grooming</span> treatments help battle fleas, allergic dermatitis, and other challenging skin conditions.</p> <h4>General Health</h4> <p>Wellness and senior exams, ultrasound, x-ray, and dental cleanings are just a few of our general health services.</p> <h5>Nutrition</h5> <p>Let our nutrition experts review your pet's diet and prescribe a custom nutrition plan for optimum health and disease prevention.</p> <h6>Pest Control</h6> <p>We offer the latest advances in safe and effective prevention and treatment of fleas, ticks, worms, heart worm, and other parasites.</p> <h2>Vaccinations</h2> <p>Our veterinarians are experienced in modern vaccination protocols that prevent many of the deadliest diseases in pets.</p> </section> </div> </body>
در اینجا اگر کلاس text-right را به heading اضافه کنیم:
<h1 class="text-right">Our Commitment <small>to you</small></h1>
و یا میتوان این کلاسها را با هم ترکیب کرد:
<h1 class="text-center text-sm-right">Our Commitment <small>to you</small></h1>
- متن h1 در حالت عادی در وسط صفحه نمایش داده شود.
- اما متن مدنظر در break-point ایی به نام sm، یعنی با اندازهی صفحهی بیشتر از 576px، در سمت راست صفحه نمایش داده خواهد شد.
این اعداد توسط افزونهی ViewPort نمایش داده شدهاند.
همچنین تاثیر text-justify را نیز به اولین پاراگراف
<p class="lead text-justify">
و یا اگر text-nowrap را به پاراگرافی اعمال کنیم:
<p class="text-nowrap">
سبب نمایش یک سطری آن خواهد شد که در اینجا با پدید آمدن یک اسکرول بار افقی، قابل ملاحظهاست.
کلاسهای کمکی کار با لیستها و نقل قولها در بوت استرپ 4
بوت استرپ 4 به همراه کلاسهایی کمکی برای کار با لیستها و نقل قولها است؛ مانند:
- کلاس list-unstyled سبب حذف bullets از یک لیست میشود.
- برای ایجاد لیستهای Inline میتوان از کلاس list-inline بر روی المان UL و سپس list-inline-item بر روی هر LI آن، کمک گرفت.
<body> <div class="container"> <section class="content" id="services"> <h2>Exotic Pets</h2> <p>We offer <strong>specialized</strong> care for <em>reptiles, rodents, birds,</em> and other exotic pets.</p> <ul class="list-unstyled"> <li>Grooming</li> <li>General Health</li> <li>Nutrition</li> <li>Pest Control</li> <li>Vaccinations</li> </ul> <ul class="list-inline"> <li class="list-inline-item">Grooming</li> <li class="list-inline-item">General Health</li> <li class="list-inline-item">Nutrition</li> <li class="list-inline-item">Pest Control</li> <li class="list-inline-item">Vaccinations</li> </ul> </section> </div> </body>
در حالت لیست inline، آیتمهای لیست از چپ به راست در یک سطر نمایش داده میشوند. برای مثال میتواند برای نمایش breadcrumbs در یک سایت مناسب باشد.
همچنین برای نمایش نقل قولها میتوان از کلاس blockquote و برای نمایش بهتر امضای آن از کلاس blockquote-footer استفاده کرد:
<body> <div class="container"> <section class="content" id="testimonials"> <h2>Testimonials</h2> <blockquote> During the summer, our rabbit, Tonto, began to have severe redness and itching on his belly and feet. I'm very thankful to the veterinarians and staff at Wisdom for the excellent care Tonto received, and for nipping his allergies in the bud, so to speak. Jane </blockquote> <blockquote class="blockquote text-right"> When Samantha, our Siamese cat, began sleeping all the time and urinating excessively, we brought her to see the specialists at Wisdom. Now, two years later, Samantha is still free from any complications of diabetes, and her blood sugar regularly tests normal. <div class="blockquote-footer"> The McPhersons </div> </blockquote> </section> </div> </body>
در اینجا دو blockquote را مشاهده میکنید. مورد اول بدون کلاس blockquote است و دومی به همراه این کلاس و یک footer تعریف شدهاست. همچنین میتوان کلاسهایی مانند text-right را نیز به blockquote اضافه کرد.
البته در نگارش 4، حاشیهی خاکستری blockquote که در نگارش سوم آن وجود داشت، حذف شدهاست.
کار با رنگها در بوت استرپ 4
بوت استرپ، به همراه تعدادی کلاس مخصوص رنگها است که از آن در همه جا استفاده میکند؛ مانند رنگهای دکمهها، پس زمینهها و متون.
1) کلاسهای تعیین رنگ متون:
برای مثال در اینجا بجای Color میتوان یکی از ثوابت ذیل آنرا قید کرد؛ مانند text-primary و یا text-danger
این کلاسها برای تعیین رنگ متون و همچنین لینکها کاربرد دارند.
2) کلاسهای تعیین رنگ پس زمینه:
در اینجا برای نمونه بجای Color میتوان یکی از ثوابت ذیل آنرا قید کرد؛ مانند bg-primary و یا bg-danger
مثال: اعمال رنگهای زمینهای بوت استرپ
<body> <div class="container"> <section class="content" id="services"> <h2 class="text-danger">Our Mission</h2> <p class="bg-danger text-white">Wisdom Pet Medicine strives to blend the best in traditional and alternative medicine in the diagnosis and treatment of companion animals including dogs, cats, birds, reptiles, rodents, and fish. We apply the wisdom garnered in the centuries old tradition of veterinary medicine, to find the safest treatments and cures.</p> <ul> <li><a class="text-warning" href="#">Grooming</a></li> <li><a href="#">General Health</a></li> <li><a href="#">Nutrition</a></li> <li><a href="#">Pest Control</a></li> <li><a href="#">Vaccinations</a></li> </ul> </section> <section class="content" id="testimonials"> <h2>Testimonials</h2> <blockquote class="blockquote bg-faded text-info"> During the summer, our rabbit, Tonto, began to have severe redness and itching on his belly and feet. I'm very thankful to the veterinarians and staff at Wisdom for the excellent care Tonto received, and for nipping his allergies in the bud, so to speak. <div class="blockquote-footer"> Jane </div> </blockquote> </section> </div> </body>
در اینجا مثالهایی را از اعمال کلاسهای رنگهای بوت استرپ مشاهده میکنید. همچنین امکان ترکیب آنها مانند مثال زیر نیز وجود دارد:
<p class="bg-danger text-white">
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: Bootstrap4_02.zip