Исходные коды программ и игр

Программирование - работа и хобби

Компоненты Blazor

Язык программирования C#

Компоненты как программные модули

Структура приложения BlazorПриложения Blazor - это интерактивные веб страницы предназначенные для создания развитого пользовательского интерфейса. Каркас Blazor основан на компонентах, которые представляют из себя активные программные модули с HTML разметкой. Компоненты Blazor это видоизменённые классы C#. Хотя компоненты и выглядят больше похожими на обычные HTML документы, всё же они более ближе к программным кодам C#, чем к гипертекстовой разметке. Компоненты могут включать разметку HTML, разметку встраиваемого серверного кода Razor и собственную программную логику на C#.

Компонентами в Blazor могут быть веб-страницы со своими URL, навигационные меню, формы авторизации, группы кнопок и вообще любые HTML шаблоны. Компоненты наделены возможностями реагировать на события, создаваемые пользователями сайта. При высокой функциональности компоненты могут использовать "услуги" всего множества классов библиотеки .NET. С помощью программного кода на C# компоненты эффективно общаются с клиентской стороной, при этом отсутствует непосредственное кодирование на JavaScript.

Физически компоненты Blazor представляют собой файлы с расширением .razor. Компоненты могут быть вложены друг в друга и поддерживают любой уровень вложенности. При компиляции компонента создаётся класс с таким же названием, как и у файла. Внедрение C# полей, условий, циклов и выражений в HTML код обеспечивается специальными блоками предваряемыми директивами и разметкой Razor. Члены класса компонента определяются в блоках @code{...}. Компоненты встраиваются друг в друга обычной гипертекстовой разметкой со специальными атрибутами, представляющими параметры компонента.

Примерная структура кода компонента:
@*Данный файл компонента компилируется в класс C# с одноименным названием.
Например: файл ProgressBar.razor компилируется в класс ProgressBar
*@

@*Присутствие этой директивы определяет компонент
как веб страницу с собственным URL. Если данного определения нет
компонент может быть только вложенным.*@
@page "/bootstrap-progressbar"

@*подключение необходимых пространств имён*@
@using BlazorComponents.Models
@using BlazorComponents.DataBase
@using BlazorComponents.MyCode

@code {
	// ===== Блок определения экземпляров класса компонента =====
	// Допускается несколько блоков на странице
    
	...
	
	// Класс определен в пространстве имен 
	// BlazorComponents.Models
	private Category category = new Category();
}

HTML код с внедрением программного кода C#

@* Вложенный компонент. Поддерживается любое количество
   компонентов на странице. Параметром может быть примитивный тип
 или любой объект. Компонент может иметь несколько параметров. 
*@
<div class="col">
    <Название_компонента  Параметр="Значение для параметра" />
</div>


@code {
    // ===== Блок определения экземпляров класса компонента =====
    // Собственная логика
    // Допускается несколько блоков на странице

    // Открытые параметры для управления компонентом в родительском модуле.
    // Может не быть на компоненте странице с URL.
    [Parameter]
    public ClassName instance{ get; set; }

    [Parameter]
    public int number{ get; set; }
	
    // Класс определен в пространстве имен 
    // BlazorComponents.MyCode
    private ParseModule = new ParseModule();
	
    // Класс определён в BlazorComponents.DataBase
    DBStore dbStore = new DBStore();
}

Исходники

К статье прикреплен исходник приложения Blazor с примерами компонентов. Функциональная часть компонентов подготовлена в автономном файле .razor. Далее этот автономный модуль добавляется на веб страницы приложения. Приложение выполняется на серверной стороне, построено на модели размещения Blazor Server, в которой обновление элементов пользовательского интерфейса, обработка событий и вызовы JavaScript обрабатываются через подключение SignalR.

Активные элементы веб приложения построены без кодирования на JavaScript. Платформа Blazor заботливо берёт на себя непривычную задачу для C# программистов по кодированию на JavaScript. К каждой веб странице, при преобразовании компонентов в сайт, прикрепляется файл blazor.server.js, посредством которого и осуществляется интерактивность.

Компонент Form

Компонент Form для BlazorКомпонент прекрасно подходит для интернет магазинов. В режиме реального времени клиент общается с логикой магазина. Хотя компонент Form интуитивно подразумевает применение тега <form>, но к удивлению он отсутствует в HTML разметки модуля. И всё же список товаров, которые выбрал клиент, фиксируется на сервере. Получаемых данных достаточно для однозначного определения состава покупки.

Компонент Form состоит из одной страницы и включает гипертекстовую разметку с элементами выбора и ввода. Для демонстрации работы создана псевдо-база данных электронной продукции с моделями таблиц категорий и товаров. Вывод категорий товаров автоматически генерируется на основе данных в базе.

События компонента

Листинг кода выбора категорий товаров:
<div class="col-lg-5">
    <b>Выбор категории:</b>
    <select class="form-control" id="category" @bind="selectCategory" title="Выбор категории">
        <option value="0" class="text-muted">Выбор категории...</option>
        @foreach (var i in BStore.Categories)
         {
             <option value="@i.Id">@i.Name</option>
         }
    </select>

    <div class="mt-2">
        <label class="m-0" for="note">Примечание:</label>
        <textarea @bind="note" @bind:event="oninput" class="form-control" id="note" rows="1">
        </textarea>
    </div>
</div>

Событие onchange отслеживается в элементе управления select и значение выбранного пункта сохраняется в привязанной к событию переменной selectCategory. В качестве значений для атрибутов value используются идентификаторы категорий. На основе сохранённого идентификатора для клиентов воображаемого магазина открывается список товаров выбранной категории. HTML элемент <textarea> служит для передачи на сервер дополнительных пожеланий клиентов. В <textarea> отслеживается событие oninput и при каждом вводе символа он передаётся на сервер посредством привязанной переменной note. Если в <textarea> событие изменить на onchange, или вообще удалить код привязки события, то передача текста будет происходить при потере фокуса.

Выбор товаров

При выборе категории раскрывается соответствующий список товаров. Какой список товаров выводить однозначно определяет идентификатор selectCategory. Из общего списка товаров BStore.Products получаем только те которые соответствуют выбранной категории. Аргументы события onchange дают информацию о состоянии кликнутого чекбокса.

Листинг кода выбора товаров:
<div class="col-lg-7">
    <div style="min-height:10rem;">
        <b>Выбор товара:</b>
        @foreach (var i in BStore.Products.FindAll(e => e.IdCategory == selectCategory))
        {
            <div class="form-check">
                <input class="form-check-input" checked="@SelectedProducts.Contains(i)" 
                  type="checkbox" id="@i.Id" 
                  @onchange="eventArgs => { Checkbox_Changed(i, eventArgs.Value); }">
                <label class="form-check-label" for="@i.Id">
                   @i.Name
                </label>
            </div>
        }
    </div>
</div>

Идентификатор категории, список товаров и примечание после обработки на сервере выводится в браузер клиента. Данные выбранных товаров сохраняются в списке продуктов List<Product> SelectedProducts. Если пользователь откроет повторно предыдущие категории флажки чекбоксов будут выставлены соответственно выбранным. За это отвечают атрибуты checked выставляемые в зависимости от результата выражения SelectedProducts.Contains(i). При возврате истины чекбокс получает атрибут checked="checked", в случае возврата ложь атрибут удаляется.

Компонент Calendar

Компонент Bootstrap Calendar для BlazorКомпонент Boostrap Calendar создаёт визуальный лист календаря с кнопками изменения месяца. Если скрыть кнопки, из 12 компонентов, можно сформировать календарь на весь год. Или же на одном листе просмотреть месяцы всего года. Визуальная часть компонента построена на популярной библиотеке Bootstrap.

Компонент Calendar объединяет 3 модуля: класс календарных данных DataCalendar, непосредственно сам компонент с логикой и пользовательским интерфейсом и страница, отображающая календарь. После компиляции они образуют единую веб страницу с разметкой HTML с отзывчивыми элементами календарных месяцев.

Интерактивность компонента

Интерактивность календаря заключается в возможности выделения даты и получение информации о ней серверным кодом. вывод информации о ней в разметку HTML такого вида 26.10.2020 г.. Гибкость компонентов Blazor позволяет развивать функциональность выделения даты в желаемых просторах. Например, на основе компонента Calendar можно создать онлайн-календарь с напоминанием о предстоящих событиях.

Часть кода визуализации компонента:
...
<ol class="day-names list-unstyled text-center font-weight-bold text-uppercase">
    <li>Пн</li>
    <li>Вт</li>
    <li>Ср</li>
    <li>Чт</li>
    <li>Пт</li>
    <li class="red-text">Сб</li>
    <li class="red-text">Вс</li>
</ol>
<ol class="days list-unstyled">
    @for (var i = 0; i < offset; i++)
    {
        <li>
            <div class="date">&nbsp;</div>
        </li>
    }
    @foreach (var i in Enumerable.Range(0, numDays))
    {
        <li>
            <div class="date py-2 @SetStyle(i)" @onclick="() => SelectDate(i)">@(i + 1)</div>
        </li>
    }
</ol>
<div>
    <span class="text-success">&nbsp;@ShowSelect()</span>
</div>
...

Для точного расположения числа месяца под необходимым днём недели высчитывается требуемое количество пробелов в виде HTML конструкции <div class="date">&nbsp;</div>

Принцип выделения даты заключается в следующем: пользователь кликает на желаемое число, данные отправляются на сервер, на сервере создаётся параметр стиля для выбранного числа и полная информация о выбранной дате, параметр стиля и информация отправляется пользователю, происходит обновление компонента в браузере. Благодаря тому что обновляется только часть кода компонента общение сервера и браузера происходит гораздо быстрее чем если обновлять всю страницу.

C# код выбора, фиксации и вывода выделенной даты:
// ===== Выбор даты =====

// Поле хранения числа выбранной даты
private int select = -1;

// Подсвечивание выбранной даты
string SetStyle(int v) => select == v ? "bg-success" : "";

// Фиксация выбранной даты
private void SelectDate(int v) => select = v;

// Сброс выделения даты
private void ResetSelect() => select = -1;

// Вывод информации о выбранной дате
private string ShowSelect()
{
    return (select != -1) ?
      DateTime.Parse($"{select + 1}.{Month}.{year}").ToString("dd.MM.yyyy г.") : "";
}

Формирование месяцев календаря

Название месяца, количество дней в месяцах, смещение начала месяца в соответствии с днём недели компонент Calendar получает из функциональности класса DataCalendar. Класс имеет единственный метод, где происходит необходимая подготовка данных и в виде кортежа(упорядоченного набора элементов) возвращает набор из 3-х значений.

Дни недели в библиотеке классов .NET C# хранятся в перечислении enum DayOfWeek. Значения констант в перечислении начинаются воскресением (число 0) и заканчиваются субботой (число 6). Для конвертирования смещения под дни недели с началом от понедельника применяется вспомогательный массив int[] convertOffset.

public class DataCalendar
{
    public (string nameMonth, int numDays, int offset) GetDays(int year, int month)
    {
        int numDays = DateTime.DaysInMonth(year, month);

        string nameMonth = new DateTime(year, month, 1).
         ToString("MMMM yyyy", System.Globalization.CultureInfo.CurrentCulture);

        // Конвертирует смещение с началом от понедельника.
        // Самое большое смещение у воскресения, у понедельника смещение 0.
        int[] convertOffset = { 6, 0, 1, 2, 3, 4, 5 };
        int i = (int)DateTime.Parse(year.ToString() + " " + month.ToString()).DayOfWeek;
        int offset = convertOffset[i];
        
        // Возврат кортежа значений.
        return (nameMonth, numDays, offset);
    }
}

Компонент ProgressBar

Компонент ProgressBar для BlazorКомпонент ProgressBar это одна или несколько цветных полос на белом фоне, индицирующие затяжные процессы. ProgressBar состоит из модели, в которой определятся функциональные параметры одной полосы, модуля логики работы компонента, веб страницы на которой размещаются индикаторные полосы. При изменении значения ширины полосы она анимированно увеличивается в размерах. HTML код и стили анимированной индикаторной полосы базируются на каркасе Bootstrap.

Для исследования поведения компонента на веб страницу добавлены кнопки изменения значений индикаторных полос, цвета, увеличения и уменьшения количества элементов progressbar. Изменение параметров компонента происходит на сервере, но благодаря библиотеке общения в режиме реального времени, в браузере компоненты обновляются со скоростью на какую только способно интернет-соединение.

Программный код ProgressBar

Модель компонента:
public class ProgressBarItem
{
    // Значение индикатора, оно же ширина полосы.
    public int Value { get; set; }

    // Цвет индикаторной полосы.
    public string Color { get; set; }
}

Код компонента создан в отдельном файле и предназначен для вложенности в другой компонент. У компонента всего один параметр список-массив для экземпляров класса ProgressBarItems. Этот параметр устанавливает значение (ширину) и цвет каждой индикаторной полосы. Количество полос progressBar определяется количеством элементов в списке.

Код компонента, файл _ProgressBar.razor :
@using BlazorComponents.Models

<div class="border rounded shadow p-4" style="background: #ffffff;">
    @foreach (var i in ProgressBarItems)
    {
        <div class="progress mb-4">
            <div class="progress-bar @i.Color progress-bar-striped progress-bar-animated" 
              aria-valuenow="@i.Value" aria-valuemin="0" aria-valuemax="100" 
                style="width: @i.Value%;">@i.Value%</div>
       </div>
    }
</div>

@code {
   
    // Открытый параметр компонента
    [Parameter]
    public List<ProgressBarItem> ProgressBarItems { get; set; }
}

Компонент ProgressBar размещается на странице, которая тоже является компонентом приложения Blazor.

@page "/progress-bar-bootstrap"
@using BlazorComponents.Models

<header class="text-center mb-5">
    <h1>Компонент Bootstrap ProgressBar</h1>
</header>

<div class="row">
    <div class="col">
        <_ProgressBar ProgressBarItems="ProgressBarItems" />
    </div>
</div>
<div class="row">
    <div class="col">
        <div class="text-center flex-grow-1 justify-content-end justify-content-lg-end py-3">
            ...
            <button class="btn btn-outline-dark" type="button" @onclick="ChangeValue">
                Изменить значения
            </button>
        </div>
    </div>
</div>


@code {

    private Random r = new Random();
    
    private string[] colors = { "bg-primary", "bg-secondary", 
                                "bg-success", "bg-info", "bg-warning", 
                                "bg-danger", "bg-dark" };

    private List<ProgressBarItem> ProgressBarItems = new List<Models.ProgressBarItem>();


    private void ChangeValue()
    {
        foreach (var i in ProgressBarItems)
        {
            i.Value = r.Next(5, 101);
            i.Color = colors[r.Next(0, 7)];
        }
    }

    ...

}

Прикреплённый исходник

Исходный код веб приложения Blazor с компонентами создан в среде программирования MS Visual Studio 2019. Для работы приложения необходимо установить .NET Core 3.1.

Просмотр демо 🔎

Файл: BlazorComponents-vs16.zip
Размер: 988 Кбайт
Загрузки: 13