SortableJS یکی از ویژگیهای رایج برای برنامههای وب، لیستهای قابل مرتبسازی است. SortableJS یکی از مورد علاقههای من در کتابخانههای جاوااسکریپت است و وقتی با Blazor کار میکردم، احساس کمبود آن را داشتم. برای حل این موضوع، تصمیم گرفتم SortableJS را در بردارم و به یک کامپوننت Blazor تبدیل کنم که آن را با نام Bazor Sortable ساختم و به صورت متنباز در GitHub قرار دادم که فکر میکنم شما آن را دوست خواهید داشت. در این پست، من به شما نحوه افزودن آن به برنامههای وب Blazor خودتان را نشان خواهم داد.
توجه: Blazor Sortable یک کامپوننت جامعه متنباز است و کامپوننت رسمی از مایکروسافت نیست. تیم Fluent UI برای Blazor در حال کار بر روی یکپارچهسازی یک کامپوننت قابل مرتبسازی در نسخههای آینده Fluent UI برای Blazor است. شما میتوانید امروز نمونه نمایشی Fluent UI Sortable را امتحان کنید.
https://github.com/SortableJS/Sortable
بازسازی یک برنامه واقعی به نام theurlist.com در Blazor کار میکنیم. جریان این کار در "Burke Learns Blazor" در Twitch و .NET YouTube (لایک و اشتراک!) دنبال میشود. و ما دوست داریم که شما هم به ما بپیوندید. عمدتاً به این دلیل که ما به تمام کمکی که میتوانیم با این چیز دریافت کنیم نیاز داریم، چون من واقعاً نمیدانم چه کاری انجام میدهم.
ما به یک کامپوننت لیست قابل مرتبسازی برای این بازسازی نیاز پیدا کردیم و در حالی که چند نمونه "Blazor Sortable" وجود دارد، من کمی قلبم به SortableJS گرایش داشت. SortableJS یک کتابخانه برجسته برای ساخت لیستهای قابل مرتبسازی از آیتمها با تقریباً هر ویژگی که شما نیاز داشته باشید است - مرتبسازی، مرتبسازی بین لیستها، کلون کردن آیتمها، فیلتر کردن، انیمیشن سفارشی، حمایت کمری. خوب - نه آن آخری، اما این، مثل اینکه، تنها چیزی است که ندارد.
پس با کمی کمک از استیو سندرسون، ما یک سادهسازی بر روی SortableJS ساختیم که میتوانید در برنامههای خود استفاده کنید. بیایید نگاهی به چگونگی استفاده و سفارشیسازی Blazor Sortable برای برنامههای Blazor خودتان بیندازیم
استفاده از Blazor Sortable
مخزن GitHub برای Blazor Sortable شامل کد منبع برای لیست قابل مرتبسازی به همراه نمایشها است. برای پروژه خودتان، تنها به فایلهای Shared/SortableList.razor، Shared/SortableList.razor.css و Shared/SortableList.razor.js نیاز دارید.
کامپوننت SortableList یک کامپوننت عمومی است که یک لیست از آیتمها را میگیرد و SortableItemTemplate که تعریف میکند چگونه هر آیتم در لیست قابل مرتبسازی را رندر کند. برای مثال، بیایید بگوییم که شما یک لیست از کتابها دارید که به این شکل به نظر میرسد...
public class Book
{
public string Title { get; set; } = "";
public string Author { get; set; } = "";
public int Year { get; set; }
}
public List<Book> books = new List<Book>
{
new Book { Title = "The Very Hungry Caterpillar", Author = "Eric Carle", Year = 1969 },
new Book { Title = "Where the Wild Things Are", Author = "Maurice Sendak", Year = 1963 },
new Book { Title = "Goodnight Moon", Author = "Margaret Wise Brown", Year = 1947 },
new Book { Title = "The Cat in the Hat", Author = "Dr. Seuss", Year = 1957 },
new Book { Title = "Charlotte's Web", Author = "E.B. White", Year = 1952 },
new Book { Title = "Harry Potter and the Sorcerer's Stone", Author = "J.K. Rowling", Year = 1997 },
new Book { Title = "The Lion, the Witch and the Wardrobe", Author = "C.S. Lewis", Year = 1950 },
new Book { Title = "Matilda", Author = "Roald Dahl", Year = 1988 },
new Book { Title = "The Giving Tree", Author = "Shel Silverstein", Year = 1964 },
new Book { Title = "Oh, the Places You'll Go!", Author = "Dr. Seuss", Year = 1990 }
};
شما می توانید این لیست را در یک SortableList مانند این ارائه دهید…
<div>
<SortableList Items="books" Context="book">
<SortableItemTemplate>
<div class="book">
<p>@book.Title</p>
</div>
</SortableItemTemplate>
</SortableList>
</div>
کامپوننت SortableList لیست آیتمها را با استفاده از SortableItemTemplate رندر میکند و سپس با استفاده از SortableJS، لیست را قابل مرتبسازی میکند. پارامتر Context برای تعریف نام متغیری که برای نمایش هر آیتم در لیست استفاده میشود به کار میرود. در این مورد، Context کتاب است و بنابراین هر آیتم در لیست توسط یک متغیر به نام کتاب نمایش داده میشود.
با این حال، اگر در این نقطه سعی کنید آیتمها را با کشیدن و رها کردن جابجا کنید، متوجه خواهید شد که وقتی یکی را رها میکنید، فقط به جایی که قبلاً بوده باز میگردد. این به این دلیل است که ما به SortableList نگفتهایم که چه کاری انجام دهد وقتی لیست مرتب میشود. ما این کار را با رسیدگی به رویداد OnUpdate و انجام مرتبسازی خودمان انجام میدهیم.
<div>
<SortableList Items="books" Context="book" OnUpdate="@SortList">
<SortableItemTemplate>
<div class="book">
<p>@book.Title</p>
</div>
</SortableItemTemplate>
</SortableList>
</div>
...
public void SortList((int oldIndex, int newIndex) indices)
{
// deconstruct the tuple
var (oldIndex, newIndex) = indices;
var items = this.books;
var itemToMove = items[oldIndex];
items.RemoveAt(oldIndex);
if (newIndex < items.Count)
{{
items.Insert(newIndex, itemToMove);
}}
else
{{
items.Add(itemToMove);
}}
}
رویداد OnUpdate هر زمان که لیست مرتب شود، فراخوانی میشود. این رویداد یک جفت شامل شاخص قدیمی و شاخص جدید آیتمی که جابجا شده را ارسال میکند. در متد SortList، ما این جفت را به دو متغیر تفکیک میکنیم و سپس از این متغیرها برای جابجایی آیتم در لیست استفاده میکنیم.
خیلی مهم است که هرگز DOMی که توسط Blazor کنترل میشود را تغییر ندهید. Blazor یک کپی داخلی از DOM نگهداری میکند و اگر شما آن را با چیزی مانند جاوااسکریپت تغییر دهید، نتایج عجیبی خواهید گرفت زیرا وضعیت صفحه با وضعیت داخلی Blazor هماهنگ نخواهد بود. پس آنچه ما در پشت صحنه انجام میدهیم این است که حرکت جاوااسکریپت را لغو میکنیم تا آیتم واقعاً در صفحه جابجا نشود. سپس ما آیتم را در لیست جابجا میکنیم و Blazor لیست را با ترتیب جدید بازنمایی میکند.
مثال پیچیدهتر
SortableJS یک کتابخانه بسیار قدرتمند است و میتواند بیش از فقط مرتبسازی لیستها انجام دهد. این کتابخانه همچنین میتواند بین لیستها مرتبسازی کند، آیتمها را کلون کند، آیتمها را فیلتر کند و موارد دیگر. کامپوننت SortableList از بسیاری از این ویژگیها پشتیبانی میکند. بیایید نگاهی به یک مثال پیچیدهتر بیندازیم - مرتبسازی بین دو لیست...
<div>
<div class="container">
<div class="columns">
<div class="column">
<h3>Books</h3>
<SortableList Items="books" Context="book" OnRemove="@AddToFavoriteList" Group="favorites">
<SortableItemTemplate>
<div class="book">
<p>@book.Title</p>
</div>
</SortableItemTemplate>
</SortableList>
</div>
<div class="column">
<h3>Favorite Books</h3>
<SortableList Items="favoriteBooks" Context="book" OnRemove="@RemoveFromFavoriteList" Group="favorites">
<SortableItemTemplate>
<div class="book">
<p>@book.Title</p>
</div>
</SortableItemTemplate>
</SortableList>
</div>
</div>
</div>
</div>
در این مثال، ما دو لیست داریم - یک لیست از تمام کتابها و یک لیست از کتابهای مورد علاقه. آنها از طریق ویژگی Group به هم مرتبط هستند.
ما میخواهیم بتوانیم کتابها را از لیست تمام کتابها به لیست کتابهای مورد علاقه کشیده و رها کنیم. برای انجام این کار، ما نیاز داریم که رویداد OnRemove را برای هر دو لیست رسیدگی کنیم.
public void AddToFavoriteList((int oldIndex, int newIndex) indices)
{
var (oldIndex, newIndex) = indices;
var book = books[oldIndex];
favoriteBooks.Insert(newIndex, book);
books.RemoveAt(oldIndex);
}
public void RemoveFromFavoriteList((int oldIndex, int newIndex) indices)
{
var (oldIndex, newIndex) = indices;
var book = favoriteBooks[oldIndex];
books.Insert(newIndex, book);
favoriteBooks.RemoveAt(oldIndex);
}
استایلدهی به SortableList
به طور پیشفرض، SortableList شامل برخی استایلهای پیشفرض است که عنصر "شبح" را هنگام کشیدن مخفی میکند. این باعث میشود که شما یک فاصله بین آیتمها هنگام کشیدن داشته باشید. بدون این تغییر استایل، خود آیتم به عنوان مقصد رها کردن نشان داده میشود. این کمی عجیب است زیرا به این معنی است که آیتمی که شما در حال کشیدن هستید همان آیتمی است که روی آن رها میکنید. اما اگر این سبک شماست، میتوانید استایلها را در فایل SortableList.razor.css دستکاری کنید یا اصلاً آن را وارد نکنید.
از آنجایی که تمام محتوای رندر شده در داخل SortableList در داخل یک فرزند SortableItemTemplate رندر میشود، شما همیشه باید از اصلاحکننده "::deep" برای اعمال تغییرات استفاده کنید.
اگر شما استایل SortableList را از یک صفحه/کامپوننت والد (مثلاً Index.razor.css) اعمال کنید، باید SortableList را در یک عنصر محتوا قرار دهید و از اصلاحکننده "::deep" نیز استفاده کنید. اگر این کار را نکنید، استایلهای شما اعمال نمیشوند و شما ناراحت و گیج و عصبانی از من برای ساخت این کامپوننت خواهید شد. این یک موضوع Blazor است، نه موضوع SortableJS. شما میتوانید بیشتر در مورد استایلهای دامنه در مستندات ASP.NET Core بخوانید.
احساس میکنم که هیچ کس آن پاراگراف آخر را نخواهد خواند و خواهیم شنید که چه فریادها و آههایی بلند خواهد شد. اما من سعی کردم. پیشاپیش عذرخواهی میکنم.
چرا Drag and Drop HTML5 نه؟
سوال منصفانهای است و یکی که من قطعاً قبل از رفتن به راهحل جاوااسکریپت به آن نگاه کردم. به طور خلاصه، پشتیبانی بومی HTML5 برای کشیدن و رها کردن به اندازه کافی قوی نیست برای یک مرتبسازی مناسب. به عنوان مثال، هیچ راهی برای استایل دادن به بسیاری از رفتارهای کشیدن و رها کردن وجود ندارد. به نظر میرسد ... عجیب ... و چیزی وجود ندارد که واقعاً بتوانید در مورد آن کاری انجام دهید. همچنین از نظر پشتیبانی در مرورگرها نیز نسبتاً ناپایدار است. برخی از ویژگیهای ضروری فقط در Chrome کار میکنند.
با این حال، SortableJS واقعاً سعی میکند از Drag and Drop HTML5 استفاده کند و در پلتفرمهایی مانند iOS به راهحل جاوااسکریپت بازگردد. با این حال، شما هنوز کنترل بر استایل خود را از دست میدهید و کشیدن و رها کردن عجیب به نظر میرسد. بنابراین من HTML5 را در SortableList خاموش کردهام. اگر میخواهید آن را دوباره روشن کنید، به فایل SortableList.razor.razor.js بروید و ویژگی forceFallback: true را حذف کنید. من باید این را در یک نقطه به عنوان تنظیمات قرار دهم
Blazor Sortable را بررسی کنی
Sortable Blazor را بررسی کنید و به ما بگویید که چه فکر میکنید! شما میتوانید با آن کارهای زیادی انجام دهید، از جمله کلون کردن آیتمها، غیرفعال کردن مرتبسازی روی برخی آیتمها، مشخص کردن دستههای کشیدن و بیشتر. ما هر ویژگی تک تک از SortableJS را پیادهسازی نکردهایم. هنوز. درخواستهای کشیدن خوش آمدید هستند! 😉
برای افزودن دیدگاه خود، نیاز است ابتدا وارد حساب کاربریتان شوید