Control actions

Each control can have one or more actions (called ContextActions) added to it.
Once added, the actions are displayed in the appropriate menu to the right of the control:
 
Actions example
 
These are of the available action types:
 
  • UpdateData: An action to update the control data
  • SetNavigation: Navigation action to another section or item
  • SetRemoteAction: An action for executing client-side custom code
  • SetLocalAction: Action for server-side custom code execution
 
The Control class provides a number of methods based on ContextActionService to quickly add a new action to the control.
 
UpdateData Action 
The method adds a data update action in the control so that you don't have to reload the page if the data source has changed since you opened the form. The following code adds the update option for the default CheckBoxList control.
 
using System;
using System.Threading.Tasks;

namespace DataWeb.Data.Controls
{
    public class CheckBoxList : ControlBase
    {
        public CheckBoxList(Form form, IServiceProvider serviceProvider) : base(form, serviceProvider)
        {
        }

        public override Task InitAsync()
        {
            AddActionUpdateData();

            return Task.CompletedTask;
        }
    }
}
 
It should be noted that all the default controls that handle lists of values (CheckBoxList, ListBox, RadioButtonList, DropDownList, etc.) have already set this action.
 
SetNavigation Action 
The method adds an action that allows the user to open a section or item quickly. For example, given a DropDownList with a list of categories, you want to allow the user to add a new one, edit the selected one, or open the dedicated section.
DataWeb will automatically save the item's data before setting up browsing. The navigation is added to the existing one, i.e. when the user closes the category, it is returned to the starting item, making the experience more enjoyable.
 
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using DataWeb.Structure;
using DataWeb.Identity;
using DataWeb.Data;
using DataWeb.Data.Controls;
using DataWeb.WebApp.Infrastructure;
using Microsoft.Extensions.Localization;

namespace DataWeb.WebApp.DataWeb.Data.Controls
{
    public class Product_CategoryName :  DropDownList
    {
        private readonly CategoryRepository categoryRepository;
        private readonly IStringLocalizer localizer;

        public Product_CategoryName(Form form, IServiceProvider serviceProvider) : base(form, serviceProvider)
        {
            categoryRepository = serviceProvider.GetService<CategoryRepository>();
            localizer = serviceProvider.GetService<IStringLocalizer>();
        }

        public override async Task InitAsync()
        {
            await base.InitAsync();

            AddActionSetNavigation(localizer["DataWeb.Form.ControlAction_EditSelected"], new List<Action.Step>()
            {
                new Action.Step{ IdMaster = StructureDefinition.RootIdMaster, SectionName = AppStructureDefinition.SectionDemoCategories, IsHidden = true },
                new Action.Step{ IdMaster = "{ {IdMaster} }", SectionName = AppStructureDefinition.SectionDemoCategoryData }
            });

            AddActionSetNavigation(localizer["DataWeb.Form.ControlAction_New"], new List<Action.Step>()
            {
                new Action.Step{ IdMaster = StructureDefinition.RootIdMaster, SectionName = AppStructureDefinition.SectionDemoCategories, IsHidden = true },
                new Action.Step{ IdMaster = StructureDefinition.VoidIdMaster, SectionName = AppStructureDefinition.SectionDemoCategoryData }
            });

            AddActionSetNavigation(localizer["DataWeb.Form.ControlAction_OpenSection"], new List<Action.Step>()
            {
                new Action.Step{ IdMaster = StructureDefinition.RootIdMaster, SectionName = AppStructureDefinition.SectionDemoCategories }
            });
        }
    }
}
 
The method must be provided with the title of the action (in the example localized with Localizer) and the new navigation steps. The optional currentStep parameter allows you to move to a section adjacent to the current one.
 
SetLocalAction Action 
The method adds an action that allows the user to run client code placed in the custom Vue.js component.
For example, in the User_AccountNewsletter control we find the addition of the local (client) Edit action:
 
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using DataWeb.Identity;
using DataWeb.Structure;
using DataWeb.Validation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;

namespace DataWeb.Data.Controls
{
    public class User_AccountNewsletter(Form form, IServiceProvider serviceProvider) : Control(form, serviceProvider)
    {
        private readonly IStringLocalizer localizer = serviceProvider.GetService<IStringLocalizer>();

        public override Task InitAsync(CancellationToken cancellationToken = default)
        {
            AddActionSetLocalAction(localizer["DataWeb.Form.AccountNewsletter_Edit"], "Edit", ContextActionType.Edit);

            return Task.CompletedTask;
        }

        public override Task<List<ValidationError>>ValidateAsync(object value, List<Form.ProvidedValue> providedValues, Dictionary<string, object> sectionData, IUser user, string itemId = null, NavigationContext navigationContext = null, CancellationToken cancellationToken = default)
        {
            var errors = new List<ValidationError>();

            return Task.FromResult(errors);
        }

        public override Task<bool> IsUpdateDataAsync(object value, List<Form.ProvidedValue> providedValues, Dictionary<string, object> sectionData, IUser user, string itemId = null, NavigationContext navigationContext = null, CancellationToken cancellationToken = default)
        {
            return Task.FromResult(false);
        }
    }
}
 
And in the code of the Vue.js component we understand the selection of the Edit action:
 
    public startLocalAction(action: any) {
        switch (action.name) {
            case "Edit":
                this.edit();
                break;
        }
    }
 
SetRemoteAction Action 
The method adds an action that will be performed server-side, by a dedicated C# method.
Here, for example, we are setting up an email notification action and the change of status of a product:
 
using System;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using DataWeb.Structure;
using DataWeb.Identity;
using DataWeb.Data;
using DataWeb.Data.Controls;
using DataWeb.Web.App.Infrastructure;

namespace DataWeb.Web.App.DataWeb.Data.Controls
{
    public class Product_ActionListAreaName(Form form, IServiceProvider serviceProvider) :  ActionList(form, serviceProvider)
    {
        private readonly IStringLocalizer localizer = serviceProvider.GetService<IStringLocalizer>();
        private readonly ProductStore productStore = serviceProvider.GetService<ProductStore>();

        public override async Task InitAsync(CancellationToken cancellationToken = default)
        {
            await base.InitAsync(cancellationToken);

            AddActionSetRemoteAction("Notify status", "NotifyStatus", ContextActionType.Notification, isConfirmRequired: true, isSaveItemBeforeProcess: true, isReloadAfterProcess: true);

            AddActionSetRemoteAction("Set product status", "SetProductStatus", ContextActionType.Dialog, dialogFormName: "Form_ActionProductStatus", isConfirmRequired: true, isSaveItemBeforeProcess: true, isReloadAfterProcess: true);
        }

        public override async Task<ContextAction.Result> ProcessActionAsync(ContextAction action, IUser user, string itemId = null, object value = null, List<Form.ProvidedValue> controlValues = null, NavigationContext navigationContext = null, CancellationToken cancellationToken = default)
        {
            var result = await base.ProcessActionAsync(action, user, itemId, value, controlValues, navigationContext, cancellationToken);
            if (!result.IsValid)
            {
                return result;
            }

            switch (action.Name)
            {
                case "NotifyStatus":
                    result = await NotifyStatus(itemId);
                    break;
                case "SetProductStatus":
                    result = await NotifyStatus(itemId);
                    break;
            }

            return result;
        }

        public async Task<ContextAction.Result> NotifyStatus(string itemId = null)
        {
            await productStore.UpdateDescriptionAsync(itemId);

            var result = new ContextAction.Result { IsValid = true };
            return result;
        }
    }
}
 
When creating a server-side remote action, you can customize the execution flow with these parameters:
 
  • IsConfirmRequired: if active requires a confirmation via modal dialog before performing the action
  • IsSaveItemBeforeProcess: if active, saves the data of the current form before executing the action
  • DialogFormName: Allows you to specify the name of the form that appears in the modal dialog to collect additional values for the action
  • IsReloadAfterProcess: if enabled, reloads the data in the current form by updating its contents
  • IsDownloadResult: Instructs the browser to download the result of the action as a file (for example, when generating a report).