نصب کامپوننت Blazored TextEditor
ابتدا نیاز است بستهی نیوگت آنرا با اجرای دستور زیر، به پروژهی Blazor خود اضافه کرد:
dotnet add package Blazored.TextEditor
libman install quill --provider unpkg --destination wwwroot/lib/quill
<head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>BlazorServer.App</title> <base href="~/" /> <link href="lib/quill/dist/quill.snow.css" rel="stylesheet" /> <link href="lib/quill/dist/quill.bubble.css" rel="stylesheet" />
<script src="lib/quill/dist/quill.min.js"></script> <script src="_content/Blazored.TextEditor/quill-blot-formatter.min.js"></script> <script src="_content/Blazored.TextEditor/Blazored-BlazorQuill.js"></script> <script src="_framework/blazor.server.js"></script> </body>
و در آخر جهت سهولت کار با این کامپوننت میتوان فضای نام آنرا به فایل BlazorServer.App\_Imports.razor به صورت زیر اضافه کرد:
@using Blazored.TextEditor
استفاده از کامپوننت Blazored.TextEditor در کامپوننت HotelRoomUpsert.razor
میخواهیم در کامپوننت HotelRoomUpsert.razor مثال این سری، بجای کامپوننت InputTextArea مورد استفاده، از یک HTML Editor استفاده کنیم:
<div class="form-group"> <label>Details</label> @*<InputTextArea @bind-Value="HotelRoomModel.Details" class="form-control"></InputTextArea>*@ <BlazoredTextEditor @ref="@QuillHtml"> <ToolbarContent> <select class="ql-header"> <option selected=""></option> <option value="1"></option> <option value="2"></option> <option value="3"></option> <option value="4"></option> <option value="5"></option> </select> <span class="ql-formats"> <button class="ql-bold"></button> <button class="ql-italic"></button> <button class="ql-underline"></button> <button class="ql-strike"></button> </span> <span class="ql-formats"> <select class="ql-color"></select> <select class="ql-background"></select> </span> <span class="ql-formats"> <button class="ql-list" value="ordered"></button> <button class="ql-list" value="bullet"></button> </span> <span class="ql-formats"> <button class="ql-link"></button> </span> </ToolbarContent> <EditorContent> </EditorContent> </BlazoredTextEditor> </div>
- همانطور که ملاحظه میکنید، این تعریف به همراه یک ارجاع به وهلهای از آن نیز هست:
<BlazoredTextEditor @ref="@QuillHtml">
@code { private BlazoredTextEditor QuillHtml;
برای تغییر اندازه و مقدار placeholder پیشفرض آن، میتوان به صورت زیر عمل کرد:
<div class="form-group pb-4" style="height:250px;"> <label>Details</label> <BlazoredTextEditor @ref="@QuillHtml" Placeholder="Please enter the room's detail">
تنظیم و دریافت متن نمایشی HTML Editor
مطابق مستندات این کامپوننت، روش تنظیم متن نمایشی آن، به کمک متد LoadHTMLContent است. به همین جهت متد زیر را به کدهای کامپوننت جاری اضافه میکنیم:
private async Task SetHTMLAsync() { if(!string.IsNullOrEmpty(HotelRoomModel.Details)) { await QuillHtml.LoadHTMLContent(HotelRoomModel.Details); } }
protected override async Task OnInitializedAsync() { if (Id.HasValue) { // Update Mode Title = "Update"; HotelRoomModel = await HotelRoomService.GetHotelRoomAsync(Id.Value); await SetHTMLAsync(); } // ... }
private async Task HandleHotelRoomUpsert() { // ... // Create Mode HotelRoomModel.Details = await QuillHtml.GetHTML(); // ... }
مشکل! ادیتور در زمان ویرایش یک رکورد، اطلاعات پیشین را نمایش نمیدهد!
پس از اعمال تغییرات فوق، برنامه را اجرا میکنیم. سپس یک اتاق جدید را اضافه کرده و در لیست نمایش اتاقها، گزینهی ویرایش آنرا انتخاب میکنیم. در این حالت هرچند کار مقدار دهی HotelRoomModel.Details در زمان ثبت اطلاعات انجام شده، اما ... در زمان ویرایش چیزی نمایش داده نمیشود و تغییراتی را که به متد رویدادگردان OnInitializedAsync اضافه کردهایم، عمل نمیکنند.
در این مورد در قسمت بررسی چرخهی حیات کامپوننتها توضیحاتی ابتدایی ارائه شد:
«رویدادهای OnAfterRender و OnAfterRenderAsync
پس از هر بار رندر کامپوننت، این متدها فراخوانی میشوند. در این مرحله کار بارگذاری کامپوننت، دریافت اطلاعات و نمایش آنها به پایان رسیدهاست. یکی از کاربردهای آن، آغاز کامپوننتهای جاوا اسکریپتی است که برای کار، نیاز به DOM را دارند؛ مانند نمایش یک modal بوت استرپی.»
بنابراین در این حالت خاص که ادیتور جاوا اسکریپتی مورد استفاده، پس از رندر کامل UI نمایش داده میشود، قرار دادن متد SetHTML در روال رویدادگردان OnInitializedAsync کار نخواهد کرد و باید آنرا به روال رویدادگردان OnAfterRender انتقال دهیم:
protected override async Task OnAfterRenderAsync(bool firstRender) { await SetHTMLAsync(); }
private async Task SetHTMLAsync() { if(!string.IsNullOrEmpty(HotelRoomModel.Details)) { await QuillHtml.LoadHTMLContent(HotelRoomModel.Details); StateHasChanged(); } }
مشکل! اگر در این حالت سعی کنیم متنی را در ادیتور وارد کنیم، میسر نیست و همچنین CPU Usage سیستم به 100 درصد رسیدهاست!
علت اینجا است که فراخوانی StateHasChanged، هر چند سبب رندر مجدد UI میشود، اما چون در پایان کار رندر قرار داریم، یک حلقهی بینهایت را سبب خواهد شد. به همین جهت باید در متد OnAfterRenderAsync، بر اساس پارامتر firstRender، از رندرهای بعدی جلوگیری کرد:
protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) { return; } while (true) { try { await SetHTMLAsync(); break; } catch { await Task.Delay(100); // Quill needs some time to load } } }
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-20.zip