14 KiB
RobotNet10.CustomConfigurationEditor
Component Blazor để quản lý cấu hình với giao diện người dùng, tích hợp với RobotNet10.CustomConfiguration backend.
📋 Mục lục
✨ Tính năng
- 🎨 Giao diện đẹp: Sử dụng MudBlazor components
- 📋 Danh sách Configs: Hiển thị và tìm kiếm configs
- ✏️ Editor: Chỉnh sửa config và variables
- 📤 Import/Export: Import và export config files
- 🔍 Tìm kiếm: Tìm kiếm configs theo tên hoặc ConfigType
- ✅ Validation: Validation real-time khi chỉnh sửa
- 🎯 Type-aware Editor: Editor tự động thay đổi theo type của variable
🚀 Cài đặt
1. Thêm Project Reference
<ItemGroup>
<ProjectReference Include="..\Components\RobotNet10.CustomConfigurationEditor\RobotNet10.CustomConfigurationEditor.csproj" />
</ItemGroup>
2. Đăng ký Services
Trong Program.cs hoặc Client/Program.cs:
using RobotNet10.CustomConfigurationEditor.Services.API;
using RobotNet10.CustomConfigurationEditor.Services.State;
// HttpClient (nếu chưa có)
builder.Services.AddScoped(sp => new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
// MudBlazor (nếu chưa có)
builder.Services.AddMudServices();
// CustomConfiguration Services
builder.Services.AddScoped<ConfigApiService>();
builder.Services.AddScoped<ConfigManagerState>();
3. Copy JavaScript File
Copy wwwroot/js/downloadFile.js vào wwwroot/js/ của project frontend và thêm vào index.html:
<script src="js/downloadFile.js"></script>
4. Thêm Using
Trong _Imports.razor:
@using RobotNet10.CustomConfigurationEditor.Components.ConfigManager
@using RobotNet10.CustomConfigurationEditor.Services.API
@using RobotNet10.CustomConfigurationEditor.Services.State
📖 Sử dụng Component
Cách 1: Sử dụng Component trực tiếp
Tạo page mới:
@page "/config-manager"
@using RobotNet10.CustomConfigurationEditor.Components.ConfigManager
<PageTitle>Configuration Manager</PageTitle>
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="mt-4">
<ConfigManagerComponent />
</MudContainer>
Cách 2: Sử dụng trong Layout
Thêm vào navigation menu:
<MudNavLink Href="/config-manager" Match="NavLinkMatch.All">
<MudIcon Icon="@Icons.Material.Filled.Settings" />
Configuration Manager
</MudNavLink>
🔧 Sử dụng Services
ConfigApiService
Service để gọi API backend:
@inject ConfigApiService ApiService
@code {
protected override async Task OnInitializedAsync()
{
// Lấy tất cả configs
var configs = await ApiService.GetAllConfigsAsync();
// Lấy config theo ID
var config = await ApiService.GetConfigByIdAsync(id);
// Lấy config theo ConfigType
var mqttConfig = await ApiService.GetConfigByTypeAsync("MQTTBrokerConfig");
// Tạo config mới
var newConfig = await ApiService.CreateConfigAsync(
configType: "MyConfig",
variables: variables,
description: "My config"
);
// Cập nhật config
await ApiService.UpdateConfigAsync(id, variables, description);
// Xóa config
await ApiService.DeleteConfigAsync(id);
// Import config
using var stream = file.OpenReadStream();
var imported = await ApiService.ImportConfigAsync(stream, file.Name, "MyConfig");
// Export config
var exportStream = await ApiService.ExportConfigAsync(id);
// Cập nhật variable
await ApiService.UpdateVariableAsync(id, "port", 8080);
// Thêm variable
await ApiService.AddVariableAsync(id, newVariable);
// Xóa variable
await ApiService.RemoveVariableAsync(id, "variableName");
}
}
ConfigManagerState
State management service với events:
@inject ConfigManagerState State
<MudButton OnClick="LoadConfigs">Load Configs</MudButton>
@if (State.IsLoading)
{
<MudProgressLinear />
}
@if (!string.IsNullOrEmpty(State.ErrorMessage))
{
<MudAlert Severity="Severity.Error">@State.ErrorMessage</MudAlert>
}
@code {
protected override async Task OnInitializedAsync()
{
// Subscribe to state changes
State.OnStateChanged += StateChanged;
// Load configs
await State.LoadConfigsAsync();
}
private async Task LoadConfigs()
{
await State.LoadConfigsAsync();
}
private void StateChanged()
{
StateHasChanged();
}
public void Dispose()
{
State.OnStateChanged -= StateChanged;
}
}
🧩 Components
ConfigManagerComponent
Component chính để quản lý configs.
Sử dụng:
<ConfigManagerComponent />
Tính năng:
- Toolbar với search, import, create buttons
- Split view: List panel và Editor panel
- Error handling và notifications
ConfigListPanel
Panel hiển thị danh sách configs.
Parameters:
State: ConfigManagerStateOnConfigSelected: EventCallback khi chọn config
Sử dụng:
<ConfigListPanel
State="@State"
OnConfigSelected="@(EventCallback.Factory.Create<ConfigFileMetadataModel>(this, OnConfigSelected))" />
ConfigEditorPanel
Panel để chỉnh sửa config.
Parameters:
State: ConfigManagerStateOnSave: EventCallback khi saveOnDelete: EventCallback khi deleteOnExport: EventCallback khi export
Sử dụng:
<ConfigEditorPanel
State="@State"
OnSave="@(EventCallback.Factory.Create(this, OnSave))"
OnDelete="@(EventCallback.Factory.Create<Guid>(this, OnDelete))"
OnExport="@(EventCallback.Factory.Create<Guid>(this, OnExport))" />
VariableEditor
Component để chỉnh sửa một variable, tự động thay đổi input type theo variable type.
Parameters:
Variable: ConfigVariableModelOnValueChanged: EventCallback khi value thay đổi
Sử dụng:
<VariableEditor
Variable="@variable"
OnValueChanged="@(EventCallback.Factory.Create<object?>(this, OnValueChanged))" />
Hỗ trợ types:
string: MudTextFieldint: MudNumericField với Min/Maxdouble: MudNumericField với Min/Maxbool: MudSwitchenum: MudSelect với EnumValuesobject: MudTextField multiline (JSON)array: MudTextField multiline (JSON)
Dialogs
ImportConfigDialog
Dialog để import config từ file.
var dialog = await DialogService.ShowAsync<ImportConfigDialog>("Import Config");
var result = await dialog.Result;
ExportConfigDialog
Dialog để export config.
var dialog = await DialogService.ShowAsync<ExportConfigDialog>("Export Config");
CreateConfigDialog
Dialog để tạo config mới.
var dialog = await DialogService.ShowAsync<CreateConfigDialog>("Create Config");
EditConfigDialog
Dialog để chỉnh sửa metadata của config.
var dialog = await DialogService.ShowAsync<EditConfigDialog>("Edit Config");
📦 Models
ConfigFileModel
public class ConfigFileModel
{
public Guid Id { get; set; }
public string ConfigType { get; set; } = string.Empty;
public List<ConfigVariableModel> Variables { get; set; } = new();
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public string? Description { get; set; }
}
ConfigFileMetadataModel
public class ConfigFileMetadataModel
{
public Guid Id { get; set; }
public string ConfigType { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public string? Description { get; set; }
}
ConfigVariableModel
public class ConfigVariableModel
{
public string Name { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty; // "string", "int", "double", "bool", "enum", "object", "array"
public object? Value { get; set; }
public double? Min { get; set; }
public double? Max { get; set; }
public string? Roles { get; set; }
public List<string>? EnumValues { get; set; }
}
💡 Ví dụ
Ví dụ 1: Custom Page với State
@page "/my-configs"
@inject ConfigManagerState State
@inject ISnackbar Snackbar
<PageTitle>My Configs</PageTitle>
<MudContainer>
<MudText Typo="Typo.h4" Class="mb-4">My Configurations</MudText>
@if (State.IsLoading)
{
<MudProgressLinear />
}
<MudGrid>
@foreach (var config in State.Configs)
{
<MudItem xs="12" md="6" lg="4">
<MudCard>
<MudCardContent>
<MudText Typo="Typo.h6">@config.ConfigType</MudText>
<MudText Typo="Typo.body2">@config.Description</MudText>
</MudCardContent>
<MudCardActions>
<MudButton OnClick="@(() => SelectConfig(config))">Select</MudButton>
</MudCardActions>
</MudCard>
</MudItem>
}
</MudGrid>
</MudContainer>
@code {
protected override async Task OnInitializedAsync()
{
State.OnStateChanged += StateChanged;
await State.LoadConfigsAsync();
}
private async Task SelectConfig(ConfigFileMetadataModel config)
{
await State.SelectConfigAsync(config);
Snackbar.Add($"Selected {config.ConfigType}", Severity.Success);
}
private void StateChanged()
{
StateHasChanged();
}
public void Dispose()
{
State.OnStateChanged -= StateChanged;
}
}
Ví dụ 2: Custom Variable Editor
@inject ConfigManagerState State
<MudTable Items="@State.SelectedConfig?.Variables" Hover="true">
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh>Type</MudTh>
<MudTh>Value</MudTh>
<MudTh>Actions</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>@context.Name</MudTd>
<MudTd>@context.Type</MudTd>
<MudTd>
<VariableEditor
Variable="@context"
OnValueChanged="@(EventCallback.Factory.Create<object?>(this, value => OnVariableChanged(context.Name, value)))" />
</MudTd>
<MudTd>
<MudIconButton Icon="@Icons.Material.Filled.Delete"
OnClick="@(() => DeleteVariable(context.Name))" />
</MudTd>
</RowTemplate>
</MudTable>
@code {
private async Task OnVariableChanged(string name, object? value)
{
await State.UpdateVariableAsync(name, value);
}
private async Task DeleteVariable(string name)
{
await State.RemoveVariableAsync(name);
}
}
Ví dụ 3: Import Config với Custom Logic
@inject IDialogService DialogService
@inject ConfigManagerState State
@inject ISnackbar Snackbar
<MudButton OnClick="ImportConfig">Import Config</MudButton>
@code {
private async Task ImportConfig()
{
var parameters = new DialogParameters();
var options = new DialogOptions
{
CloseOnEscapeKey = true,
MaxWidth = MaxWidth.Medium,
FullWidth = true
};
var dialog = await DialogService.ShowAsync<ImportConfigDialog>("Import Config", parameters, options);
var result = await dialog.Result;
if (!result.Canceled)
{
Snackbar.Add("Config imported successfully", Severity.Success);
await State.LoadConfigsAsync();
}
}
}
🎨 Customization
Thay đổi Theme
Component sử dụng MudBlazor theme. Để custom theme:
builder.Services.AddMudServices(config =>
{
config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomRight;
config.SnackbarConfiguration.VisibleStateDuration = 3000;
});
Custom Styling
Thêm CSS vào wwwroot/css/app.css:
.config-manager {
padding: 1rem;
}
.config-list-item {
cursor: pointer;
transition: background-color 0.2s;
}
.config-list-item:hover {
background-color: var(--mud-palette-action-hover);
}
🔧 Troubleshooting
Component không hiển thị
Kiểm tra:
- Đã đăng ký services trong
Program.cs - Đã thêm using trong
_Imports.razor - Đã có MudBlazor services
API calls fail
Kiểm tra:
- HttpClient có
BaseAddressđúng - Backend API đang chạy
- CORS đã được cấu hình (nếu frontend và backend khác domain)
Export không hoạt động
Kiểm tra:
- Đã copy
downloadFile.jsvàowwwroot/js/ - Đã thêm script tag vào
index.html
State không update
Kiểm tra:
- Đã subscribe
OnStateChangedevent - Đã gọi
StateHasChanged()trong event handler - Component implement
IDisposablevà unsubscribe khi dispose