بهبود SEO در ASP.NET MVC
<a href="/contactus?name=وحید نصیری" rel="nofollow">ارسال پیام خصوصی</a>
بعضا دیده میشود که برنامه نویس یک Paging را به صورت Ajax ی پیاده سازی میکند که با تغییر صفحهها، اطلاعات را خوانده و به کاربر نمایش میدهد و هیچ اتفاقی در آدرس بار صفحه نمیافتد و خیلی خرسند است از کاری که انجام دادهاست. در چنین مواردی اگر کاربر استفاده کننده از برنامه بخواهد لینک صفحه دهم گرید و یا لیست اطلاعات را برای کسی بفرستد، باید چکار کند؟
توضیح بالا صرفا به این دلیل بیان شد تا به ضرورت داشتن Urlهای منحصر بفرد برای هر Page برسیم؛ هر چند بحث جاری درباره سئو نیست. ولی ترجیح دادم در کنار موضوع مقاله، توجهی هم داشته باشیم به این موضوع که رعایت آن حس بهتری را به کاربران برنامه میدهد.
کتابخانههای زیادی برای صفحه بندی اطلاعات وجود دارند و یا اینکه بعضی از برنامه نویسان و یا شرکتها ترجیح میدهند خود چرخ را مطابق میل خود از نو طراحی کنند. در ادامه قصد داریم به پیاده سازی کتابخانه PagedList که خیلی محبوب و پر طرفدار میباشد در ASP.NET MVC بپردازیم.
1- ابتدا قبل از هر کاری، با استفاده از دستور زیر اقدام به نصب کتابخانه آن مینماییم:
Install-Package PagedList.Mvc
3- در کنترلر فقط کافیست متد ToPagedList را فراخوانی کرده و مقدار بازگشتی را به View ارسال نمود.
4 - و در نهایت در داخل View (ها) فقط کافیست برای نمایش صفحه بندی، دستور Html.PagedListPager را فراخوانی کنیم.
چهار مورد فوق تقریبا تمام کارهایی است که باید انجام شوند تا قادر باشیم از این کتابخانه برای صفحه بندی اطلاعات استفاده کنیم. در ادامه نحوه پیاده سازی آن را به همراه چند مثال بیان میکنیم.
ابتدا یک مدل فرضی را همانند زیر تهیه میکنیم:
public class Post { public int Id { get; set; } public string Title { get; set; } public string Body { get; set; } }
public static class PostService { public static IEnumerable<Post> posts = new List<Post>() { new Post{Id=1,Title="Title 1",Body="Body 1"}, new Post{Id=2,Title="Title 2",Body="Body 2"}, new Post{Id=3,Title="Title 3",Body="Body 3"}, new Post{Id=4,Title="Title 4",Body="Body 4"}, new Post{Id=5,Title="Title 5",Body="Body 5"}, new Post{Id=6,Title="Title 6",Body="Body 6"}, new Post{Id=7,Title="Title 7",Body="Body 7"}, new Post{Id=8,Title="Title 8",Body="Body 8"}, new Post{Id=9,Title="Title 9",Body="Body 9"}, new Post{Id=10,Title="Title 10",Body="Body 10"}, new Post{Id=11,Title="Title 11",Body="Body 11"}, new Post{Id=12,Title="Title 12",Body="Body 12"}, }; public static IEnumerable<Post> GetAll() { return posts .OrderBy(row => row.Id); } }
public ActionResult Index(int? page) { var pageNumber = page ?? 1; var posts = PostService.GetAll(); var result = posts.ToPagedList(pageNumber, 10); ViewBag.posts = result; return View(); }
@using PagedList.Mvc; @using PagedList; <link href="/Content/PagedList.css" rel="stylesheet" type="text/css" /> <h2>List of posts</h2> <ul> @foreach (var post in ViewBag.posts) { <li>@post.Title</li> } </ul> @Html.PagedListPager((IPagedList)ViewBag.posts, page => Url.Action("Index", new { page }))
بله، درست حدس زدهاید! این روش دارای یک عیب میباشد و آن این است که ابتدا ما تمامی رکوردها را از دیتابیس فراخونی کرده و بعد از آن به تعداد 10 رکورد را از آن انتخاب نمودهایم. هر چند در مثال جاری تعداد رکوردها زیاد نمیباشد، ولی با مرور زمان و حجیم شدن دیتابیس، کوئری فوق امکان دارد به کندی اجرا شود. به همین دلیل نیاز به متدی داریم که با توجه به صفحه جاری، تعداد n رکورد را از دیتابیس خوانده و نمایش دهد. برای این منظور متدی همانند زیر را به کلاس postService اضافه مینماییم:
public static IEnumerable<Post> GetAll(int page, int recordsPerPage,out int totalCount) { totalCount = posts.Count(); return posts .OrderBy(row => row.Id).Skip(page * recordsPerPage).Take(recordsPerPage); // in real projects change like this .skip(()=>resultforSkip).Take(()=>recordsPerPage ) }
در صفحه بندی به صورت دستی، تا حدودی اکشن Index تغییر خواهد کرد. در این روش داریم:
public ActionResult Index(int? page) { var pageIndex = (page ?? 1) - 1; var pageSize = 10; int totalPostCount; var posts = PostService.GetAll(pageIndex, pageSize, out totalPostCount); var result = new StaticPagedList<Post>(posts, pageIndex + 1, pageSize, totalPostCount); ViewBag.posts = result; return View(); }
مقدار page به صورت کوئری استرینگ به انتهای url اضافه خواهد شد. جهت نظم بخشیدن به آن میبایست اقدام به افزودن route سفارشی نمایید که در حالت پیش فرض به صورت زیر میباشد:
http://localhost:53192/?page=2
routes.MapRoute( name: "paging", url: "{controller}/{action}/{page}", defaults: new { controller = "Home", action = "Index", page = UrlParameter.Optional } );
در روشهایی که شرح آنها گذشت، از viewbag برای انتقال دادهها استفاده کردیم. میتوان view مورد نظر را strongly-typed معرفی نمود و دادهها را از طریق return view به سمت view بفرستید. در این حالت در view مربوطه داریم :
@model PagedList.IPagedList <pagedListmvc.Models.Post>
@foreach (var post in Model) { <li>@post.Title</li> }
سفارشی کردن Url: فرض کنید همراه با کلیک بر روی شمارهی صفحات، بخواهیم یک سری دیتای دیگر را هم به اکشن پاس دهیم؛ برای مثال tag=mvc. برای این منظور داریم:
@Html.PagedListPager( myList, page => Url.Action("Index", new { page = page, tag= "mvc" }) )
استایل خروجی html حاصل از Html.PagedListPager بر اساس کتابخانه Bootstrap میباشد. در صورتیکه در پروژه خود از این کتابخانه استفاده نمیکنید، میتوانید فقط فایل PagedList.css را از Nuget دریافت نموده و به پروژهی خود اضافه نمایید.
یکی از overloadهای Html.PagedListPager پارامتری را تحت عنوان PagedListRenderOptions دارد که از آن میتوانید برای پیکربندی صفحه بندی استفاده نمایید. برای نمونه نمایش فقط 5 صفحه:
@Html.PagedListPager((IPagedList)ViewBag.posts, page => Url.Action("Index", new { page = page }), PagedListRenderOptions.OnlyShowFivePagesAtATime)
@Html.PagedListPager((IPagedList)ViewBag.posts, page => Url.Action("Index", new { page = page }), new PagedListRenderOptions { LinkToFirstPageFormat = "<< ابتدا", LinkToPreviousPageFormat = "< قبلی", LinkToNextPageFormat = "بعدی>", LinkToLastPageFormat = "آخرین >>" })
در این آموزش قصد دارم نحوه ایجاد یک Slideshow به صورت داینامیک را با ASP.NET طراحی کنم(منظور از ایجاد Slideshow به صورت داینامیک این است که عکسها را به صورت داینامیک از DB بخواند).
اولین گام این است که Plugin مورد نظر را دریافت کنید که من از پلاگین Orbit استفاده کرده ام
ابتدا یک DataBase با نام DynamicSlideShow ایجاد و یک جدول با ساختار زیر با نام Picturesدرون آن ایجاد میکنیم
گام بعدی ایجاد یک پروژه Asp.Net با زبان C# و اضافه کردن فایلهای پلاگین به پروژه و ایجاد یک Handler برای بازیابی دادهها از DB است. ساختار Solutionما باید به صورت زیر باشد
برای اینکه بتوانیم با DB ارتباط برقرار کنیم از EF استفاده میکنیم به همین منظور ابتدا یک Model به نام DynamicSlideShowModel ایجاد میکنیم
در گام بعد بر روی GenerateFromDatabase انتخاب کرده و بر روی دکمه Next کلیک میکنیم و در مراحل بعد ابتدا DB مربوط به مثال (DynamicSlideShow) و جدول آن را انتخاب میکنیم . حال یک Model به درون پوشه App_Code اضافه شده استدر ادامه برای واکشی رکوردهای موجود در جدول Pictures کدهای زیر را درون Handler مینویسیم
var ctx = new DynamicSlideShowEntities(); var list = ctx.Pictures.ToList(); string str = JsonConvert.SerializeObject(list); context.Response.Write(str);
در این کدها تنها نکتهای که احتیاج به توضیح دارد این است که ابتدا یک لیست از
رکوردها را از جدول Pictures
واکشی میکنیم و برای پاس دادن به کلاینت ما آنها را به فرمت Json تبدیل کرده ایم که برای تبدبل از
کنابخانه آماده Newtonsoft.Json.dll
استفاده کرده ایم
حال باید با استفاده از jQuery کدهای درون Handler را اجرا کنیم؛ برای همین منظور یک صفحه با نام default.aspx ایجاد کرده و کدهای زیر را درون آن مینویسیم
<head runat="server"> <title>Dynamic SlideShow</title> <link href="CSS/orbit-1.2.3.css" rel="stylesheet" type="text/css" /> <script src="Script/jquery-1.7.1.min.js" type="text/javascript"></script> <script src="Script/jquery.orbit-1.2.3.min.js" type="text/javascript"></script> <script type="text/javascript"> $(function () { $.ajax({ url: "Handler.ashx", contentType: "application/json; charset=utf-8", success: function (data) { $.each(data, function (i, b) { var str = '<img src="' + b.PicturePath + '" alt="' + b.PictureText + '"/>'; $("#featured").append(str); }); $('#featured').orbit(); }, dataType: "json" }); }); </script> </head> <body> <form id="form1" runat="server"> <div id="featured"> </div> </form> </body>
در اینجا ابتدا با استفاده از متد ajax کتابخانه jQuery کدهای درون Handler را اجرا کرده و به ازای هر المان موجود در جدول یک تگ img به صفحه اضافه میکنیم.
PM> Install-Package jquery.mobile
<meta name="viewport" content="width=device-width">
<head> <meta name="viewport" content="width=device-width,initial-sclae=1" /> <link href="Content/jquery.mobile-1.1.0.css" rel="stylesheet" type="text/css" /> <script src="Scripts/jquery-1.6.4.js" type="text/javascript"></script> <script src="Scripts/jquery.mobile-1.1.0.js" type="text/javascript"></script> <title></title> </head> <body> <div data-role="page"> <div data-role="header" data-theme="b"> <h1>this is a test </h1> </div> <div data-role="conent"> <ul data-role="listview" data-filter="true" data-inset="true" data-theme="e"> <li><a href="#">Water</a></li> <li><a href="#">Pepsi</a></li> <li><a href="#">Diet Pepsi</a></li> <li><a href="#">Beer</a></li> <a href="#" data-role="button" data-theme="b">Click ME</a> </ul> </div> <div data-role="footer" data-theme="b" data-position="fixed"> <h1>footer </h1> </div> </div> </body>
data-role="page"
" data-role="header
"data-theme="e
"data-role="listview
"data-filter="true
" data-inset="true"
<link href="admin/editor/contents.css" rel="stylesheet" type="text/css" /> <script src="admin/editor/ckeditor.js" type="text/javascript"></script>
var clientId = "txtPostBody"; var strEditor = strBodyFa = "CKEDITOR.replace( '" + clientId + "',{skin : 'office2003' , language : 'fa' , contentsLangDirection : 'rtl'});"; ClientScript.RegisterStartupScript(GetType(), clientId + "_Editor", strBodyFa, true);
نکته: پوشه مربوط به ادیتور در محلی که در دسترس صفحه باشد باید قرار گیرد که در اکثر موارد قسمت ادمین وب یا اپلیکشن است
همان طور که میدونید مکانیزم عملکرد اکثر پلاگینهای lightbox به صورت زیر است:
<a href="orginal size of image directory"><img src="thumb(smaller size) image directory" /></a>
خب معلوم میشه که آدرس عکسی که به صورت کوچیک)اصطلاحا بند انگشتی) به نمایش در میاد باید داخل تگ img ، وآدرس عکس با سایز اصلی ، داخل تگ a قرار بگیره.
پس تقریبا معلوم شد که چه باید بکنیم.
ابتدا باید عکسی که آپلود شده را به صورت خودکار به اندازه کوچکتر تغییر ابعاد داده و سپس آدرس عکس تغییر ابعاد داده شده ، را به عنوان آدرس عکس آپلود شده به ckeditor باز گردانیم که در شکل زیر آن را نشان داده ام:
ولی ما فقط آدرس عکس تغییر ابعاد داده شده متعلق به تگ img را ، به ckeditor داده ایم و برای اینکه با lightbox به خوبی کار کند، احتیاج داریم که آدرس عکس با سایز اصلی را داخل تگ a قرار دهیم، که در ckeditor میتوانیم از قسمت پیوندها این کار را انجام دهیم، و همهی این عملیات باید به صورت خودکار انجام شود.خب تا اینجا خلاصه ای از آنچه باید انجام شود را گفتم.
در کل منطقی که باید پیاده شود بدین گونه است:
1- پیاده سازی ckeditor
2- فعال کردن قسمت آپلود عکس ckeditor
3- آپلود کردن عکس و تغییر اندازه آن به کمک کلاس WebImage
4- برگرداندن آدرس عکسهای آپلود شده به ckeditor و روشهای پیاده سازی آن
نکات:
الف) من در اینجا از ASP.NET MVC استفاده میکنم .شما نیز میتوانید منطق پیاده سازی شده را به راحتی و با کمی تغییرات به پروژه خود اعمال کنید.
ب) کدهایی که من نوشتم 100% بهینه نیستند و کاملا هم بدون اشکال نیستند ولی نیازهای شما را برآورده میکند.
ج)آدرس دادن فایلهای من کاملا صحیح نیستند و بهتر است شما از T4MVC استفاده کنید.
مرحله اول: دانلود و پیاده سازی ckeditor در صفحه
ابتدا به سایت ckeditor.com مراجعه و از قسمت دانلود، آن را دانلود کرده و سپس از حالت فشرده درآورده، در فولدری مثلا به نام Scripts در پروژهی خود بریزید.
همان طور که میدانید ckeditor با یک textarea یکپارچه میشود ، که من سادهترین آن را برای شما شرح میدهم.
اول از همه شما باید یک فایل جاوا اسکریپت متعلق به ckeditor را در صفحه ای که از آن میخواهید استفاده کنید به صفحه ضمیمه کنید. نام این فایل ckeditor.js هست که در فایل دانلودی در داخل پوشهی ckeditor دیده میشود.
<script src="/Scripts/ckeditor/ckeditor.js" type="text/javascript"></script>
خب فرض کنید که یک textarea به شکل زیر داریم. برای اینکه این textarea با ckeditor یکپارچه شود کافیست که class آن را بر روی ckeditor قرار دهید.
<textarea id="content" name="content" class="ckeditor" ></textarea>
تا به اینجای کار توانستیم ckeditor را اجرا کنیم.
در مرحله بعد سراغ فعال سازی آپلود عکس در آن میرویم.
مرحله دوم: فعال سازی آپلود عکس در ckeditor
از شواهد این طور به نظر میاد که به صورت پیشفرض این امکان غیر فعال است و باید به صورت دستی آن را فعال کنیم.
برای این کار کافیست که از مستندات ckeditor کمک بگیریم.
خب یکی از راحتترین این روشها این است که قبل از بسته شدن تگ بادی فایل جاوا اسکریپت زیر را بنویسیم:
<script type="text/javascript"> CKEDITOR.replace( 'content' , { filebrowserImageUploadUrl: '/Admin/UploadImage' } ); </script>
توضیحات:
آرگومان اول CKEDITOR.replace که در اینجا content است ، در واقع id همان textarea ای هست که میخواهیم ckeditor روی آن اعمال شود.
آرگومان دوم نام کنترلر و اکشن را برای آپلود فایل مشخص میکنه.(از منطق ASP.Net MVC استفاده کردم)
خب تا اینجا اگر تست بگیرید میبینید که قسمت زیر برایتان فعال شده.
مرحله سوم: آپلود کردن عکس و تغییر اندازه آن به کمک کلاس WebImage
برای این که ببینم که این فرم آپلود ckeditor ، چه پارامترهایی را به اکشن متد من ارسال میکنه ، از فایرباگ کمک گرفتم:
همان طور که مییبینید به خوبی آدرس post شدن اطلاعات به اکشن متدی که من برایش مشخص کردم را فهمیده.
اما سه پارامتر دیگر نیز به اکشن متد ما ارسال میکنه: CKEditor و CKeditorFuncNum و langCod
برای اینکه با این پارامترهای ارسالی بیشتر آشنا شوید ، توصیه میکنم این صفحه را ببینید.
آنچه که از این پارامترها برای ما مهم هست ، من در اکشن متد تعریفی خود لحاظ کرده ام.
خب همون طور که یپش از این گفته بودم ما نیاز داریم که عکس آپلود شده را به ابعاد کوچکتر تغییر اندازه داده و اصطلاحا از آن به عنوان تصویر بند انگشتی استفاده کنیم. آدرس این عکس کوچک شده ، همانیست که در آدرس تگ img قرار میگیره و در ابتدا به کاربر نمایش داده میشه.
برای انجام این عمل من کلاسی را برای کار کردن با عکس برایتان معرفی میکنم، به نام WebImage که در داخل فضای نامی System.Web.Helpers قرار گرفته است.
از طریق این کلاس میتوان کلیه عملیات دریافت فایل آپلود شده، ویرایش ، تغییر اندازه ، چرخاندن ، بریدن و حتی watermark کردن و در نهایت ذخیره عکس را به آسانی انجام داد.
من کدهای متد UploadImage را برایتان قرار میدهم که زیاد هم بهینه نیست و سپس برایتان توضیح میدهم.
public ActionResult UploadImage(string CKEditorFuncNum, string CKEditor, string langCode) { string message; // message to display when file upload successfully(optional) string thumbPath = ""; // the directory for thumb file that should resize var db = new MyDbContext(); // make new instance from my context // here logic to upload image // and get file path of the image var file = WebImage.GetImageFromRequest(); // get the uploaded file from request var ext = Path.GetExtension(file.FileName); // get the path of file //get the file name without extension var fileName = Path.GetFileNameWithoutExtension(file.FileName); //add time to file name to avoid same name file overwrite, and then add extension to it fileName += DateTime.Now.ToFileTime() + ext; //choose the path for the original size of image var path = Path.Combine(Server.MapPath("~/Content/UploadedImages/Default"), fileName); file.Save(path); //save the original size of the image db.Images.Add(new Image { RealName = file.FileName, FileName = fileName, UploadDate = DateTime.Now }); // save image info to db db.SaveChanges(); // submit changes to db string defaultPath = "/Content/UploadedImages/Default/" + fileName; //path for original size of //images if (file.Width > 400) // if width of image bigger than 400 px do resize { file.Resize(400, 400, true); //resize the image , third argument is aspect ratio string thumbName = "Thumb-" + fileName; // resized image name //path for resized image file string path2 = Server.MapPath("~/Content/UploadedImages/Thumbs/" + thumbName); thumbPath = "/Content/UploadedImages/Thumbs/" + thumbName; //save resized image file file.Save(path2); } else { thumbPath = defaultPath; // if the size not bigger than 400px the thumb, path = default path } // passing message success/failure message = "Image was saved correctly"; // since it is an ajax request it requires this string //java script that return files path to ckeditor string output = @"<script>window.parent.CKEDITOR.tools.callFunction(" + CKEditorFuncNum + ", \"" + thumbPath + "\", \"" + message + "\");window.parent.document.getElementById('cke_145_textInput').value='" + defaultPath + "';window.parent.document.getElementById('cke_125_textInput').value=0;</script>"; return Content(output); }
توضیحات:
ابتدا یه رشته به نام message در نظر گرفتم ، برای هنگامی که آپلود شد، ckeditor به کاربر نشان بده.
سپس منطق من به این صورت بوده که مسیری برای ذخیره سازی فایلهای تغییر اندازه داده شده ، و نیز مسیری برای فایلهای با اندازه اصلی در نظر گرفتم.
همچنین من در اینجا من از بانک اطلاعاتی برای ذخیره سازی اطلاعاتی از عکس استفاده کردم، که در اینجا بحث اصلی ما نیست.
سپس به کمک WebImage.GetImageFromRequest فایل آپلود شده را دریافت کردم.این متد به اندازه کافی باهوش هست که بفهمد ، چه فایلی آپلود شده.
سپس پسوند فایل را از نام فایل جدا کردم، و تاریخ کنونی را به شکل رشته در آورده و به انتهای نام عکس اضافه کرده تا از تکراری نبودن نام عکسها مطمئن باشم.
سپس پسوند فایل را نیز دوباره به نام فایل اضافه کردم و به کمک متد Save کلاس WebImage عکس را ذخیره کردم.
سپس چک کردم که اگر عرض عکس بیشتر از 400 پیکسل هست ، آن را تغییر اندازه بده و ذخیره کنه، و در غیر این صورت آدرس عکسی که قرار بود تغییر اندازه داده بشه با آدرس عکس اصلی یکی میشه.
قسمت مهم:
نکته مهم اینه که ما آدرسهای عکسهای آپلود شده را چگونه به ckeditor برگردانیم.
همان طوری که در قسمت آخر هم مشاهده میکنید ، ما سه دستور جاوا اسکریپت به مرورگر برگردوندیمم:
اولیش:
window.parent.CKEDITOR.tools.callFunction(" + CKEditorFuncNum + ", \"" + thumbPath + "\", \"" + message + "\");
در حقیقت ما در اینجا ما از apiهای ckeditor و همچنین پارامترهای ارسالی از طرف ckeditor استفاده کردیم ، تا قسمت آدرس عکس را با آدرس عکس تغییر اندازه داده شده و کوچک شده پر میکنیم.
سوال؟
حالا چگونه قسمت پیوند را پر کنیم؟ این را دیگر من پیدا نکردم ، تا دست به دامن دوست و یا شایدم دشمن قدیمیمون جاوا اسکریپت شدم.
اول رفتم به کمک فایرباگ دیدم که id فیلد پیوندها چیه؟
همان طور که معلومه id این فیلد cke_145_textInput هست و به کمک یه خط js میتوان این فیلد را با آدرس عکس آپلود شده با سایز اصلی پر کرد.
اولش من این را نوشتم:
document.getElementsById("cke_145_textInput").value = defaultPath;
اما بازم js شروع کرد به بدقلق شدن. هرچی توی کنسول دیباگش کردم خطای null بودن را میداد. بعد از یه ساعت سرو کله زدن و تقریبا ناامید شدن ، چشمم به قسمت اول کد api خود ادیتور که اولش را با window.parent شروع کرده افتاد ، و من هم کد خودم را به شکل زیر تغییر دادم:
window.parent.document.getElementsById("cke_145_textInput").value = defaultPath;
موفقیت در این قسمت از کد باعث شد که من دست به کدتر شوم و مشکل border عکس ها، که به صورت دیفالت در IE یا همون دشمن همیشگی وجود داره ، را حل کنم ومقدار border را به صورت پیش فرض صفر کنم.
window.parent.document.getElementsById('cke_125_textInput').value=0;
خب همهی این دردسرها را ما تحمل کردیم تا به سادهترین شکل ممکن هر عکسی را که آپلود شد ، برای مکانیزم lightbox آماده کنیم و به راحتی یه گالری عکس خوب داشته باشیم.
خب حتما میپرسید که از چه پلاگینی برای ایجاد lightbox استفاده کنیم:
من به شخصه پلاگین colorbox را پیشنهاد میدم.
با انجام یک سرچ ساده سایتش را پیدا کنید و با مستندات و ویژگیهای آن آشنا شوید.
یک پیشنهاد:
برای انجام سلکت زدن برای عناصری که باید پلاگین colorbox روی آنها اعمال شوند من سلکت زدن به شیوهی زیر را پیشنهاد میکنم:
$(".container [href$='.jpg']").colorbox({ maxWidth: 800, opacity: 0.5, rel: 'gal' }); $(".container [href$='.png']").colorbox({ maxWidth: 800, opacity: 0.5, rel: 'gal' }); $(".container [href$='.gif']").colorbox({ maxWidth: 800, opacity: 0.5, rel: 'gal' });
فرض کنید div ی که متن و عکسهای ما را شامل میشه ، کلاسش container باشه . با کمک [href$='.jpg'] میتوان گفت هر لینکی که، پسوند فایلی که به آن اشاره میکند، .jpg هست، ویژگی colorbox را به خود بگیرید.
یک پیشنهاد برای تشکیل گالری عکس:
همان طور که من در بالا اشاره کردم ، rel را بر روی gal قرار دادم، تا هر تگی که ویژگی rel را داشته باشد، تشکیل یک گروه برای گالری عکس را بدهد.
برای اینکه بتوانیم این ویژگی را به عناصر مورد نظر خود اعمال کنیم ، بازم دست به دامان jQuery میشویم:
$(".container [href$='.jpg']").attr("rel", "gal");
خب مثل اینکه دیگر کار تمام شده و امیدوارم برای شما مفید بوده باشه.
موفق باشید...
معرفی Kendo UI
Kendo UI یک فریم ورک جاوا اسکریپتی ساخت برنامههای مدرن و تعاملی وب است و برای رسیدن به این مقصود، از JavaScript، CSS 3، HTML 5 و jQuery کمک میگیرد.
امکانات فراهم شده توسط Kendo UI
1) انواع و اقسام ویجتها: کنترلهای وب تهیه شده برفراز jQuery
ویجتهای آن در سه گروه کلی قرار میگیرند:
- گروه وب، مانند گرید، tree-view و غیره.
- گروه DataViz که جهت نمایش بصری اطلاعات و ترسیم انواع و اقسام نمودارها کاربرد دارد.
- گروه موبایل که با استفاده از فناوری adaptive rendering، در سیستم عاملهای مختلف موبایل، مانند اندروید و آی او اس، ظاهری بومی و هماهنگ با آنها را ارائه میدهد.
2) منبع داده سمت کاربر (Client side data source)
منبع داده سمت کاربر Kendo UI، از انواع و اقسام منابع داده محلی مانند آرایههای جاوا اسکریپتی تا منابع داده راه دور، مانند JSON، XML و JSONP، جهت نمایش اطلاعات و data binding پشتیبانی میکند. این منبع داده، مواردی مانند صفحه بندی، مرتب سازی اطلاعات و گروه بندی آنها را نیز فراهم میکند. به علاوه با عملیات ثبت، ویرایش و حذف اطلاعات نیز هماهنگی کاملی را دارد.
3) به همراه یک فریم ورک MVVM توکار است
این فریم ورک MVVM مواردی مانند two way data binding و همچنین declarative binding را نیز پشتیبانی میکند.
4) امکان تعویض قالب
5) پویا نمایی، کشیدن و رها کردن
6) فریم ورک اعتبارسنجی
چرا Kendo UI؟
- مهمترین مزیت کار با Kendo UI، فراهم آوردن تمام نیازهای توسعهی یک برنامهی مدرن وب، تنها در یک بسته است. به این ترتیب دیگر نیازی نیست تا گرید را از یکجا، tree-view را از جایی دیگر و کتابخانههای رسم نمودار را از منبعی ناهمگون با سایر عناصر برنامه دریافت و استفاده کنید؛ در اینجا تمام اینها در قالب یک بستهی آماده برای شما فراهم شدهاست و همچنین با یکدیگر سازگاری کاملی دارند.
- تمام ویجتهای آن برای نمایش سریع با کارآیی بالا طراحی شدهاند.
- پشتیبانی خوب آن. این فریم ورک محصول شرکتی است که به صورت تخصصی کار تهیه کامپوننتهای وب و دسکتاپ را انجام میدهد.
مرورگرهای پشتیبانی شده
یکی دیگر از مزایای مهم کار با Kendo UI پشتیبانی گستردهی آن از اکثر مرورگرهای موجود است. این فریم ورک با مرورگرهای زیر سازگار است:
- IE 7 به بعد
- فایرفاکس 10 به بعد
- تمام نگارشهای کروم
- اپرا 10 به بعد
- سفاری 4 به بعد
مجوز استفاده از Kendo UI
Kendo UI با سه مجوز ذیل ارائه میشود:
- 30 روزه آزمایشی رایگان
- تجاری
- سورس باز با مجوز Apache
پیشتر نسخهی تجاری آن تحت مجوز GPL نیز در دسترس بود. اما اخیرا مجوز GPL آن حذف شده و به Apache تغییر یافته است. اما باید در نظر داشت که نسخهی سورس باز آن شامل کنترلهای مهمی مانند «گرید» نیست و این موارد تنها در نسخهی تجاری آن لحاظ شدهاند.
مثالهای Kendo UI
پس از دریافت بستهی کامل آن، پوشههایی مانند js، styles و امثال آن قابل مشاهده هستند؛ به همراه پوشهی examples آن که حداقل 86 پوشهی دیگر در آن جهت ارائه مثالهایی از نحوهی کاربرد المانهای مختلف آن تدارک دیده شدهاند.
نحوهی افزودن Kendo UI به صفحه
از آنجائیکه Kendo UI یک فریم ورک جاوا اسکریپتی است، همانند سایر برنامههای وب، افزودن تعاریف فایلهای js، css و تصاویر مرتبط با آن، برای شروع به کار کفایت میکند. برای این منظور ابتدا پوشههای js و styles بستهی دریافتی آنرا به برنامهی خود اضافه کنید (این پوشهها در فایل پیوست انتهای بحث موجود هستند).
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <!--KendoUI: Web--> <link href="styles/kendo.common.min.css" rel="stylesheet" type="text/css" /> <link href="styles/kendo.default.min.css" rel="stylesheet" type="text/css" /> <script src="js/jquery.min.js" type="text/javascript"></script> <script src="js/kendo.web.min.js" type="text/javascript"></script> <!--KendoUI: DataViz--> <link href="styles/kendo.dataviz.min.css" rel="stylesheet" type="text/css" /> <script src="js/kendo.dataviz.min.js" type="text/javascript"></script> <!--KendoUI: Mobile--> <link href="styles/kendo.mobile.all.min.css" rel="stylesheet" type="text/css" /> <script src="js/kendo.mobile.min.js" type="text/javascript"></script> <script type="text/javascript"> $(function() { $("#pickDate").kendoDatePicker(); }); </script> </head> <body> <span> Pick a date: <input id="pickDate" type="text"/> </span> </body> </html>
دریافت سورس کامل این قسمت که حاوی فایلهای اصلی kendoui.professional.2014.2.1008 نیز میباشد:
KendoUI01.7z
بنابراین برای ایجاد یک کامپوننت میتوانیم به اینصورت عمل کنیم:
var app = angular.module("dntModule", []); app.component("pmApp", { template: `Hello this is a simple component` });
همانطور که مشاهده میکنید تابع component دو پارامتر را از ورودی دریافت خواهد کرد؛ نام کامپوننت و یک شیء برای تعیین تنظیمات کامپوننت. نام کامپوننت در اینجا به صورت camel case تعریف شده است؛ که در واقع یک convention برای Angular است. در اینحالت برای استفادهی از کامپوننت باید به اینصورت عمل کنیم:
<pm-app></pm-app>
در قسمت تنظیمات کامپوننت، در سادهترین حالت یک template تعیین شدهاست که بیانگر نحوهی رندر شدن یک کامپوننت میباشد. در اینحالت وقتی انگیولار به تگ فوق برسد، یک کامپوننت با نام pmApp را بارگذاری خواهد کرد.
ایجاد یک کامپوننت ساده
در ادامه میخواهیم یک کامپوننت ساده را جهت نمایش یکسری URL درون صفحه طراحی کنیم. ساختار صفحه index.html به صورت زیر خواهد بود:
<html ng-app="DNT"> <head> <meta charset="UTF-8"> <title>Using Angular Component</title> <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"> <link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-3"> <dnt-widget></dnt-widget> </div> </div> </div> <script src="bower_components/angular/angular.js"></script> <script src="scripts/app.js"></script> <script src="scripts/components/dnt-widget.component.js"></script> </body> </html>
در اینجا ابتدا توسط دایرکتیو ng-app، به Angular، ماژولمان را معرفی کردهایم. سپس مداخل بوتاسترپ و کتابخانهی font-awesome را مشاهده میکنید. در ادامه، کتابخانهی Angular و همچنین فایل app.js جهت معرفی ماژول برنامه معرفی شدهاست. در نهایت نیز یک فایل در مسیر ذکر شده برای قرار دادن کدهای کامپوننت در مسیر scripts/components اضافه شدهاست.
همانطور که ملاحظه میکنید، کامپوننتمان به صورت یک تگ سفارشی، درون صفحه قرار گرفته است:
<dnt-archive></dnt-archive>
در ادامه باید به Angular، نحوهی تعریف این کامپوننت را اعلام کنیم. بنابراین یک فایل جاوا اسکریپتی را با نام dnt-widget.component، با محتویات زیر ایجاد کنید:
(function () { "use strict"; var app = angular.module("DNT"); function DntArchiveController() { var model = this; model.panel = { title: "Panel Title", items: [ { title: "Dotnettips", url: "https://www.dntips.ir" }, { title: "Google", url: "http://www.google.con" }, { title: "Yahoo", url: "http://www.yahoo.con" } ] }; }; app.component("dntWidget", { templateUrl: '/scripts/components/dnt-widget.component.html', controllerAs: "model", controller: DntArchiveController }); } ());
توضیح کدهای فوق:
همانطور که مشاهده میکنید، برای پارمتر دوم کامپوننت، سه پراپرتی را تعیین کردهایم:
templateUrl: به کمک این پراپرتی به Angular گفتهایم که محتوای قالب این کامپوننت، درون یک فایل HTML مجزا قرار دارد و به صورت linked template میباشد.
controllerAs: یکی از مزایای استفاده از کامپوننتها، استفاده از controller as syntax میباشد. لازم به ذکر است اگر این پراپرتی را مقداردهی نکنیم، به صورت پیشفرض مقدار ctrl$ در نظر گرفته خواهد شد.
controller: مزیت دیگر کامپوننتها، استفاده از کنترلرها است. با استفاده از این پراپرتی، یک کنترلر را برای کامپوننتمان رجیستر کردهایم. در نتیجه زمانیکهی Angular میخواهد کامپوننتمان را نمایش دهد، تابع تعریف شده برای این پراپرتی، جهت ایجاد یک controller instance فراخوانی خواهد شد. بنابراین هر پراپرتی یا تابعی که برای این controller instance تعریف کنیم، به راحتی درون ویوی آن جهت اعمال بایندینگ در دسترس خواهد بود (در نتیجه نیازی به scope$ نخواهد بود).
درون کنترلر نیز برای راحتی کار و همچنین به عنوان یک best practice، مقدار this را توسط یک متغیر با نام model، کپچر کردهایم. در اینجا یک شیء را با نام panel نیز به مدل اضافه کردهایم.
محتویات تمپلیت:
<div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title"> <span class="fa fa-archive"></span> {{ model.panel.title}} </h3> </div> <ul class="list-group"> <li class="list-group-item" ng-repeat="item in model.panel.items"> <span class="fa fa-industry"></span> <a href="{{ item.url }}">{{ item.title }}</a> </li> </ul> </div>
ویوی کامپوننت پیچیدگی خاصی ندارد. همانطور که مشاهده میکنید یک پنل بوتاسترپی را ایجاد کردهایم که مقدار عنوان آن و همچنین آیتمهای آن، از شیء اتچ شده به مدل دریافت خواهند شد. بنابراین اکنون اگر برنامه را اجرا کنید، خروجی کامپوننت را به اینصورت مشاهده خواهید کرد:
همانطور که مشاهده میکنید استفاده از کامپوننتها در Angular 1.5 در مقایسه با ایجاد دایرکتیوها و کنترلرها خیلی سادهتر است. در واقع امکانات این API جدید تنها به مثال فوق ختم نمیشود؛ بلکه این API یک سیستم مسیریابی جدید را نیز معرفی کرده است که در قسمتهای بعدی به آن نیز خواهیم پرداخت.
جهت تکمیل بحث نیز یک تقویم شمسی ساده را در اینجا قرار دادهام. میتوانید جهت مرور بحث جاری به کدهای آن مراجعه کنید. البته هدف از تعریف این پروژه تنها یک مثال ساده برای معرفی کامپوننتها بود و طبیعتاً باگهای زیادی دارد. اگر مایل بودید میتوانید در توسعهی آن مشارکت نمائید.
کدهای این قسمت را نیز از اینجا میتوانید دریافت کنید.