چه تکنولوژی می‌خواهید یاد بگیرید؟

نظرات و انتقادات خود را با ما در میان بگذارید

آپدیت روزانه

بیش از 1500 مطلب آموزشی

نیازهای روز

Infinite Scrolling یا صفحات بی‌پایان

در این مقاله قصد داریم شما رو با نحوه پیاده سازی تکنیک صفحات بی‌پایان در پروژه ASP.Net MVC آشنا کنیم.

مقدمه:

استفاده از تکنیک صفحات بی‌پایان تجربه کاربری بهتری به بازدیدکنندگان سایت خواهد داد که البته استفاده از صفحات بی‌پایان با اسکرول اطلاعات جدیدی را به کاربران نشان می‌دهد چیز جدیدی نبوده و سایت‌های بزرگی از این تکنیک طراحی سایت بهره می‌برند همانند شبکه‌های اجتماعی گوگل پلاس، فیسبوک، پینترست، بخش جستجوی تصاویر گوگل و... . معرفی و استفاده از تکنیک Infinite Scrolling با ورود گوشی‌های همراه به دنیای اینترنت شروع شد، جاییکه صفحه نمایش کاربران باریک و اطلاعات صفحه به صورت عمودی و از بالا به پایین لیست می‌شوند.
روش کار بدین صورت می‌باشد که با پایین آمدن اسکرول صفحه سایت، بخش‌های جدید و اطلاعات تازه به صورت خودکار بارگذاری شده و نمایش داده می‌شود. به همین دلیل در بارگذاری اولیه نیاز نیست تمامی اطلاعات بارگذاری شود و کاربران تجربه پایین بودن سرعت سایت را نخواهند داشت.
البته نباید فراموش کرد این تکنیک علاوه بر مزایای گفته شده معایت به همراه خواهد داشت:

  • بهینه نبودن برای موتورهای جستجوگر
  • عدم دسترسی کاربران به فوتر سایت
  • عدم تشخیص میزان اطلاعات
  • و ...

ویژگی‌های مثبت این روش و نحوه ارتباط با آن جلب توجه بسیاری می‌کند اما لازم هستش نیازهای مربوطه بخوبی برسی و در نهایت اقدام به پیاده سازی شود.
یک راه حل ساده‌تر، احتمالا اگر از برنامه نویسان با تجربه باشید کم و بیش با سیستم مدیرت محتوا ماژولار آشنایی دارید، خوب در این سیستم ها بنا به  نوع قالب میتونه نوع درخواست‌ها هم متفاوت باشد:

  • ارسال به صورت Postback
  • ارسال درخواست به صورت Ajax

در این مقاله قصد داریم این تکنیک را برای این دو نوع درخواست بهینه سازی نمایم تا معایب این تکنیک پوشانده شود.

مثال اول:

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

/**
* بعد از تغییر اسکرول صفحه این رخداد فراخوانی خواهد شد
*/
$(window).scroll(function() {
// شرط برسی می‌شود که اگر ارتفاع کلی صفحه کوچکتر یا مساوی موقعیت
// مکان اسکرول + ارتفاع صفحه نمایشی باشد شرط برابر خواهد بود با مقدار
   // true
    if ($(document).height() <= ($(window).scrollTop() + $(window).height())) {
     // نمایش پیغام
     alert("End Of The Page");
     }
});

مثال دوم:

ایجاد صفحه بندی به صورت Paging و Infinite Scrolling، در کنترلر Home یک اکشن متد ایجاد نموده‌ایم که به نوع درخواست‌ها پاسخ متفاوت بازگشت می‌دهد، این متد حاوی دو پارامتر ورودی برای اعمال صفحه بندی در نظر گرفته شده که به صورت پیشفرض مقادیری را در بر دارد. همان گونه که در بالا گفته شد اعمال صفحات بی‌پایان علاوه بر ویژگی‌های مثبت آن معایبی به همراه خواهد داشت که از مهمترین آن عدم نمایش محتوا به صورت Paging در نتیجه موتورهای جستجوگر خواهد بود اما در این مقاله این مشکل برطرف خواهیم کرد. کد زیر اطلاعات را برای کاربر نمایش خواهد داد:

using System.Linq;
using System.Web.Mvc;
using Paging.Models;

namespace Paging.Controllers
{
    public class HomeController : Controller
    {
        /// <summary>
        ///
        /// </summary>
        /// <param name="skip"></param>
        /// <param name="take"></param>
        /// <returns></returns>
        // در صورتی که بر روی اکشن متد نوع درخواست مشخص نباشد می‌توان در حالت
        // مختلف به صورت
        // Get و Post
        // استفاده نمود. در این مثال با پست بک شدن درخواست به صورت
        // Get
        // به سرور ارسال خواهد شد و در غیر اینصورت درخواستی
        // Ajax
        // روبرو خواهیم بود
        public ActionResult Index(int skip = 0, int take = 30)
        {
            using (var db = new ApplicationDbContext())
            {
                // مدل
                var model = db.Persons.OrderBy(z=>z.Id).Skip(skip * take).Take(take).ToList();
                // برسی می‌شود درخواست به صورت
                // Ajax
                // ارسال شده است.
                if (Request.IsAjaxRequest()) return PartialView("Partial/_Persons", model);
                // در صورتی که درخواست به صورت
                // Ajax
                // ارسال نشده باشد شرط برسی نمی‌شود و کد زیر فراخوانی خواهد شد
                return View(model);
            }
        }
    }
}

کدهای HTML:

@model IEnumerable<Paging.Models.Persons>
@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>
<table class="table" id="tblDefault">
    <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>Family</th>
            <th>Father</th>
            <th>Gender</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>@item.Id</td>
                <td>@item.FirstName</td>
                <td>@item.LastName</td>
                <td>@item.FatherName</td>
                <td>@(item.Gender ? "Man" : "Woman")</td>
            </tr>
        }
    </tbody>
</table>

اکنون لازم است با توجه به مثال اول، یکم در کد تغییراتی اعمال نمایم، اما قبل از اعمال تغییرات لازم هستش یک متد داینامیک ایجاد نمایم تا هر بار لازم به تعریف متد Ajax نداشته باشیم:

/**
 *
 * @param {} model
 * @param {} data
 * @param {} success
 * @param {} error
 * @returns {}
 */
function SendObj(model, data, success, error, contentType) {
    var dataType = "json";
    if (typeof contentType == "undefined") {
        contentType = "application/html; charset=utf-8";
        dataType = "html";
    }
    $.ajax({
        type: "POST",
        url: model,
        data: data,
        contentType: contentType,
        dataType: dataType,
        success: function (data) {
            success(data);
        },
        error: function (data) {
            //error(data);
        }
    });
}

برای شروع هر پروژه‌ای داشتن تجربه و دید کافی به موضوع می‌تونه راه رو برای برنامه نویس هموار کند، جمله قبلی رو می‌توان در کدهای زیر حس کرد:

/**
 * نام جدول
 */
var tableId = tableId;

تصویر کنید تو یک صفحه نیاز باشد چندین Table برای نمایش محتوا داشته باشم، در نتیجه لازم هستش یک پراپرتی جهت نگهداری نام جدول در نظر گرفته شود تا موقع نیاز بتوان به جدول انتخابی دسترسی گرفت.

پیاده سازی متدی جهت اضافه کردن تغییرات به Table:

/**
 *
 * @param {} data
 * @param {} append
 * @returns {}
 */
function searchResult(data, append) {
    if (tableId == null)
        tableId = "tblDefault";
    if (typeof append === 'undefined' || append === "false") {
        $("#" + tableId + " tbody").empty();
    }
    var array = data.split('</tr>');
    for (var i = 0; i < array.length - 1; i++) {
        $("#" + tableId + " tbody").append($(array[i])[0]);
    }
}

متد تعریفی بالا دو ورودی دریافت خواهد کرد، ورودی اول اطلاعات دریافتی از سرور، ورودی دوم مشخص خواهد کرد لازم است مقادیر قبلی حذف و اطلاعات جدید جایگزین شود یا نه.

پیاده سازی مثال اول:

/**
* بعد از تغییر اسکرول صفحه این رخداد فراخوانی خواهد شد
*/
$(window).scroll(function () {
    // شرط برسی می‌شود که اگر ارتفاع کلی صفحه کوچکتر یا مساوی موقعیت
    // مکان اسکرول + ارتفاع صفحه نمایشی باشد شرط برابر خواهد بود با مقدار
    // true
    if ($(document).height() <= ($(window).scrollTop() + $(window).height())) {
        SendObj('/Home/Index', '{}', function (data) {
            searchResult(data, true);
        }, function () { alert('خطا در ارتباط با سرور') });
    }
});

اکنون با توجه به متد Index در کنترلر Home لازم است یک PartialView ایجاد کنیم که لیست از اطلاعات جدید درخواستی را برای ما برگرداند:

@model IEnumerable<Paging.Models.Persons>
@foreach (var item in Model)
{
    <tr>
        <td>@item.Id</td>
        <td>@item.FirstName</td>
        <td>@item.LastName</td>
        <td>@item.FatherName</td>
        <td>@(item.Gender ? "Man" : "Woman")</td>
    </tr>
}

اکنون اگر پروژه را اجرا کنید با پایین کشیدن اسکرول به پایین صفحه درخواستی Ajax به سرور ارسال شده و لیست از اشخاص برای ما برگشت داده می‌شود، اما این نتیجه‌ای نیست که ما انتظار داریم چرا که با پایین کشیدن اسکرول همان مقادیر قبلی برگردانه خواهد شد در نتیجه باید پارامتر skip که به صورت پیشفرض مقدار دهی شده است را مقدار دهی نموده و با ذخیره و اجرای مجدد پروژه این بار اطلاعات صحیح برگشت داده می‌شود.

مقداردهی پارامتر Skip:

خوب اگر بخوایم مقدار skip رو به سرور ارسال کنیم از روش زیر استفاده خواهیم کرد، در این بخش نکته ای وجود دارد که اگر مقدار skip مورد نظر ارسالی به سرور موجب برگرداندن هیچ رکورد از بانک اطلاعاتی شود باید برسی شود که درخواست به سرور متوقف گردیده تا در گام‌های بعدی ارسال درخواست به سرور صورت نپذیرد خوب این مورد رو در شرط جای خواهیم داد به صورت زیر:

/**
 * صفحه
 */
var row = 1;
/**
 *
 */
var canLoad = true;
/**
* بعد از تغییر اسکرول صفحه این رخداد فراخوانی خواهد شد
*/
$(window).scroll(function () {
    // شرط برسی می‌شود که اگر ارتفاع کلی صفحه کوچکتر یا مساوی موقعیت
    // مکان اسکرول + ارتفاع صفحه نمایشی باشد شرط برابر خواهد بود با مقدار
    // true
    if ($(document).height() <= ($(window).scrollTop() + $(window).height()) && canLoad) {
        SendObj('/Home/Index?skip=' + row, '{}', function (data) {
            if (data === "") { canLoad = false; return;}
            searchResult(data, true);
            row++;
        }, function () { alert('خطا در ارتباط با سرور') });
    }
});

تا این گام تکنیک صفحات بی‌پایان پیاده سازی گردید اکنون اگر درخواست ارسالی به سرور به صورت Ajax نباشد و فقط با مقدار دهی skip میتوان Paging را هم پیاده نمود.

نکته: مقدار ورودی take که به صورت پیشفرض برابر است با مقدار عددی 30 تعداد رکوردهای بازگشتی از بانک اطلاعاتی را مشخص خواهد کرد.

برای دانلود پروژه اینجا کلیک کنید.

امید نصری