Rss آب و هوای هر شهر در یاهو به صورت یک لینک یکتا میباشد؛ به شکل زیر :
و این لینک http://weather.yahooapis.com/forecastrss?w=28350859&u=c اطلاعات آب و هوای تهران را در قالب یک RSS به شما نمایش خواهد داد.
خوب، حالا پارامتر دوم یعنی پارامتر u چکاری را انجام میدهد؟
* چنانچه مقدار پارامتر u برابر c باشد، یعنی شما دمای آب و هوای شهر مد نظر را بر اساس سانتیگراد میخواهید.
* اگر مقدار پارامتر u برابر f باشد، یعنی شما دمای آب و هوای آن شهر مورد نظر را بر اساس فارنهایت میخواهید.
برای گرفتن WOEID شهرها هم به این سایت بروید http://woeid.rosselliot.co.nz و اسم هر شهری که میخواهید بزنید تا WOEID را به شما نمایش دهد.
در این مثال من از یک DropDown استفاده کردم که کاربر با انتخاب هر شهر از DropDown، آب و هوای آن شهر را مشاهده میکند.
Action مربوط به صفحهی Index به صورت زیر میباشد :
[HttpGet] public ActionResult Index() { ViewBag.ProvinceList = _RPosition.Positions; ShowWeatherProvince(8); return View(); }
حال تابعی را که آب و هوای مربوط به هر شهر را نمایش میدهد، به شرح زیر است:
public ActionResult ShowWeatherProvince(int dpProvince) { XDocument rssXml=null; CountryName CountryName = new CountryName(); if (dpProvince != 0) { switch (dpProvince) { case 1: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345768&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Azarbayejan-e Sharqhi" }; break; } case 2: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345767&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Azarbayejan-e Qarbi" }; break; } case 3: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2254335&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Ardebil" }; break; } case 4: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=28350859&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Alborz" }; break; } case 5: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345787&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Esfahan" }; break; } case 6: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345775&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Ilam" }; break; } case 7: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2254463&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Bushehr" }; break; } case 8: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=28350859&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Tehran" }; break; } case 9: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345769&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Chahar Mahall va Bakhtiari" }; break; } case 10: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=56189824&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Razavi Khorasan" }; break; } case 11: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345789&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Shomali Khorasan" }; break; } case 12: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345789&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Jonubi Khorasan" }; break; } case 13: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345778&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Khuzestan" }; break; } case 14: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2255311&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Zanjan" }; break; } case 15: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345784&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Semnan" }; break; } case 16: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345770&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Sistan va Baluchestan" }; break; } case 17: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345772&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Fars" }; break; } case 18: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=20070200&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Qazvin" }; break; } case 19: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2255062&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Qom" }; break; } case 20: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345779&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Kordestan" }; break; } case 21: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2254796&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Kerman" }; break; } case 22: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2254797&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Kermanshah" }; break; } case 23: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345771&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Kohgiluyeh va Buyer Ahmad" }; break; } case 24: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=20070201&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Golestan" }; break; } case 25: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345773&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Gilan" }; break; } case 26: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345782&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Lorestan" }; break; } case 27: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345783&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Markazi" }; break; } case 28: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345780&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Mazandaran" }; break; } case 29: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2254664&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Hamedan" }; break; } case 30: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2345776&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Hormozgan" }; break; } case 31: { rssXml = XDocument.Load("http://weather.yahooapis.com/forecastrss?w=2253355&u=c"); CountryName = new CountryName() { Country = "Iran", City = "Yazd" }; break; } } ViewBag.Location = CountryName; XNamespace yWeatherNS = "http://xml.weather.yahoo.com/ns/rss/1.0"; List<YahooWeatherRssItem> WeatherList = new List<YahooWeatherRssItem>(); for (int i = 0; i < 4; i++) { YahooWeatherRssItem YahooWeatherRssItem = new YahooWeatherRssItem() { Code = Convert.ToInt32(rssXml.Descendants("item").Elements(yWeatherNS + "forecast").ElementAt(i).Attribute("code").Value), Day = rssXml.Descendants("item").Elements(yWeatherNS + "forecast").ElementAt(i).Attribute("day").Value, Low = rssXml.Descendants("item").Elements(yWeatherNS + "forecast").ElementAt(i).Attribute("low").Value, High = rssXml.Descendants("item").Elements(yWeatherNS + "forecast").ElementAt(i).Attribute("high").Value, Text = rssXml.Descendants("item").Elements(yWeatherNS + "forecast").ElementAt(i).Attribute("text").Value, }; WeatherList.Add(YahooWeatherRssItem); } ViewBag.FeedList = WeatherList; } return PartialView("_Weather"); }
حالا کد مربوط به خواندن فایل Rss را برایتان توضیح میدهم : حلقهی for 0 تا 4 (که در کد بالا مشاهده میکنید)یعنی اطلاعات 4 روز آینده را برایم برگردان.
من تگهای Code ، Day ، Low ، High و text فایل RSS را در این حلقه For میخوانم که البته مقادیر این 4 روز را در لیستی اضافه میکنم که نوع این لیست هم از نوع YahooWeatherRssItem میباشد. من این کلاس را در فایل ضمیمه قرار دادم. اکنون هر کدام از این تگها را برایتان توضیح میدهم:
code : هر آب و هوا کدی دارد .مثلا آب و هوای نیمه ابری یک کد ، آب و هوای آفتابی کدی دیگر و ...
Low: حداقل دمای آن روز را به ما میدهد .
High: حداکثر دمای آن روز را به میدهد .
day: نام روز از هفته را بر میگرداند مثلا شنبه ، یکشنبه و ....
text: که توضیحاتی میدهد مثلا اگر هوا آفتابی باشد مقدار sunny را بر میگرداند و ...
خوب، تا اینجا ما Rss مربوط به هر شهر را خواندیم حالا در قسمت Design باید چکار کنیم .
کدهای html صفحهی Index ما شامل کدهای زیر است :
@{ ViewBag.Title = "Weather"; } <link href="~/Content/User/Weather/Weather.css" rel="stylesheet" /> @section scripts{ <script src="@Url.Content("~/Scripts/jquery-1.6.2.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script> <script type="text/javascript"> $("#dpProvince").change(function () { $(this).parents("form").submit(); }); </script> } <h2>Weather</h2> <div id="Progress"> <img src="~/Images/User/Other/ajax-loader.gif" /> </div> <div id="BoxContent"> @Html.Partial("_Weather")</div> @using (Ajax.BeginForm(actionName: "ShowWeatherProvince", ajaxOptions: new AjaxOptions { UpdateTargetId = "BoxContent", LoadingElementId = "Progress", InsertionMode = InsertionMode.Replace })) { <div style="padding-top:15px;"> <div style="float:left; width:133px; ">Select Your Province</div> <div style="float:left"> @Html.DropDownList("dpProvince", new SelectList(ViewBag.ProvinceList, "Id", "Name"),"Select Your Province", new { @class = "webUserDropDown", @style = "width:172px" })</div> </div> }
@{ List<Weather.YahooWeatherRssItem> Feeds = ViewBag.FeedList; } <div> @{ HtmlString StartTable = new HtmlString("<table class='WeatherTable' cellspacing='0' cellpadding='0'><tbody><tr>"); HtmlString EndTable = new HtmlString("</tr></tbody></table>"); HtmlString StartTD = new HtmlString("<td>"); HtmlString EndTD = new HtmlString("</td>"); } <div style="width: 300px;"> @{ @StartTable foreach (var item in Feeds) { @StartTD <div>@item.Day</div> <div> @{ string FileName = ""; switch (item.Code) { case 0: { FileName = "/Images/User/Weather/Tornado.png"; break; } case 1: { FileName = "/Images/User/Weather/storm2.gif"; break; } case 2: { FileName = "/Images/User/Weather/storm2.gif"; break; } case 3: { FileName = "/Images/User/Weather/storm2.gif"; break; } case 4: { FileName = "/Images/User/Weather/15.gif"; break; } case 5: { FileName = "/Images/User/Weather/29.gif"; break; } case 6: { FileName = "/Images/User/Weather/29.gif"; break; } case 7: { FileName = "/Images/User/Weather/29.gif"; break; } case 8: { FileName = "/Images/User/Weather/26.gif"; break; } case 9: { FileName = "/Images/User/Weather/drizzle.png"; break; } case 10: { FileName = "/Images/User/Weather/26.gif"; break; } case 11: { FileName = "/Images/User/Weather/18.gif"; break; } case 12: { FileName = "/Images/User/Weather/18.gif"; break; } case 13: { FileName = "/Images/User/Weather/19.gif"; break; } case 14: { FileName = "/Images/User/Weather/19.gif"; break; } case 15: { FileName = "/Images/User/Weather/19.gif"; break; } case 16: { FileName = "/Images/User/Weather/22.gif"; break; } case 17: { FileName = "/Images/User/Weather/Hail.png"; break; } case 18: { FileName = "/Images/User/Weather/25.gif"; break; } case 19: { FileName = "/Images/User/Weather/dust.png"; break; } case 20: { FileName = "/Images/User/Weather/fog_icon.png"; break; } case 21: { FileName = "/Images/User/Weather/hazy_icon.png"; break; } case 22: { FileName = "/Images/User/Weather/2017737395.png"; break; } case 23: { FileName = "/Images/User/Weather/32.gif"; break; } case 24: { FileName = "/Images/User/Weather/32.gif"; break; } case 25: { FileName = "/Images/User/Weather/31.gif"; break; } case 26: { FileName = "/Images/User/Weather/7.gif"; break; } case 27: { FileName = "/Images/User/Weather/38.gif"; break; } case 28: { FileName = "/Images/User/Weather/6.gif"; break; } case 29: { FileName = "/Images/User/Weather/35.gif"; break; } case 30: { FileName = "/Images/User/Weather/7.gif"; break; } case 31: { FileName = "/Images/User/Weather/33.gif"; break; } case 32: { FileName = "/Images/User/Weather/1.gif"; break; } case 33: { FileName = "/Images/User/Weather/34.gif"; break; } case 34: { FileName = "/Images/User/Weather/2.gif"; break; } case 35: { FileName = "/Images/User/Weather/freezing_rain.png"; break; } case 36: { FileName = "/Images/User/Weather/30.gif"; break; } case 37: { FileName = "/Images/User/Weather/15.gif"; break; } case 38: { FileName = "/Images/User/Weather/15.gif"; break; } case 39: { FileName = "/Images/User/Weather/15.gif"; break; } case 40: { FileName = "/Images/User/Weather/12.gif"; break; } case 41: { FileName = "/Images/User/Weather/22.gif"; break; } case 42: { FileName = "/Images/User/Weather/22.gif"; break; } case 43: { FileName = "/Images/User/Weather/22.gif"; break; } case 44: { FileName = "/Images/User/Weather/39.gif"; break; } case 45: { FileName = "/Images/User/Weather/thundershowers.png"; break; } case 46: { FileName = "/Images/User/Weather/19.gif"; break; } case 47: { FileName = "/Images/User/Weather/thundershowers.png"; break; } case 3200: { FileName = "/Images/User/Weather/1211810662.png"; break; } } } <img alt='@item.Text' title='@item.Text' src='@FileName'> </div> <div> <span>@item.High°</span> <span>@item.Low°</span> </div> @EndTD } } @EndTable </div> </div>
چنانچه در مورد RSS وضعیت آب و هوای یاهو اطلاعات دقیقتری را میخواهید بدانید به این لینک بروید.
در آموزش بعدی قصد دارم برایتان این بخش را توضیح دهم که بر اساس IP بازدید کننده سایت شما، اطلاعات آب و هوایی شهر بازدید کننده را برایش در سایت نمایش دهد.
Files-06bf65bac63d4dd694b15fc24d4cb074.zip
موفق باشید
حال مواقعی پیش میآید که نیاز داریم پیغام هایی با گرافیک و عبارات متفاوت نمایش دهیم . برای رفع این نیاز میتوانیم از پلاگین jQuery Impromptu استفاده کنیم . البته این پلاگین قابلیتهای دیگری هم دارد که در این مقاله با آنها آشنا میشویم .
از ویژگیهای این پلاگین میتوان حجم کم (حدود 11 کیلوبایت) و قدرت شخصی سازی بالا اشاره کرد .
طرز استفاده به این شکل است :
$.prompt( msg , options )
msg میتواند یک string یا یک شئ از states باشد . string ارسال شده میتواند شامل کدهای html باشد .
یک شئ states هم شامل مجموعه ای از وضعیتهای prompt است . برای مثال میتوان یک prompt ایجاد کرد که مثل یک Wizard شامل چند مرحله باشد .
options هم تنظیماتی است که میتوان مشخص کرد . تنظیماتی مثل : prefix,classes,persistent,timeout,...
msg :
گفتیم شئ states شامل وضعیتهای مختلف prompt است . هر وضعیت ( state ) میتواند شامل بخشهای زیر باشد :
- html : مقدار Html وضعیت
- buttons : یک شئ شامل متن و مقدار دکمه هایی که کاربر میتواند کلیک کند
- focus : ایندکس دکمهی focus شده در وضعیت
- submit : تابعی که زمانی که یکی از دکمههای وضعیت انتخاب شود فراخوانی میشود .
اگر در این تابع false بازگشت داده شود یا متد preventDefault از event فراخوانی شود ، prompt باز میماند . ( روشی برای جلوگیری از بسته شدن prompt هنگام تغییر state یا اعتبار سنجی فرم )
همچنین شئ event شامل state ( المنت state ) و stateName ( نام state ) میباشد .
پیشفرض :
function(event, value, message, formVals){}
- position : مشخص کنندهی موقعیت state که شامل موارد زیر است :
position: { container: '#container', x: 0, y: 0, width: 0, arrow: 'lm' }
container : selector المنتی است که state باید در آن مکان قرار بگیرد .
x/y : موقعیت نسبی prompt نسبت به container
arrow : جهت نمایش فلش prompt است که میتواند یکی از این مقادیر باشد : tl, tc, tr, rt, tm, tb, br, bc, bl, lb, lm, lt.
var tourSubmitFunc = function (e, v, m, f) { if (v === -1) { $.prompt.prevState(); return false; } else if (v === 1) { $.prompt.nextState(); return false; } }; var states = { state0: { html: "State1", buttons: { Next: 1 }, //position: { container: '#container', x: 10, y: 0, width: 350, arrow: 'lm' }, submit: tourSubmitFunc }, state1: { html: "State2", buttons: { Prev: -1, Next: 1 }, submit: tourSubmitFunc }, state2: { html: "State3", buttons: { Prev: -1, Done: 0 }, submit: tourSubmitFunc } }; $.prompt(states);
تا به اینجا با پارامتر اول prompt آشنا شدیم و فهمیدیم که میتوانیم یک رشته یا یک شئ states به عنوان message به prompt ارسال کنیم .
options :
اکنون با optionهای prompt ( پارامتر دوم ) آشنا خواهیم شد .
توجه کنید که زمانی که یک رشته به prompt ارسال کنید ، مقادیر buttons,focus,submit از این تنظیمات دریافت میشود .
به عبارت دیگر ، زمانی که یک شئ states به prompt ارسال کنید ، از مقادیر فوق که در تنظیمات است ، استفاده نمیشود .
- loaded
یک تابع که زمانی که prompt کامل بارگزاری شده فراخوانی میشود .
$.prompt("Message", { loaded: function() { alert("Prompt Loaded !"); } });
- submit
یک تابع که زمانی که یکی از دکمههای state کلیک شود ، فراخوانی میشود .
( زمانی اتفاق میوفتد که یک رشته به عنوان متن به prompt ارسال کرده باشید و زمانی که یک شئ از states ارسال میکنید ، هنگام کلیک دکمههای آنها ، این تابع فراخوانی نمیشود . )
پیشفرض :
function(event){}
- statechanging
یک تابع که زمانی که یک state در حال تعویض شدن هست فراخوانی میشود .
پیشفرض :
function(event, fromStateName, toStateName){}
- statechanged
یک تابع که زمانی که یک state در حال تعویض شدن هست فراخوانی میشود .
پیشفرض :
function(event, toStateName){}
- callback
یک تابع که زمانی که ( یکی از دکمههای prompt کلیک شود و ) prompt بسته شود ، فراخوانی میشود .
پیشفرض :
function(event[, value, message, formVals]){}
- buttons
یک شئ شامل مجموعه ای از دکمهها .
پیشفرض :
{ Ok : true }
[ {title: 'Hello World',value:true}, {title: 'Good Bye',value:false} ]
- prefix
یک پیشوند برای همه css classها و idهای المنتهای html که توسط prompt ایجاد میشود .
پیشفرض : jqi
- classes
یک css class که به بالاترین سطح prompt داده میشود .
در حالت پیشفرض مقداری ندارد .
- focus
ایندکس دکمهی focus شده
پیشفرض : 0
- zIndex
zIndex اعمال شده بروی prompt .
پیشفرض : 999
- useiframe
استفاده از یک iframe برای overlay در IE6
پیشفرض : false
- top
فاصلهی prompt از بالای صفحه
پیشفرض : 15%
- opacity
میزان شفافیت لایهی ای که صفحه را پوشانده است .
پیشفرض : 0.6
- overlayspeed
سرعت نمایش افکت fadeIn , fadeOut لایهی پوشاننده .
مقادیر قابل قبول : "slow", "fast", number(milliseconds)
پیشفرض : "slow"
- promptspeed
سرعت نمایش prompt .
مقادیر قابل قبول : "slow", "fast", number(milliseconds)
پیشفرض : "fast"
- show
نام یک متد jQuery برای animate کردن نمایش prompt .
مقادیر قابل قبول : "show","fadeIn","slideDown", ...
پیشفرض : "promptDropIn"
- persistent
بسته شدن prompt زمانی که بروی لایهی fade کلیک شود .
false : بسته شدن
پیشفرض : true
- timeout
مدت زمانی که پس از آن ، prompt بصورت خودکار بسته میشود . ( milliseconds )
پیشفرض : 0
returns :
مقدار بازگشتی متد prompt ، یک شئ jQuery ، شامل همهی محتویات تولید شده توسط prompt است .
Events using Bind :
- promptloaded
معادل loaded در optionها .
- promptsubmit
هنگام submit شدن ( کلیک شدن یکی از دکمههای ) state فعال میشود .
- promptclose
معادل callback در optionها .
- promptstatechanging
معادل statechanging در optionها .
- promptstatechanged
معادل statechanged در optionها .
ظرز بایند کردن یک event به شئ prompt :
var myPrompt = $.prompt( msg , options ); myPrompt.bind('promptloaded', function(e){});
- jQuery.prompt.setDefaults(options)
تعیین مقادیر پیشفرض برای همهی promptها .
jQuery.prompt.setDefaults({ prefix: 'myPrompt', show: 'slideDown' });
- jQuery.prompt.setStateDefaults(options)
تعیین مقادیر پیشفرض برای stateها .
jQuery.prompt.setStateDefaults({ buttons: { Ok:true, Cancel:false }, focus: 1 });
- jQuery.prompt.getCurrentState()
یک شئ jQuery از state جاری برمیگرداند .
- jQuery.prompt.getCurrentStateName()
نام state جاری را برمیگرداند .
- jQuery.prompt.getStateContent(stateName)
یک شئ jQuery از state مشخص شده برمیگرداند .
- jQuery.prompt.goToState(stateName, callback)
state مشخص شده را در prompt نمایش میدهد .
callback تابعی است که بعد از تغییر state فراخوانی میشود .
- jQuery.prompt.nextState(callback)
prompt را به state بعدی منتقل میکند .
- jQuery.prompt.prevState(callback)
prompt را به state قبلی منتقل میکند .
- jQuery.prompt.close()
prompt را میبندد .
حال که با این پلاگین آشنا شدیم ، سه دستور جاوا اسکریپتی که در ابتدای مقاله ذکر کردیم را با این پلاگین پیاده سازی میکنیم .
- alert
$.prompt("یک پیام تستی", { prefix: 'dnt', buttons: { 'تایید': true } });
نتیجه :
- confirm
$.prompt("درخواست تایید - موافقید ؟", { prefix: 'dnt', buttons: { 'تایید': true, 'انصراف': false } });
نتیجه :
- prompt
یک فرم html مخفی برای نمایش در prompt :
<div class="prompt-content" style="display: none;"> <span>نام خود را وارد نمایید : </span> <span> <input type="text" name="name" /> </span> </div>
نمایش prompt :
$.prompt( $(".prompt-content").html(), { prefix: 'dnt', buttons: { 'تایید': true, 'انصراف': false } });
نتیجه :
در این سه مثال آخر ، از یک css متفاوت استفاده کردیم . این پلاگین یک سری کلاس دارد که نام این کلاسها از ترکیب مقدار prefix که در option مشخص کردیم حاصل میشود .
div.dnt .dntmessage { color: #444444; line-height: 20px; padding: 10px; /* Edited */ direction:rtl; text-align:right; }
البته راه بهتری هم هست که نیاز به آشنایی با فایرباگ دارد . در این روش ابتدا کل قالب jqi ( موجود در قالب پیشفرض ) را در برنامه خود کپی میکنیم ، مقادیر jqi را با نام قالب جایگزین میکنیم ، مقدار prefix را در prompt برابر با نام قالب قرار میهیم .
اکنون در FireFox یک prompt ایجاد میکنیم و توسط فایرباگ استایل هایی که با نام قالب بروی prompt اعمال شدهاند را مطابق سلیقه تغییر میدهیم . در مرحله آخر به تب CSS در فایرباگ میرویم و کل استایلهای مربوط به قالب را کپی و جایگزین استایل قبلی در برنامه میکنیم .
/* Start : DotNetTips Theme */ .dntfade { background-color: #AAAAAA; position: absolute; } div.dnt { background-color: #FFFFFF; border-radius: 10px 10px 10px 10px; border: 1px solid #FFFFFF; box-shadow: 0px 0px 10px 1px #6D6D6C; font-family: tahoma; font-size: 11px; padding: 7px; position: absolute; text-align: left; width: 400px; } div.dnt .dntcontainer { font-size: small; } div.dnt .dntclose { color: #BBBBBB; cursor: pointer; font-weight: bold; position: absolute; top: 4px; width: 18px; } div.dnt .dntmessage { color: #444444; line-height: 20px; padding: 10px; } div.dnt .dntbuttons { background-color: #F4F4F4; border: 1px solid #EEEEEE; padding: 5px 0px; text-align: right; } div.dnt button { background-color: #2F6073; border: 1px solid #F4F4F4; color: #FFFFFF; font-size: 12px; font-weight: bold; margin: 0px 10px; padding: 3px 10px; } div.dnt button:hover { background-color: #728A8C; } div.dnt button.dntdefaultbutton { background-color: #0099CC; } .dnt_state { direction: rtl; text-align: right; } .dnt_state button { font-family: tahoma; } .dntwarning .dnt .dntbuttons { background-color: #CCDDFF; } .dnt .dntarrow { border: 10px solid transparent; font-size: 0px; height: 0px; line-height: 0; position: absolute; width: 0px; } .dnt .dntarrowtl { border-bottom-color: #FFFFFF; left: 10px; top: -20px; } .dnt .dntarrowtc { border-bottom-color: #FFFFFF; left: 50%; margin-left: -10px; top: -20px; } .dnt .dntarrowtr { border-bottom-color: #FFFFFF; right: 10px; top: -20px; } .dnt .dntarrowbl { border-top-color: #FFFFFF; bottom: -20px; left: 10px; } .dnt .dntarrowbc { border-top-color: #FFFFFF; bottom: -20px; left: 50%; margin-left: -10px; } .dnt .dntarrowbr { border-top-color: #FFFFFF; bottom: -20px; right: 10px; } .dnt .dntarrowlt { border-right-color: #FFFFFF; left: -20px; top: 10px; } .dnt .dntarrowlm { border-right-color: #FFFFFF; left: -20px; margin-top: -10px; top: 50%; } .dnt .dntarrowlb { border-right-color: #FFFFFF; bottom: 10px; left: -20px; } .dnt .dntarrowrt { border-left-color: #FFFFFF; right: -20px; top: 10px; } .dnt .dntarrowrm { border-left-color: #FFFFFF; margin-top: -10px; right: -20px; top: 50%; } .dnt .dntarrowrb { border-left-color: #FFFFFF; bottom: 10px; right: -20px; } /* End : DotNetTips Theme */
ASP.NET MVC #18
[HttpGet] public ActionResult LogOn(string returnUrl) { if (User.Identity.IsAuthenticated) //remember me { if (ShouldRedirect(returnUrl)) { return Redirect(returnUrl); } return Redirect(FormsAuthentication.DefaultUrl); } return View(); // show the login page }
[Authorize(Roles = "Admin")]
IsAuthenticated=true
ShouldRedirect(returnUrl)
این کد برای من اینجوری کار میکنه
تفاوت AngularJS با KnockoutJS
در مطلب جاری قصد داریم با نحوه ارائه یک UI کاربر پسند برای این منظور آشنا شویم و سؤال مهم هم این است: «چگونه میتوان کار کاربر را در حین وارد کردن تعدادی از برچسبهای مرتبط با یک مطلب سادهتر کرد؟». برای این منظور یکی از راه حلهایی که در بسیاری از سایتها مرسوم شده است، استفاده از افزونههایی مانند jQuery TagIt میباشد که در ادامه با نحوه استفاده از آن در ASP.NET MVC آشنا خواهیم شد.
پیشنیازها:
دریافت افزونه TagIt
همچنین دریافت jQuery UI (افزونه TagIt برای نمایش لیست Auto Complete آیتمها از jQuery UI در پشت صحنه استفاده میکند)
<head> <title>@ViewBag.Title</title> <link href="@Url.Content("~/Content/TagIt/jquery-ui-1.8.23.custom.css")" rel="stylesheet" type="text/css" /> <link href="@Url.Content("~/Content/TagIt/tagit-simple-blue.css")" rel="stylesheet" type="text/css" /> <link href="@Url.Content("Content/Site.css")" rel="stylesheet" type="text/css" /> <script src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Content/TagIt/jquery-ui-1.8.23.custom.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Content/TagIt/tagit.js")" type="text/javascript"></script> @RenderSection("JavaScript", required: false) </head>
آشنایی با مدل برنامه
using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace jQueryMvcSample04.Models { public class BlogPostViewModel { [DisplayName("عنوان"), Required(ErrorMessage = "*")] public string Title { set; get; } [DisplayName("متن"), Required(ErrorMessage = "*")] public string Body { set; get; } /// <summary> /// آرایهای محدود از برچسبهای این مطلب خاص به صورت جیسون که پیشتر ثبت شده است /// هدف استفاده در حین ویرایش مطلب /// </summary> public string InitialTags { set; get; } /// <summary> /// آرایهای جیسونی از تمام برچسبهای موجود در سیستم /// هدف نمایش منوی انتخاب برچسبها از لیست /// </summary> public string TagsSource { set; get; } /// <summary> /// آرایهای از برچسبهای وارد شده توسط کاربر در حین ثبت مطلب /// </summary> [DisplayName("برچسبها"), Required(ErrorMessage = "*")] public string[] Tags { set; get; } public int? Id { set; get; } } }
افزونه TagIt برای نمایش اطلاعات خود به دو منبع داده نیاز دارد:
الف) TagsSource : لیستی است به فرمت JSON، از هر آنچه که در سیستم پیشتر به عنوان یک برچسب ثبت شده است. از این لیست برای نمایش منوی خودکار انتخاب آیتمها استفاده میشود.
ب) InitialTags : لیستی است به فرمت JSON، از تمام برچسبهای مرتبط با یک مطلب. از این اطلاعات در حین ویرایش یک مطلب استفاده خواهد شد.
در این ViewModel یک خاصیت دیگر به شکل آرایه، به نام Tags تعریف شده است که لیست برچسبهای وارد شده توسط کاربر را دریافت خواهد کرد.
معرفی کنترلر برنامه
using System.Web.Mvc; using jQueryMvcSample04.Extensions; using jQueryMvcSample04.Models; namespace jQueryMvcSample04.Controllers { public class HomeController : Controller { [HttpGet] public ActionResult Index(int? id) { //در ابتدای کار تمام تگهای موجود در سیستم از بانک اطلاعاتی دریافت خواهند شد //از این تگها برای تشکیل منوی انتخاب برچسبها استفاده میشود var tagsSource = new[] { "C#", "C++", "C", "ASP.NET", "MVC" }.ToJson(); //همچنین صرفا برچسبهای مطلب جاری که پیشتر ثبت شدهاند نیز باید از بانک اطلاعاتی دریافت گردند //از این برچسبها برای ویرایش یک مطلب موجود استفاده خواهد شد var init = new[] { "ASP.NET" }.ToJson(); var model = new BlogPostViewModel { TagsSource = tagsSource, InitialTags = init, Id = id }; return View(model); } [HttpPost] public ActionResult Index(BlogPostViewModel data) { if (this.ModelState.IsValid) { //todo: save data // ... return RedirectToAction(actionName: "index", controllerName: "home"); } //در صورت بروز خطا مجددا اطلاعات موجود نمایش داده خواهند شد data.TagsSource = new[] { "C#", "C++", "C", "ASP.NET", "MVC" }.ToJson(); data.InitialTags = data.Tags.ToJson(); return View(data); } } }
با توجه به توضیحاتی که ارائه شد، کنترلر برنامه ساختار واضحتری را یافته است. در اولین بار نمایش صفحه، لیست منبع داده تگها و همچنین تگهای مرتبط با یک مطلب (در صورت وجود) به View ارائه خواهند شد.
از همین ViewModel، در عملیات Post نیز استفاده گردیده و اطلاعات دریافت میگردد.
تعریف متد الحاقی ToJson مورد استفاده را نیز در ادامه ملاحظه مینمائید:
using System.Linq; using System.Web.Script.Serialization; namespace jQueryMvcSample04.Extensions { public static class JsonExt { public static string ToJson(this string[] initialTags) { if (initialTags == null || !initialTags.Any()) return "[]"; else return new JavaScriptSerializer().Serialize(initialTags); } } }
و مرحله آخر تعریف View متناظر است
@model jQueryMvcSample04.Models.BlogPostViewModel @{ ViewBag.Title = "Index"; } @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>ثبت مطلب جدید</legend> @Html.HiddenFor(model => model.Id) <div class="editor-label"> @Html.LabelFor(model => model.Title) </div> <div class="editor-field"> @Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) </div> <div class="editor-label"> @Html.LabelFor(model => model.Body) </div> <div class="editor-field"> @Html.EditorFor(model => model.Body) @Html.ValidationMessageFor(model => model.Body) </div> <div class="editor-label"> @Html.LabelFor(model => model.Tags) </div> <div class="editor-field"> <ul id="tagsList" dir="ltr" name="Tags"> </ul> @Html.ValidationMessageFor(model => model.Tags) </div> <p> <input type="submit" value="Create" /> </p> </fieldset> } @section JavaScript { <script type="text/javascript"> $(document).ready(function () { var tagsSource = @Html.Raw(Model.TagsSource); $('#tagsList').tagit({ tagSource: tagsSource, select: true, triggerKeys: ['enter', 'comma', 'tab'], initialTags: @Html.Raw(Model.InitialTags) }); }); </script> }
الف) برای نمایش افزونه TagIt از یک ul با id مساوی tagsList استفاده شده است.
ب) خواص اضافی موجود در ViewModel که اطلاعات JSON ایی مورد نیاز را بازگشت میدهند در قسمت اسکریپت صفحه مورد استفاده قرار گرفتهاند. در اینجا نیاز است از Html.Raw استفاده شود تا اطلاعات مرتبط با JSON اشتباها encode نشده و قابل استفاده باشند.
دریافت مثال و پروژه کامل این قسمت
jQueryMvcSample04.zip
ارسال فایل Ajax ایی آن توسط HTML5 File API صورت میگیرد که در تمام مرورگرهای جدید پشتیبانی خوبی از آن وجود دارد. در مرورگرهای قدیمیتر، به صورت خودکار همان حالت متداول ارسال همزمان فایلها را فعال میکند (یا همان post back معمولی).
فعال سازی مقدماتی kendoUpload
ابتداییترین حالت کار با kendoUpload، فعال سازی حالت post back معمولی است؛ به شرح زیر:
<form method="post" action="submit" enctype="multipart/form-data"> <div> <input name="files" id="files" type="file" /> <input type="submit" value="Submit" class="k-button" /> </div> </form> <script> $(document).ready(function() { $("#files").kendoUpload(); }); </script>
فعال سازی حالت ارسال فایل Ajax ایی kendoUpload
برای فعال سازی ارسال Ajax ایی فایلها در Kendo UI نیاز است خاصیت async آنرا به نحو ذیل مقدار دهی کرد:
<script type="text/javascript"> $(function () { $("#files").kendoUpload({ name: "files", async: { // async configuration saveUrl: "@Url.Action("Save", "Home")", // the url to save a file is '/save' removeUrl: "@Url.Action("Remove", "Home")", // the url to remove a file is '/remove' autoUpload: false, // automatically upload files once selected removeVerb: 'POST' }, multiple: true, showFileList: true }); }); </script>
[HttpPost] public ActionResult Save(IEnumerable<HttpPostedFileBase> files) { if (files != null) { // ... // Process the files and save them // ... } // Return an empty string to signify success return Content(""); } [HttpPost] public ContentResult Remove(string[] fileNames) { if (fileNames != null) { foreach (var fullName in fileNames) { // ... // delete the files // ... } } // Return an empty string to signify success return Content(""); }
دو دکمهی حذف با کارکردهای متفاوت در ویجت kendoUpload وجود دارند. در ابتدای کار، پیش از ارسال فایلها به سرور:
کلیک بر روی دکمهی حذف در این حالت، صرفا فایلی را از لیست سمت کاربر حذف میکند.
پس از ارسال فایلها به سرور:
اما پس از پایان عملیات ارسال، اگر کاربر بر روی دکمهی حذف کلیک کند، توسط آدرس مشخص شده توسط خاصیت removeUrl، نام فایلهای مورد نظر، برای حذف از سرور ارسال میشوند.
چند نکتهی تکمیلی
- تنظیم خاصیت autoUpload به true سبب میشود تا پس از انتخاب فایلها توسط کاربر، بلافاصله و به صورت خودکار عملیات ارسال فایلها به سرور آغاز شوند. اگر به false تنظیم شود، دکمهی ارسال فایلها در پایین لیست نمایش داده خواهد شد.
- شاید علاقمند باشید تا removeVerb را به DELETE تغییر دهید؛ بجای POST. به همین منظور میتوان خاصیت removeVerb در اینجا مقدار دهی کرد.
- با تنظیم خاصیت multiple به true، کاربر قادر خواهد شد تا توسط صفحهی دیالوگ انتخاب فایلها، قابلیت انتخاب بیش از یک فایل را داشته باشد.
- showFileList نمایش لیست فایلها را سبب میشود.
تعیین پسوند فایلهای صفحهی انتخاب فایلها
هنگامیکه کاربر بر روی دکمهی انتخاب فایلها برای ارسال کلیک میکند، در صفحهی دیالوگ باز شده میتوان پسوندهای پیش فرض مجاز را نیز تعیین کرد.
برای این منظور تنها کافی است ویژگی accept را به input از نوع فایل اضافه کرد. چند مثال در این مورد:
<!-- Content Type with wildcard. All Images --> <input type="file" id="demoFile" title="Select file" accept="image/*" /> <!-- List of file extensions --> <input type="file" id="demoFile" title="Select file" accept=".jpg,.png,.gif" /> <!-- Any combination of the above --> <input type="file" id="demoFile" title="Select file" accept="audio/*,application/pdf,.png" />
نمایش متن کشیدن و رها کردن، بومی سازی برچسبها و نمایش راست به چپ
همانطور که در تصاویر فوق ملاحظه میکنید، نمایش این ویجت راست به چپ و پیامهای آن نیز ترجمه شدهاند.
برای راست به چپ سازی آن مانند قبل تنها کافی است input مرتبط، در یک div با کلاس k-rtl محصور شود:
<div class="k-rtl k-header"> <input name="files" id="files" type="file" /> </div>
<script type="text/javascript"> $(function () { $("#files").kendoUpload({ name: "files", async: { //... }, //... localization: { select: 'انتخاب فایلها برای ارسال', remove: 'حذف فایل', retry: 'سعی مجدد', headerStatusUploading: 'در حال ارسال فایلها', headerStatusUploaded: 'پایان ارسال', cancel: "لغو", uploadSelectedFiles: "ارسال فایلها", dropFilesHere: "فایلها را برای ارسال، کشیده و در اینجا رها کنید", statusUploading: "در حال ارسال", statusUploaded: "ارسال شد", statusWarning: "اخطار", statusFailed: "خطا در ارسال" } }); }); </script>
<style type="text/css"> div.k-dropzone { border: 1px solid #c5c5c5; /* For Default; Different for each theme */ } div.k-dropzone em { visibility: visible; } </style>
تغییر قالب نمایش لیست فایلها
لیست فایلها در ویجت kendoUpload دارای یک قالب پیش فرض است که امکان بازنویسی کامل آن وجود دارد. ابتدا نیاز است یک kendo-template را بر این منظور تدارک دید:
<script id="fileListTemplate" type="text/x-kendo-template"> <li class='k-file'> <span class='k-progress'></span> <span class='k-icon'></span> <span class='k-filename' title='#=name#'>#=name# (#=size# bytes)</span> <strong class='k-upload-status'></strong> </li> </script>
<script type="text/javascript"> $(function () { $("#files").kendoUpload({ name: "files", async: { // ... }, // ... template: kendo.template($('#fileListTemplate').html()), // ... }); }); </script>
رخدادهای ارسال فایلها
افزونهی kendoUpload در حالت ارسال Ajax ایی فایلها، رخدادهایی مانند شروع به ارسال، موفقیت، پایان، درصد ارسال فایلها و امثال آنرا نیز به همراه دارد که لیست کامل آنها را در ذیل مشاهده میکنید:
<script type="text/javascript"> $(function () { $("#files").kendoUpload({ name: "files", async: { // async configuration //... }, //... localization: { }, cancel: function () { console.log('Cancel Event.'); }, complete: function () { console.log('Complete Event.'); }, error: function () { console.log('Error uploading file.'); }, progress: function (e) { console.log('Uploading file ' + e.percentComplete); }, remove: function () { console.log('File removed.'); }, select: function () { console.log('File selected.'); }, success: function () { console.log('Upload successful.'); }, upload: function (e) { console.log('Upload started.'); } }); }); </script>
ارسال متادیتای اضافی به همراه فایلهای ارسالی
فرض کنید میخواهید به همراه فایلهای ارسالی به سرور، پارامتر codeId را نیز ارسال کنید. برای این منظور باید خاصیت e.data رویداد upload را به نحو ذیل مقدار دهی کرد:
<script type="text/javascript"> $(function () { $("#files").kendoUpload({ name: "files", async: { //... }, //... localization: { }, upload: function (e) { console.log('Upload started.'); // Sending metadata to the save action e.data = { codeId: "1234567", param2: 12 //, ... }; } }); }); </script>
[HttpPost] public ActionResult Save(IEnumerable<HttpPostedFileBase> files, string codeId)
فعال سازی ارسال batch
اگر در متد Save سمت سرور یک break point قرار دهید، مشاهده خواهید کرد که به ازای هر فایل موجود در لیست در سمت کاربر، یکبار متد Save فراخوانی میشود و عملا متد Save، لیستی از فایلها را در طی یک فراخوانی دریافت نمیکند. برای فعال سازی این قابلیت تنها کافی است خاصیت batch را به true تنظیم کنیم:
<script type="text/javascript"> $(function () { $("#files").kendoUpload({ name: "files", async: { // .... batch: true }, }); }); </script>
در یک چنین حالتی باید دقت داشت که تنظیم maxRequestLength در web.config برنامه الزامی است؛ زیرا به صورت پیش فرض محدودیت 4 مگابایتی ارسال فایلها توسط ASP.NET اعمال میشود:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <!-- The request length is in kilobytes, execution timeout is in seconds --> <httpRuntime maxRequestLength="10240" executionTimeout="120" /> </system.web> <system.webServer> <security> <requestFiltering> <!-- The content length is in bytes --> <requestLimits maxAllowedContentLength="10485760"/> </requestFiltering> </security> </system.webServer> </configuration>
آشنایی با Flex Layout Box Model
برای طراحی ظاهر یک برنامهی وب نیاز است عناصر آنرا در مکانهای مختلفی از صفحه قرار داد که به آن Layout گفته میشود. برای این منظور عموما 4 روش ذیل مرسوم هستند:
1. Table
2. Float, position, clear
3. CSS Grids
4. FlexBox CSS
امروزه دیگر آنچنان روشهای 1 و 2 به صورت مستقیم مورد استفاده قرار نمیگیرند. CSS Grid روش نهایی طراحی Layout در آینده خواهد بود و در حال حاضر تعداد مرورگرهایی که از آن پشتیبانی میکنند، قابل توجه نیست؛ اما از FlexBox در IE 11، کروم 21 و فایرفاکس 22 به بعد پشتیبانی میشود.
FlexBox CSS، سیلان المانهای قرار گرفتهی در داخل آنرا سبب میشود. در اینجا یک container اصلی وجود دارد که در برگیرندهی المانها است. در تصویر فوق دو محور را مشاهده میکنید. محور افقی از چپ به راست ادامه پیدا میکند. محور عمودی نحوهی ارتباط عناصر را مشخص میکند.
اکنون این سؤال مطرح میشود که چه تفاوتی بین یک Grid و FlexBox CSS وجود دارد؟ در یک Grid طراحی دو بعدی سطرها و ستون وجود دارد. اما به FlexBox باید به صورت سیلان یک بعدی سلولها نگاه کرد. برای مثال عناصر قرار گرفتهی درون Container یا به صورت افقی درون آن گسترده شده و قرار میگیرند و یا به صورت عمودی.
نحوهی تفکر و کارکرد با FlexBox چگونه است؟
در اینجا باید به دو مفهوم دقت داشت:
الف) سیلان عناصر درون Container که میتواند افقی و یا عمودی باشد.
ب) اندازهی المانها که میتواند ثابت و یا نسبی باشد.
یک Container، جهت سیلان عناصر درون آنرا مشخص میکند. المانهای آن، اندازه، فاصلهی از یکدیگر و ترتیب قرارگیری را ارائه میدهند. یک flex container میتواند شامل چندین flex container تو در تو نیز باشد.
نحوهی سیلان عناصر در FlexBox چگونه است؟
برای نمونه طرحبندی متداول ذیل را درنظر بگیرید:
نحوه تفکر در مورد طراحی این طرحبندی، باید از بیرون به درون و از بالا به پایین (سیلان عمودی) باشد:
سپس نحوهی سیلان عناصر درون Containerهای تعریف شده را مشخص میکنیم. برای مثال اولین Container دارای سیلان افقی از چپ به راست خواهد بود که عنصر سوم آن به دلیل اندازههای مشخص دو عنصر قبلی، به سطر دوم منتقل شدهاست.
در ادامه به قسمت میانی میرسیم که آن نیز دارای سیلان افقی از چپ به راست است:
در اینجا نیز میتوان سه Container را متصور شد که وسطی دارای سیلان افقی از راست به چپ است و مواردی که بر اساس اندازهی آنها در یک سطر جا نشدهاند، به سطر بعدی منتقل خواهند شد:
و تمام این سیلانها و انتقال به سطرهای بعدی بر اساس اندازهی المانها صورت میگیرد:
البته در این تصویر یک ایراد هم وجود دارد. با توجه به اینکه در ناحیهی میانی سه Container تعریف خواهند شد. Container ایی که در میان آن قرار میگیرد، دارای سیلان خاص خودش است و اندازههای آن باید نسبت به این Container تعریف شوند و نه نسبت به کل ناحیهی میانی. یعنی بجای اینکه 50 درصد، 25، 25 و 50 درصد را داشته باشیم، اینها در اصل 100 درصد، 50 و 50 درصد و سپس 100 درصد هستند.
معرفی کتابخانهی Angular Flex Layout
برای کار با Flex CSS نیاز است:
- مقدار زیادی کد CSS نوشت.
- نیاز به درک عمیقی از Flex Box دارد.
- نیاز است با باگهای مرورگرها و تفاوتهای پیاده سازیهای آنها در مورد FlexBox آشنا بود.
- نیاز به Prefixing دارد.
- برای Angular طراحی نشدهاست.
جهت رفع این مشکلات و محدودیتها، تیم Angular کتابخانهای را به نام Angular Flex Layout مخصوص نگارشهای جدید Angular طراحی کردهاست. این کتابخانه مستقل از Angular Material است اما عموما به همراه آن استفاده میشود.
مزایای کار با کتابخانهی Angular flex layout
- پیاده سازی TypeScript ایی دارد. در اصل یک سری directives مخصوص Angular است که با TypeScript نوشته شدهاست.
- به صورت پویا و inline تمام CSSهای مورد نیاز را تولید و تزریق میکند.
- به همراه یک API استاتیک است و همچنین یک API واکنشگرا
- با Angular CLI نیز یکپارچه شدهاست.
نصب و تنظیم کتابخانهی Angular Flex layout
برای نصب این کتابخانه، در ریشهی پروژه دستور زیر را صادر کنید:
npm install @angular/flex-layout --save
import { FlexLayoutModule } from "@angular/flex-layout"; @NgModule({ imports: [ FlexLayoutModule ], exports: [ FlexLayoutModule ] }) export class SharedModule { }
کار با API استاتیک Angular Flex layout
API استاتیک Angular Flex layout شامل این مزایا و مشخصات است:
- به صورت یکسری دایرکتیو Angular طراحی شدهاست که به HTML قالب کامپوننتها اضافه میشود.
- از data binding پشتیبانی میکند.
- CSS نهایی را به صورت پویا و inline تولید و به صفحه تزریق میکند. Inline CSS تزریق شده به ویژگیهای styles هر المان تزریق میشوند و موارد مشابه را در صورت وجود بازنویسی میکنند.
- از تشخیص تغییرات پشتیبانی میکند.
- به همراه ویژگیهای fxHide و fxShow است.
- کارآیی مطلوبی دارد.
در اینجا برای تعریف container اصلی از دایرکتیوهای زیر استفاده میشود:
- fxLayout جهتهای flex را مشخص میکند.
<div fxLayout="row" fxLayout.xs="column"></div>
- fxLayoutWrap مشخص میکند که آیا المانها باید به سطر و یا ستون بعدی منتقل شوند یا خیر؟
<div fxLayoutWrap></div>
<div fxLayoutGap="10px"></div>
<div fxLayoutAlign="start stretch"></div>
چند مثال:
و یا حالت راست به چپ آن به صورت زیر است:
و برای تعریف آیتمهای قرار گرفتهی درون containers میتوان از دایرکتیوهای زیر استفاده کرد:
- fxflex برای تعیین اندازه و flex المانها
<div fxFlex="1 2 calc(15em + 20px)"></div>
fxFlex="grow shrink basis"
fxFlex="basis"
- shrink به این معنا است که اگر به اندازهی کافی فضا وجود نداشت، این المان نسبت به سایر المانهای دیگر تا چه اندازهای میتواند کوچک شود.
- basis به معنای اندازهی پیشفرض المان است.
در اینجا اندازهها برحسب پیکسل، درصد و یا calcs, em, cw, vh میتوانند تعیین شوند. همچنین یک سری نام مستعار مانند grow, initial, auto, none, nogrow, noshrink هم قابل استفاده هستند.
- fxflexorder برای تعیین ترتیب قرارگیری یک المان
<div fxFlexOrder="2"></div>
<div fxFlexOffset="20px"></div>
<div fxFlexAlign="center"></div>
<div fxFlexFill></div>
چند مثال:
در اینجا سه نمایشی را که در ذیل تعریف divها مشاهده میکنید بر اساس تغییر اندازهی صفحه حاصل شدهاند. چون آیتم دوم دارای مقدار grow مساوی 5 است، به همین جهت با تغییر اندازهی صفحه و دسترسی به مقدار فضای بیشتر، بزرگتر شدهاست.
یک مثال کامل
اگر علاقمند باشید تا توانمندیهای angular flex layout را در قالب یک مثال کامل مشاهده کنید، به آدرس زیر مراجعه نمائید:
https://tburleson-layouts-demos.firebaseapp.com/#/docs
در این مثال با تغییر گزینههای مختلف، کد معادل angular flex layout آن نیز تولید میشود.
همچنین wiki خود پروژه نیز به همراه مثالهای بیشتری است:
https://github.com/angular/flex-layout/wiki
کار با API واکنشگرای Angular Flex layout
در طراحی واکنشگرا، container و عناصر داخل آن بر اساس تغییرات اندازهی صفحه و یا اندازهی وسیلهی نمایشی، تغییر اندازه و همچنین موقعیت میدهند و این تغییرات بر اساس انطباق با viewport وسیلهی نمایشی صورت میگیرند. به همین جهت برای طراحی واکنشگرا نیاز به Flex CSS و همچنین Media Query است. نوشتن Medial Query و ترکیب آن با Flex CSS کار مشکلی است. به همین جهت Angular Flex layout به همراه یک API واکنشگرا نیز هست که در پشت صحنه Flex CSS را بر اساس طراحی متریال و Medial Queries مورد استفاده قرار میدهد.
اگر علاقمند هستید تا اندازههای واکنشگرای استاندارد متریال را ملاحظه کنید، میتوانید به آدرس زیر مراجعه نمائید (قسمت Breakpoint system آن):
https://material.io/design/layout/responsive-layout-grid.html#breakpoints
برای مثال هر اندازهای کمتر از 600px در گروه extra small قرار میگیرد (با مخفف xs). از 600px تا 1024px در بازهی small (با مخفف sm)، از 1024px تا 1440px در بازهی medium (با مخفف md) و از 1440px تا 1920px در بازهی large (با مخفف lg) و بیشتر از آن در بازهی xlrage قرار میگیرند (با مخفف xl). این اعداد و بازهها، پایهی طراحی API واکنشگرای Angular Flex layout هستند. به همین جهت نام این بازهها در این API به صورت مخفف xs, sm, md, lg, xl درنظر گرفته شدهاند و مورد استفاده قرار میگیرند. همچنین اگر اندازههای مدنظر از این بازهها کمتر باشند، میتوان از lt-sm, lt-md, lt-lg, lt-xl نیز استفاده کرد. در اینجا lt به معنای less than است و یا اگر بازههای مورد نیاز بیش از این اندازهها باشند میتوان با gt-xs, gt-sm, gt-md, gt-lg کار کرد. در اینجا gt به معنای greater than است.
به این مخففها «media query alias» هم گفته میشود و اکنون که لیست آنها مشخص است، تنها کافی است آنها را به API استاتیکی که پیشتر بررسی کردیم، اضافه کنیم. برای مثال:
fxLayout.sm = "..." fxLayoutAlign.md = "..." fxHide.gt-sm = "..."
معادل طراحی آن با API استاتیک Angular Flex Layout به صورت زیر است:
اکنون که این طرحبندی دسکتاپ را داریم، چگونه باید آنرا تبدیل به طرحبندی موبایل، مانند شکل زیر کنیم؟
برای اینکار ابتدا fxLayout.xs را به سطر میانی اضافه میکنیم تا هرگاه به این اندازه رسیدیم، بجای ردیف، تبدیل به ستون شود. سپس توسط fxFlexOrder.xs، در اندازهی xs، محل قرارگیری المانهای این ستون را هم مشخص میکنیم:
همانطور که ملاحظه میکنید کار کردن با این API بسیار سادهاست و نیازی به کارکردن مستقیم با Media Queries و یا برنامه نویسی مستقیم ندارد و تمام آن در قالب HTML یک کامپوننت قابل پیاده سازی است.
یک نکته: مثال کاملی که پیشتر در این بحث مطرح شد، به همراه مثال واکنشگرا نیز هست که برای مشاهدهی اثر آنها بهتر است اندازهی مرورگر را کوچک و بزرگ کنید.
مخفی کردن و یا نمایش قسمتی از صفحه بر اساس اندازهی آن
علاوه بر media query alias هایی که عنوان شد، امکان نمایش و یا مخفی سازی قسمتهای مختلف صفحه بر اساس اندازهی صفحهی نمایشی نیز هست:
<div fxShow fxHide.xs="false" fxHide.lg="true"></div>
تغییر اندازهی قسمتی از صفحه بر اساس اندازهی آن
در مثال زیر اگر اندازهی صفحه gt-sm باشد (بیشتر از small)، اندازهی این div به 100 درصد بجای 50 درصد حالتهای دیگر، تنظیم میشود:
<div fxFlex="50%" fxFlex.gt-sm="100%"></div>
حالتهای ویژهی طراحی واکنشگرا در Angular Flex Layout
در API واکنشگرای آن حالتهای ویژهی fxshow, fxhide, ngclass و ngstyle نیز درنظر گرفته شدهاند که امکان فعالسازی آنها در اندازههای مختلف صفحه مسیر است:
<div fxShow [fxShow.xs]="isVisibleOnMobile()"></div> <div fxHide [fxHide.gt-sm]="isVisibleOnDesktop()"></div> <div [ngClass.sm]="{'fxClass-sm': hasStyle}" ></div> <div [ngStyle.xs]="{color: 'blue'}"></div>
امکان کار با API واکنشگرا از طریق برنامه نویسی
برای این منظور میتوان از سرویس ObservableMedia مانند مثال زیر استفاده کرد:
در اینجا به فعالسازی یک بازهی خاص گوش فرا خواهیم داد. برای مثال اگر اندازهی صفحه xs بود، سبب بارگذاری محتوای خاص مرتبط با موبایل خواهیم شد.
برای مطالعهی بیشتر
emit$:
دو کنترلر به نامهای FirstCtrl و SecondCtrl داریم. FirstCtrl به عنوان والد کنترلر Second است(در این مورد در این پست توضیح داده شده است).
پس فایل html نیز به صورت زیر خواهد بود:
<body ng-app> <div ng-controller="FirstCtrl"> <p>{{title}}</p> <div ng-controller="SecondCtrl"> <button ng-click="onUpdate()">Update First Ctrl Title</button> </div> </div> </body>
function ChildCtrl($scope){ $scope.onUpdate = function(){ this.$emit("Update_Title", "Good Bye"); }; }
function FirstCtrl($scope){ $scope.title= "Hello"; $scope.$on("Update_Title", function(event, message){ $scope.title= message; }); }
»پارامتر دوم سرویس on$ برابر با مقدار جدید ارسال شده توسط سرویس emit$ است.
»نام رویدادی که به عنوان پارامتر به on$ پاس داده میشود باید برابر با نام رویداد پاس داده شده به emit$ باشد.
»میتوان چندین پارامتر را با استفاده از emit$ ارسال کرد و در سرویس on$ با تعریف متغیر به تعداد پارامترها مقادیر آنها را دریافت نمود.
broadcast$
همان طور که مشاهده کردید SecondCtrl در محدوده FirstCtrl تعریف شده است. در نتیجه به راحتی با استفاده از سرویس emit$ توانستیم یک رویداد را منتشر نماییم. اما نکته مهم این است که اگر قصد داشته باشیم یک رویداد را از کنترلر والد (در این جا FirstCtrl است) منتشر نماییم به طوری که در کنترلرهای فرزند قابل دریافت باشد(حرکت رویداد بالا به پایین است)، باید از broadcast$ استفاده کنیم.
»broadcast$ فقط از نظر کاربرد با emit$ متفاوت است و در پیاده سازی کاملا مشابه هستند.
یک مثال:
function ParentCtrl($scope){ $scope.foo = "Hello"; $scope.$on("UPDATE_PARENT", function(event, message){ $scope.title= message; $scope.$broadcast("DO_BIDDING", { buttonTitle : "Taken over", onButtonClick : function(){ $scope.title= "HAHA this button no longer works!"; } }); }); } function ChildCtrl($scope){ $scope.buttonTitle = "Update Parent"; $scope.onButtonClick = function(){ this.$emit("UPDATE_PARENT", "Updated"); }; $scope.$on("DO_BIDDING", function(event, data){ for(var i in data){ $scope[i] = data[i]; } }); }
اگر حالت فرزند و والد بین کنترلرها نباشد چه؟
در این حالت باید rootScope$ را به کنترلر مورد نظر تزریق نمایید و سپس با استفاده از سرویس broadcast$ یا emit$ رویدادتان را منتشر کنید. مثال:
'use strict'; angular.module('myAppControllers', []) .controller('FirstCtrl', function ($rootScope) { $rootScope.$broadcast('UPDATE_ALL'); Or $rootScope.$emit('UPDATE_ALL'); });
از آن جا که حرکت بالا به پایین event bubbling بسیار هزینه برتر است نسبت به حرکت پایین به بالا در نتیجه سعی کنید تا جای ممکن از rootScope$.$broadcast استفاده نکنید. در این جا توضیح کاملی درباره دلایل عدم استفاده از rootScope$.$broadcast داده شده است.
هم چنین میتوانید یک مثال Live را نیز برای مقایسه بین emit$ و broadcast$ در این جا مشاهده کنید.
Thickbox یکی از پلاگینهای jQuery است که جهت نمایش صفحات modal بکار میرود.
روش استفاده بسیار سادهای هم دارد:
الف) jquery.js باید به صفحه معرفی شود.
ب) سپس thickbox.js باید به صفحه الحاق شود.
ج) و فایل css آن یعنی thickbox.css نیز باید به صفحه افزوده شود.
برای استفاده از آن کافی است یک لینک به صفحه اضافه کنید که ویژگی class آن مساوی thickbox باشد. مثلا:
<a href="#TB_inline?height=155&width=300&inlineId=hiddenModalContent&modal=true" class="thickbox">Show hidden modal content.</a>
<a href="iframeModal.html?placeValuesBeforeTB_=savedValues&TB_iframe=true&height=200&width=300&modal=true" title="add a caption to title attribute / or leave blank" class="thickbox">Open iFrame Modal</a>
و مثالهای دیگری از این دست که در صفحه رسمی آن موجود است و نیازی به تکرار آنها نیست.
اما مشکلی که در بلاگر وجود دارد این است که شما نمیتوانید این ویژگیها را در بسیاری از موارد به صورت دستی تنظیم کنید. یک ویجت ظاهر میشود که فقط به شما امکان ثبت یک عنوان و لینک مربوطه را میدهد و همین.
برای انتساب ویژگیهای thickBox به این نوع لینکها که کنترلی روی آن نیست میتوان از خود jQuery کمک گرفت:
$(document).ready(function(){
$("a[href='http://www.mysite.com/page.htm?TB_iframe=true&height=340&width=530']").each(function(){
var obj = $(this);
obj.attr("title","Contact me");
obj.attr("class","thickbox");
}).bind("contextmenu",function(e){
return false;
});
});
همچنین در اینجا کلیک راست نیز بر روی این لینک بسته شده تا کاربر فقط کلیک معمولی را انجام دهد (فقط روی همین یک لینک در صفحه اعمال میشود و نه کل صفحه).