Modulo ListEdit

Il modulo ListEdit permette di gestire entità basic item, ovvero con struttura non legata alle funzioni di DataWeb, con pochi passaggi di configurazione. L'entità deve avere un campo indetità Id e può avere un campo Status per la gestione degli stati dell'item.

 

Il campo Status viene utilizzato per decidere in fase di rimozione dell'elemento se si deve applicare una rimozione effettiva dallo storage o se applicare una "soft delete" impostando lo stato a "Deleted" mantenendo l'entità nella base dati.

 

Per attivare una sezione con modulo ListEdit è sufficiente configurare i settaggi specificando la tabella di appoggio, il form di editing dell'elemento e i campi da mostrare in lista.

 

 

ListEdit supporta gran parte delle funzioni di gestione delle entità ad esclusione di quelle specifiche degli item DataWeb (come le versioni o la navigazione).

Sono supportate le azioni personalizzate sia sul singolo elemento che sulla filtratura.

 

Il modulo può essere utilizzato con la sola configurazione in DataWeb ma, se serve, può essere personalizzato in ogni funzione impostando una classe ereditata da quella base ModuleListEdit e sovrascrivendo i medodi interessati. La nuova classe viene impostata nei relativi settaggi in DataWeb.

 

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using DataWeb.Data;
namespace DataWeb.Structure.Modules
{
    public class ModuleSubscriberListEdit(Section section, NavigationContext navigationContext, IServiceProvider serviceProvider) : ModuleListEdit(section, navigationContext, serviceProvider)
    {
        private readonly TimeProvider timeProvider = serviceProvider.GetService<TimeProvider>();
        public override Task ProcessOnSaveDataAsync(BasicItem basicItem, List<Form.ProvidedValue> providedValues, CancellationToken cancellationToken = default)
        {
            if (basicItem.Id == StructureDefinition.VoidId)
            {
                basicItem.Data["SubscriptionDate"] = timeProvider.GetLocalNow();
            }
            return Task.CompletedTask;
        }
    }
}

 

In questo esempio stiamo perfezionando il salvataggio dell'item in modo che se è nuovo venga impostata anche la data di iscrizione.

 

Questa tabella può essere usata come riferimento per identificare le aree della classe base ModuleListEdit che possono essere personalizzate creando una classe derivata, come l'esempio di ModuleSubscriberListEdit.

 

MetodoDescrizionePossibile personalizzazione
GetDataAsyncRecupera i dati degli item e restituisce un oggetto ListData contenente informazioni sugli item, i campi dati, i widget, i filtri di ricerca e la paginazione.Puoi personalizzare come vengono recuperati e trasformati i dati o aggiungere logica specifica per i widget, filtri, o il comportamento di ordinamento.
GetDeferredDataAsyncRecupera dati aggiuntivi in modo asincrono per gli item specificati. Utilizzato per caricare dati "deferred".Può essere sovrascritto per modificare il comportamento di caricamento dei dati differiti o per aggiungere logica custom.
ConvertToListItemConverte un BasicItem in un ListItem contenente i campi dati, i widget e le altre informazioni di rappresentazione.Sovrascrivi per personalizzare la conversione da BasicItem a ListItem (es. aggiungere logica personalizzata per i campi dati).
GetDataFieldsAsyncRecupera i campi dati configurati per la sezione.Personalizza per aggiungere o modificare i campi dati visibili agli utenti.
GetListWidgetsRecupera i widget configurati per la lista.Sovrascrivi per aggiungere widget personalizzati o modificare i widget esistenti.
ProcessListWidgetsValuesElabora i valori dei widget per gli item specificati.Personalizza la logica per elaborare o calcolare i valori dei widget in base ai dati degli item o ai parametri utente.
ProcessListWidgetsDeferredValuesAsyncElabora i valori differiti dei widget in modo asincrono.Puoi personalizzare come vengono gestiti i valori differiti, aggiungendo logica per calcoli complessi o chiamate esterne.
GetItemIsWriteDetermina se un item è modificabile. Restituisce sempre true di default.Sovrascrivibile per aggiungere logica che limita la possibilità di modifica (es. basandosi su permessi utente o stato dell'item).
GetFormDataAsyncRecupera i dati necessari per la visualizzazione del form di modifica di un item.Può essere personalizzato per modificare il comportamento del caricamento dati o aggiungere valori predefiniti dinamici.
SaveItemAsyncSalva un item, validandone i dati, aggiornando il database e registrando eventi di audit.Sovrascrivibile per aggiungere logica personalizzata al salvataggio (es. aggiornare campi extra o inviare notifiche).
ProcessOnSaveDataAsyncEseguito durante il salvataggio per elaborare ulteriori dati dell'item.Può essere sovrascritto per aggiungere logica personalizzata per il salvataggio, come mostrato nell'esempio con la data di iscrizione (SubscriptionDate).
CloneItemAsyncCrea una copia di un item esistente.Personalizzabile per modificare il comportamento di clonazione o per aggiungere logica extra (es. registrazione di log o modifica di campi clonati).
DeleteItemsAsyncElimina uno o più item. Può effettuare una "soft delete" (impostando lo stato a Deleted) o una rimozione effettiva dallo storage.Sovrascrivibile per modificare il comportamento della cancellazione, ad esempio per gestire permessi o eseguire azioni aggiuntive.
SetSearchAsyncAggiorna i filtri di ricerca basati sui parametri forniti.Sovrascrivibile per aggiungere logica personalizzata per i filtri di ricerca.
GetSearchSuggestionsAsyncFornisce suggerimenti di ricerca basati sui campi configurati.Personalizza per aggiungere logica di completamento automatico o suggerimenti avanzati.
SetPageIndexAsyncImposta l'indice di pagina corrente per la paginazione.Può essere personalizzato per aggiungere logica di controllo sull'indice di pagina.
SetOrderByAsyncModifica l'ordinamento della lista basandosi sul campo specificato.Può essere personalizzato per aggiungere logica avanzata per l'ordinamento o supportare nuovi tipi di ordinamento.
GetActionsAsyncRecupera le azioni disponibili per la sezione.Sovrascrivibile per aggiungere azioni personalizzate.
ProcessActionAsyncGestisce l'esecuzione delle azioni definite.Può essere sovrascritto per aggiungere logica custom per azioni personalizzate, come esportazioni o processi specifici.
ProcessExportAsyncElabora l'esportazione di item in un file Excel.Sovrascrivibile per personalizzare l'output del file esportato (es. aggiungere colonne extra o modificare il formato).

 

Il metodo GetBasicItemsAsync può essere utilizzato nei vari contesti per recuperare rapidamente sia gli item filtrati che quelli selezionati (specificando gli Id degli item coinvolti tramite itemIds). Ad esempio:

 

        public override async Task<ContextAction.Result> ProcessActionAsync(ContextAction action, IUser user, List<string> itemIds = null, List<Form.ProvidedValue> controlValues = null, NavigationContext navigationContext = null, CancellationToken cancellationToken = default)
        {
            var result = await base.ProcessActionAsync(action, user, itemIds, controlValues, navigationContext, cancellationToken);

            switch (action.Name)
            {
                case "FilteredItemsExport":
                    var basicItems = (await GetBasicItemsAsync(false, cancellationToken: cancellationToken)).BasicItems;
                    result = new ContextAction.Result { IsValid = true, Stream = await ProcessExportAsync(basicItems, cancellationToken), FileName = string.Format("{0}.xlsx", section.Title), ContentType = "application/vnd.ms-excel" };
                    break;
                case "SelectedItemsExport":
                    if (itemIds == null || itemIds.Count == 0)
                    {
                        return new ContextAction.Result { IsValid = false, Error = new ValidationError { Name = "ActionName", Message = "items are required" } };
                    }
                    var selectedBasicItems = (await GetBasicItemsAsync(false, itemIds: itemIds, cancellationToken: cancellationToken)).BasicItems;

                    result = new ContextAction.Result { IsValid = true, Stream = await ProcessExportAsync(selectedBasicItems, cancellationToken), FileName = string.Format("{0}.xlsx", section.Title), ContentType = "application/vnd.ms-excel" };
                    break;
            }

            return result;
        }

 

Esempio di personalizzazione per gestire il permesso di scrittura sull'item:

     public override bool GetItemIsWrite(BasicItem basicItem)
        {
            return basicItem.Data.ContainsKey("IsSystem") && !Convert.ToBoolean(basicItem.Data["IsSystem"]);
        }

 

Esempio di personalizzazione per impostare una lista nel selettore di filtratura:

        public override async Task<List<SearchInfo.SearchFilter>> GetSearchFiltersAsync(IEnumerable<ListData.DataField> dataFields, IEnumerable<UserSetting> userSettings, CancellationToken cancellationToken = default)
        {
            var searchFilters = await base.GetSearchFiltersAsync(dataFields, userSettings, cancellationToken);

            // Set TemplateContext
            var searchFilterTemplateContext = searchFilters.FirstOrDefault(x => x.Name == "TemplateContext");
            if (searchFilterTemplateContext != null)
            {
                searchFilterTemplateContext.Type = "List";
                searchFilterTemplateContext.ListValues = (await templateService.GetTemplateContextsAsync(cancellationToken)).Select(x => new List.ListItem(x)).ToList();
            }

            return searchFilters;
        }

 

Esempio di personalizzazione per impostare un'azione:

        public override async Task<List<ContextAction>> GetActionsAsync(CancellationToken cancellationToken = default)
        {
            var actions = await base.GetActionsAsync(cancellationToken);

            actions.Add(contextActionService.GetRemoteAction(localizer["DataWeb.Explorer.ModuleResourceListEdit_CachedResourcesUpdate"], "CachedResourcesUpdate", ContextActionType.Update, context: "FilteredItems", isConfirmRequired: true, isReloadAfterProcess: false, isSaveItemBeforeProcess: false));

            return actions;
        }

        public override async Task<ContextAction.Result> ProcessActionAsync(ContextAction action, IUser user, List<string> itemIdMasters = null, List<Form.ProvidedValue> controlValues = null, NavigationContext navigationContext = null, CancellationToken cancellationToken = default)
        {
            var result = await base.ProcessActionAsync(action, user, itemIdMasters, controlValues, navigationContext, cancellationToken);

            switch (action.Name)
            {
                case "CachedResourcesUpdate":
                    result = await UpdateCachedResourcesAsync(cancellationToken);
                    break;
            }

            return result;
        }

 

Esempio di personalizzazione per recuperare un'entità diversa da basic item:

        public override async Task<ListData> GetDataAsync(IEnumerable<UserSetting> userSettings, CancellationToken cancellationToken = default)
        {
            await section.InitListWidgetsAsync(serviceProvider, cancellationToken);

            var dataFields = await GetDataFieldsAsync(cancellationToken);

            var filter = new ResourceFilter
            {
                IsDeletedIncluded = navigationContext.User.PublishMode == PublishMode.Maintenance,
                Culture = userSettings.FirstOrDefault(x => x.Name == "SearchCulture")?.Value,
                CultureMode = userSettings.FirstOrDefault(x => x.Name == "SearchCultureMode")?.Value,
                ResourceContext = userSettings.FirstOrDefault(x => x.Name == "SearchResourceContext")?.Value,
                SearchResourceGroup = userSettings.FirstOrDefault(x => x.Name == "SearchResourceGroup")?.Value,
                SearchResourceKey = userSettings.FirstOrDefault(x => x.Name == "SearchResourceKey")?.Value,
                SearchText = userSettings.FirstOrDefault(x => x.Name == "SearchText")?.Value
            };

            var searchStatusSetting = userSettings.FirstOrDefault(x => x.Name == "SearchStatus");
            if (!string.IsNullOrEmpty(searchStatusSetting?.Value))
            {
                filter.Status = localizationService.ConvertStatus(searchStatusSetting.Value);
            }

            var pageIndexSetting = userSettings.FirstOrDefault(x => x.Name == "PageIndex");

            var pagination = PaginationHelper.GetPaginationInfo(section.Settings.PageSize ?? 50, pageIndexSetting != null ? Convert.ToInt64(pageIndexSetting.Value) : 0, await localizationService.GetResourceCountAsync(filter));
            if (pagination.PageCount > 1)
            {
                filter.IsPagination = true;
                filter.PageIndex = pagination.PageIndex;
                filter.PageSize = pagination.PageSize;
            }

            var orderByModeSetting = userSettings.FirstOrDefault(x => x.Name == "OrderByMode");
            var orderByDataFieldSetting = userSettings.FirstOrDefault(x => x.Name == "OrderByDataField");

            if (orderByDataFieldSetting?.Value != null && orderByDataFieldSetting?.Value != "Position")
            {
                filter.OrderDataFields = [new() { Name = orderByDataFieldSetting.Value, OrderMode = orderByModeSetting?.Value }];
            }

            var resources = await resourceStore.GetResourcesAsync(filter);

            var moduleData = new ListData()
            {
                Items = resources.Select(ConvertToListEditItem).ToList(),
                DataFields = dataFields,
                ListWidgets = GetListWidgets(cancellationToken),
                Actions = (await GetActionsAsync(cancellationToken)).Select(x => x.ToContextActionInfo()),
                Search = new SearchInfo { Filters = await GetSearchFiltersAsync(dataFields, userSettings, cancellationToken) },
                OrderBy = new OrderByInfo
                {
                    Mode = userSettings.FirstOrDefault(x => x.Name == "OrderByMode")?.Value ?? "DESC",
                    DataField = userSettings.FirstOrDefault(x => x.Name == "OrderByDataField")?.Value ?? "Position"
                },
                Pagination = pagination
            };

            ProcessListWidgetsValues(moduleData.Items, userSettings);

            return moduleData;
        }

        private ListItem ConvertToListEditItem(Resource resource)
        {
            return new ListItem()
            {
                Id = resource.Id,
                Label = resource.Name,
                IsWrite = !resource.IsSystem,
                DataFields = [
                    new ListItem.DataField{ Name = nameof(resource.Name), Value = resource.Name},
                    new ListItem.DataField{ Name = nameof(resource.Status), Value = Convert.ToString(resource.Status)},
                    new ListItem.DataField{ Name = nameof(resource.IsSystem), Value = resource.IsSystem},
                    new ListItem.DataField{ Name = nameof(resource.ResourceContext), Value = resource.ResourceContext},
                    new ListItem.DataField{ Name = nameof(resource.ResourceGroup), Value = resource.ResourceGroup},
                    new ListItem.DataField{ Name = nameof(resource.ResourceKey), Value = resource.ResourceKey},
                    new ListItem.DataField{ Name = nameof(resource.CultureItems), Value = resource.CultureItems},
                    new ListItem.DataField{ Name = nameof(resource.Notes), Value = resource.Notes}
                ],
                ListWidgetValues = []
            };
        }