Hola
Te paso este código, el componente Roles llama al componente FormularioRol que contiene un árbol de permisos, el árbol de permisos implementa checkboxes, hay mucho código extra que puedes ignorar, espero que te sirva de algo...
La magia reside en que el atributo CheckedValues del árbol debe ser un subconjunto del atributo Data.
Si necesitas más ayuda, no dudes en comentar, Saludos
@using Apps.Siga.Components
<PageTitle>@pageTitle</PageTitle>
@attribute [Authorize]
@implements IDisposable
@namespace Apps.Siga.Pages
@inherits ProviderInterceptor
@page "/siga/administracion/roles"
<RadzenCard Style="min-height: 720px">
<div class="row">
<div class="col-md-5">
<ArbolRoles Items="@items" OnChangeActionAsync="@GetSingleRolAsync" />
</div>
<div class="col-md-7">
<div class="row">
<div class="col-12 text-end">
<RadzenButton Click=@(args => LimpiarForm()) Text="Nuevo" Icon="add" ButtonStyle="ButtonStyle.Primary" />
</div>
<div class="col-12">
<RadzenTemplateForm TItem="VmAddOrUpdateRol" Data=@model Submit="@OnSubmitFormAsync" Visible="@showForm">
<RadzenFieldset Text="Detalles">
<div class="row">
<div class="col-md-9">
<RadzenLabel Text="Nombre" Component="Nombre" />
<RadzenTextBox Name="Nombre" @bind-Value=@model.Nombre Style="width: 100%"
MouseEnter="@(args => ShowTooltip(args, "Nombre con el cual será identificado el rol."))" />
<RadzenRequiredValidator Component="Nombre" Text="Debes ingresar el nombre del rol" />
</div>
<div class="col-md-3">
<RadzenLabel Text="Padre" Component="PadreId" />
<RadzenNumeric TValue="int?" Name="PadreId" @bind-Value=@model.PadreId Style="width: 100%"
MouseEnter="@(args => ShowTooltip(args, "Id del identificador contenedor padre del permiso"))" />
</div>
</div>
<div class="row">
<div class="col-md-12">
<RadzenLabel Text="Descripción" Component="Descripcion" />
<RadzenTextArea Name="Descripcion" @bind-Value=@model.Descripcion Style="width: 100%" Rows="2"
MouseEnter="@(args => ShowTooltip(args, "Breve descripción del rol y/o sus funciones."))" />
<RadzenRequiredValidator Component="Descripcion" Text="Debes ingresar la descripción del rol" />
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="col-md-6">
<RadzenLabel Text="Fecha Inicial" Component="FechaInicial" />
<RadzenDatePicker Name="FechaInicial" @bind-Value=@model.FechaInicial Style="width: 100%" DateFormat="dd-MMM-yyyy"
MouseEnter="@(args => ShowTooltip(args, "Inicio de operaciones, vacío para omitir restricciones de tiempo"))" />
</div>
<div class="col-md-6">
<RadzenLabel Text="Fecha Final" Component="FechaFinal" />
<RadzenDatePicker Name="FechaFinal" @bind-Value=@model.FechaFinal Style="width: 100%" DateFormat="dd-MMM-yyyy"
MouseEnter="@(args => ShowTooltip(args, "Fin de operaciones, vacío para omitir restricciones de tiempo"))" />
</div>
<div class="col-md-12 mt-3">
<RadzenSwitch @bind-Value=@model.AddOrUpdateCategorias
Change="@((args) => {model.CategoriasSeleccionadas = categoriasIniciales;})" />
<span class="form-text">Activa este switch si deseas actualizar las categorías del rol</span>
<RadzenListBox class="w-100"
Multiple="true"
Data=@categorias
ValueProperty="Id"
Style="height:200px"
AllowFiltering="true"
TextProperty="Nombre"
Name="ComboCategorias"
Disabled=@(!model.AddOrUpdateCategorias)
@bind-Value=@model.CategoriasSeleccionadas
Placeholder="Categorias"
FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive" />
</div>
</div>
</div>
<div class="col-md-6">
<div class="row">
<div class="col-md-12 mt-4 mb-2">
<RadzenSwitch @bind-Value=@model.IsInDevelopedMode />
<span class="form-text">Activa el Modo Desarrollador para que este rol no este disponible en etapa de producción</span>
</div>
<div class="col-md-12 mt-3">
<RadzenSwitch @bind-Value=@model.AddOrUpdatePermisos
Change=@ResetCheckedPermisos />
<span class="form-text">Activa este switch si deseas actualizar los permisos del rol</span>
</div>
<div class="col-md-12">
<RadzenTree Expand=@OnExpand
AllowCheckBoxes="true"
AllowCheckParents="false"
AllowCheckChildren="false"
Data=@permisosPrincipales
CheckedValues="@checkedValues"
CheckedValuesChanged="@(value => OnChangeCheckedValues(value))">
<RadzenTreeLevel Text=@GetTextForNode
Template="@PermisoTemplate"
Expanded="@(value => (value as VmHojaPermiso).HasChildren)" />
</RadzenTree>
</div>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-md-12 text-end">
<RadzenButton ButtonType="ButtonType.Reset" Text="Cancelar" Icon="cancel"
Click="@(() => {showForm = false;})"
ButtonStyle="ButtonStyle.Light"></RadzenButton>
<RadzenButton Text="Eliminar"
Icon="delete_outline"
Visible="@(idRol != 0)"
Click=@ShowDeleteDialogAsync
ButtonStyle="ButtonStyle.Secondary" />
<RadzenButton ButtonType="ButtonType.Submit" Text="Guardar" Icon="save" ButtonStyle="ButtonStyle.Primary"></RadzenButton>
</div>
</div>
</RadzenFieldset>
</RadzenTemplateForm>
</div>
</div>
</div>
</div>
</RadzenCard>
@code {
[Inject] protected DialogService DialogService { get; set; }
[Inject] protected IRolesProvider RolesProvider { get; set; }
[Inject] protected TooltipService tooltipService { get; set; }
[Inject] protected AppStateContainer StateContainer { get; set; }
[Inject] protected IPermisosProvider PermisosProvider { get; set; }
[Inject] protected ICategoriasProvider CategoriasProvider { get; set; }
[Inject] protected NotificationService NotificationService { get; set; }
private int idRol = 0;
private bool showForm = false;
private string pageTitle = "Administración de Roles";
private VmAddOrUpdateRol model { get; set; } = new();
private IEnumerable<VmHojaRol> items = new Collection<VmHojaRol>();
private IEnumerable<object> checkedValues = new Collection<object>();
private IEnumerable<int> categoriasIniciales = new Collection<int>();
private IEnumerable<VmHojaPermiso> permisos = new Collection<VmHojaPermiso>();
private IEnumerable<VmComboCategoria> categorias = new Collection<VmComboCategoria>();
private IEnumerable<VmHojaPermiso> permisosIniciales = new Collection<VmHojaPermiso>();
private IEnumerable<VmHojaPermiso> permisosPrincipales = new Collection<VmHojaPermiso>();
protected override void OnInitialized() {
StateContainer.UpdatePageTitle(pageTitle);
base.OnInitialized();
}
protected override async Task OnInitializedAsync() {
try {
items = await RolesProvider.GetAllAsync();
permisos = await PermisosProvider.GetAllAsync();
categorias = await CategoriasProvider.GetComboAsync();
permisosPrincipales = permisos?
.Where(x => x.PadreId is null)
.ToList();
} catch (Exception e) {
NotificationService.Notify(new NotificationMessage {
Severity = NotificationSeverity.Error,
Summary = "Error",
Detail = e.Message,
Duration = 4000
});
}
await base.OnInitializedAsync();
}
#region Funcionalidad del árbol de roles
public async Task GetSingleRolAsync(int id) {
try {
idRol = id;
showForm = true;
model = Mapper.Map(await RolesProvider.GetAsync(idRol))
.ToANew<VmAddOrUpdateRol>();
model.PermisosSeleccionados = model.PermisosSeleccionados ?? new Collection<int>();
model.CategoriasSeleccionadas = model.CategoriasSeleccionadas ?? new Collection<int>();
permisosIniciales = permisos
.Where(x => model.PermisosSeleccionados.Contains(x.Id))
.ToList()
.Clone();
categoriasIniciales = model.CategoriasSeleccionadas.ToArray();
checkedValues = permisos?
.Where(x => model.PermisosSeleccionados.Contains(x.Id));
} catch (Exception e) {
NotificationService.Notify(new NotificationMessage {
Severity = NotificationSeverity.Error,
Summary = "Error",
Detail = e.Message,
Duration = 4000
});
}
}
#endregion
#region Funcionalidad del formulario de rol
void ShowTooltip(ElementReference elementReference, string text = null) =>
tooltipService.Open(elementReference, text);
private void LimpiarForm() {
idRol = 0;
showForm = true;
model = new VmAddOrUpdateRol();
model.PermisosSeleccionados = new Collection<int>();
model.CategoriasSeleccionadas = new Collection<int>();
permisosIniciales = permisos
.Where(x => model.PermisosSeleccionados.Contains(x.Id))
.ToList()
.Clone();
categoriasIniciales = model.CategoriasSeleccionadas.ToArray();
checkedValues = permisos.Where(x => model.PermisosSeleccionados.Contains(x.Id));
}
public async Task OnSubmitFormAsync() {
try {
if (idRol == 0)
await RolesProvider.AddAsync(model);
else
await RolesProvider.UpdateAsync(model, idRol);
items = await RolesProvider.GetAllAsync();
NotificationService.Notify(new NotificationMessage {
Severity = NotificationSeverity.Success,
Summary = "Éxito",
Detail = "Registro actualizado",
Duration = 4000
});
} catch (Exception e) {
NotificationService.Notify(new NotificationMessage {
Severity = NotificationSeverity.Error,
Summary = "Error",
Detail = e.Message,
Duration = 4000
});
}
}
async Task ShowDeleteDialogAsync() {
var result = await DialogService.OpenAsync("Confirmación", ds =>
@<div>
<p class="mb-4">Estás a punto de borrar el elemento seleccionado ¿Deseas continuar?</p>
<div class="row">
<div class="col text-end">
<RadzenButton ButtonStyle="ButtonStyle.Light" Text="No" Click="() => ds.Close(false)" />
<RadzenButton ButtonStyle="ButtonStyle.Secondary" Text="Si" Click="() => OnDeleteAsync()" />
</div>
</div>
</div>
);
}
public async Task OnDeleteAsync() {
try {
if (idRol == 0)
throw new ArgumentException("No se a seleccionado un registro");
else
await RolesProvider.DeleteAsync(idRol);
items = await RolesProvider.GetAllAsync();
showForm = false;
DialogService.Close();
NotificationService.Notify(new NotificationMessage {
Severity = NotificationSeverity.Success,
Summary = "Éxito",
Detail = "Registro eliminado con éxito",
Duration = 4000
});
} catch (Exception e) {
NotificationService.Notify(new NotificationMessage {
Severity = NotificationSeverity.Error,
Summary = "Error",
Detail = e.Message,
Duration = 4000
});
}
}
#endregion
#region Funcionalidad del árbol de permisos
void OnExpand(TreeExpandEventArgs args) {
var permiso = args.Value as VmHojaPermiso;
var children = GetChildren(permiso.Id);
args.Children.Data = children;
args.Children.Text = GetTextForNode;
args.Children.HasChildren = (data) => ((VmHojaPermiso)data).HasChildren;
args.Children.Template = PermisoTemplate;
}
RenderFragment<RadzenTreeItem> PermisoTemplate = (context) => builder => {
bool isParent = context.HasChildren;
builder.OpenComponent<RadzenIcon>(0);
builder.AddAttribute(1, "Icon", isParent ? "account_tree" : "label");
builder.CloseComponent();
builder.AddContent(2, context.Text);
};
string GetTextForNode(object data) {
VmHojaPermiso _data = (VmHojaPermiso)data;
return _data.Descripcion;
}
public IEnumerable<VmHojaPermiso> GetChildren(int idPadre) =>
permisos
.Where(x => x.PadreId == idPadre)
.OrderBy(x => x.IndiceDeOrdenamiento)
.ToList();
private void OnChangeCheckedValues(IEnumerable<object> value) {
IEnumerable<int> checkeds = ((IEnumerable)value).Cast<VmHojaPermiso>()
.Select(x => x.Id)
.ToList();
model.PermisosSeleccionados = checkeds;
}
private void ResetCheckedPermisos() {
IEnumerable<int> idsPermisos = ((IEnumerable)permisosIniciales).Cast<VmHojaPermiso>()
.Select(x => x.Id)
.ToList();
model.PermisosSeleccionados = idsPermisos;
}
#endregion
}
@namespace Apps.Siga.Components
<RadzenTemplateForm TItem="VmAddOrUpdateRol" Data=@Rol Submit="@InvokeOnSubmitAsync" Visible="@ShowForm">
<RadzenFieldset Text="Detalles">
<div class="row">
<div class="col-md-9">
<RadzenLabel Text="Nombre" Component="Nombre" />
<RadzenTextBox Name="Nombre" @bind-Value=@Rol.Nombre Style="width: 100%"
MouseEnter="@(args => ShowTooltip(args, "Nombre con el cual será identificado el rol."))" />
<RadzenRequiredValidator Component="Nombre" Text="Debes ingresar el nombre del rol" />
</div>
<div class="col-md-3">
<RadzenLabel Text="Padre" Component="PadreId" />
<RadzenNumeric TValue="int?" Name="PadreId" @bind-Value=@Rol.PadreId Style="width: 100%"
MouseEnter="@(args => ShowTooltip(args, "Id del identificador contenedor padre del permiso"))" />
</div>
</div>
<div class="row">
<div class="col-md-12">
<RadzenLabel Text="Descripción" Component="Descripcion" />
<RadzenTextArea Name="Descripcion" @bind-Value=@Rol.Descripcion Style="width: 100%" Rows="2"
MouseEnter="@(args => ShowTooltip(args, "Breve descripción del rol y/o sus funciones."))" />
<RadzenRequiredValidator Component="Descripcion" Text="Debes ingresar la descripción del rol" />
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="col-md-6">
<RadzenLabel Text="Fecha Inicial" Component="FechaInicial" />
<RadzenDatePicker Name="FechaInicial" @bind-Value=@Rol.FechaInicial Style="width: 100%" DateFormat="dd-MMM-yyyy"
MouseEnter="@(args => ShowTooltip(args, "Inicio de operaciones, vacío para omitir restricciones de tiempo"))" />
</div>
<div class="col-md-6">
<RadzenLabel Text="Fecha Final" Component="FechaFinal" />
<RadzenDatePicker Name="FechaFinal" @bind-Value=@Rol.FechaFinal Style="width: 100%" DateFormat="dd-MMM-yyyy"
MouseEnter="@(args => ShowTooltip(args, "Fin de operaciones, vacío para omitir restricciones de tiempo"))" />
</div>
<div class="col-md-12 mt-3">
<RadzenSwitch @bind-Value=@Rol.AddOrUpdateCategorias
Change="@((args) => {Rol.CategoriasSeleccionadas = categoriasIniciales;})" />
<span class="form-text">Activa este switch si deseas actualizar las categorías del rol</span>
<RadzenListBox class="w-100"
Multiple="true"
Data=@Categorias
ValueProperty="Id"
Style="height:200px"
AllowFiltering="true"
TextProperty="Nombre"
Name="ComboCategorias"
Disabled=@(!Rol.AddOrUpdateCategorias)
@bind-Value=@Rol.CategoriasSeleccionadas
Placeholder="Categorias"
FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive" />
</div>
</div>
</div>
<div class="col-md-6">
<div class="row">
<div class="col-md-12 mt-4 mb-2">
<RadzenSwitch @bind-Value=@Rol.IsInDevelopedMode />
<span class="form-text">Activa el Modo Desarrollador para que este rol no este disponible en etapa de producción</span>
</div>
<div class="col-md-12 mt-3">
<RadzenSwitch @bind-Value=@Rol.AddOrUpdatePermisos
Change="@((args) => {checkedValues = permisosIniciales;})" />
<span class="form-text">Activa este switch si deseas actualizar los permisos del rol</span>
</div>
<div class="col-md-12">
<RadzenTree Expand=@OnExpand
AllowCheckBoxes="true"
AllowCheckParents="false"
AllowCheckChildren="false"
Data=@PermisosPrincipales
CheckedValues="@checkedValues"
CheckedValuesChanged="@(value => InvokeOnChangeCheckedValues(value))">
<RadzenTreeLevel Text=@GetTextForNode
Template="@PermisoTemplate"
Expanded="@(value => (value as VmHojaPermiso).HasChildren)" />
</RadzenTree>
</div>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-md-12 text-end">
<RadzenButton ButtonType="ButtonType.Reset" Text="Cancelar" Icon="cancel"
Click="@(() => {ShowForm = false;})"
ButtonStyle="ButtonStyle.Light"></RadzenButton>
<RadzenButton Text="Eliminar"
Icon="delete_outline"
Visible="@ShowDeleteButton"
Click=@InvokeOnDeleteAsync
ButtonStyle="ButtonStyle.Secondary" />
<RadzenButton ButtonType="ButtonType.Submit" Text="Guardar" Icon="save" ButtonStyle="ButtonStyle.Primary"></RadzenButton>
</div>
</div>
</RadzenFieldset>
</RadzenTemplateForm>
@code {
[Inject] TooltipService tooltipService { get; set; }
[Parameter] public bool ShowForm { get; set; } = false;
[Parameter] public EventCallback OnSubmitAsync { get; set; }
[Parameter] public EventCallback OnDeleteAsync { get; set; }
[Parameter] public VmAddOrUpdateRol Rol { get; set; } = new();
[Parameter] public bool ShowDeleteButton { get; set; } = false;
[Parameter] public IEnumerable<VmHojaPermiso> Permisos { get; set; }
[Parameter] public IEnumerable<VmComboCategoria> Categorias { get; set; }
[Parameter] public IEnumerable<VmHojaPermiso> PermisosPrincipales { get; set; }
[Parameter] public EventCallback<IEnumerable<int>> OnChangeCheckedValues { get; set; }
private IEnumerable<object> checkedValues = new Collection<object>();
private IEnumerable<int> categoriasIniciales = new Collection<int>();
private IEnumerable<VmHojaPermiso> permisosIniciales = new Collection<VmHojaPermiso>();
protected override Task OnParametersSetAsync() {
/*categoriasIniciales = Rol?.CategoriasSeleccionadas
?? new Collection<int>();
permisosIniciales = Permisos?
.Where(x => Rol.PermisosSeleccionados.Contains(x.Id))
?? new Collection<VmHojaPermiso>();*/
checkedValues = Permisos?
.Where(x => Rol.PermisosSeleccionados.Contains(x.Id));
return base.OnParametersSetAsync();
}
private async Task InvokeOnDeleteAsync() =>
await OnDeleteAsync.InvokeAsync();
private async Task InvokeOnSubmitAsync() =>
await OnSubmitAsync.InvokeAsync();
void ShowTooltip(ElementReference elementReference, string text = null) =>
tooltipService.Open(elementReference, text);
#region Funcionalidad del árbol de permisos
void OnExpand(TreeExpandEventArgs args) {
var permiso = args.Value as VmHojaPermiso;
var children = GetChildren(permiso.Id);
args.Children.Data = children;
args.Children.Text = GetTextForNode;
args.Children.HasChildren = (data) => ((VmHojaPermiso)data).HasChildren;
args.Children.Template = PermisoTemplate;
}
RenderFragment<RadzenTreeItem> PermisoTemplate = (context) => builder => {
bool isParent = context.HasChildren;
builder.OpenComponent<RadzenIcon>(0);
builder.AddAttribute(1, "Icon", isParent ? "account_tree" : "label");
builder.CloseComponent();
builder.AddContent(2, context.Text);
};
string GetTextForNode(object data) {
VmHojaPermiso _data = (VmHojaPermiso)data;
return _data.Descripcion;
}
public IEnumerable<VmHojaPermiso> GetChildren(int idPadre) =>
Permisos
.Where(x => x.PadreId == idPadre)
.OrderBy(x => x.IndiceDeOrdenamiento)
.ToList();
private void InvokeOnChangeCheckedValues(IEnumerable<object> value) {
IEnumerable<int> checkeds = ((IEnumerable)value).Cast<VmHojaPermiso>()
.Select(x => x.Id)
.ToList();
OnChangeCheckedValues.InvokeAsync(checkeds);
}
#endregion
}