Control actions

One or more actions can be added to each control. The actions are displayed in the dedicated menu on the right side of the control:
 
Actions example
 
These are the types of available actions:
 
  • UpdateData: control data update action
  • SetNavigation: navigation action to another section or item
  • SetLocalAction: action for executing custom server-side code
  • SetRemoteAction: action for executing custom client-side code
 
The ControlBase class provides several methods to quickly add a new action to the Actions list of the control.
This is an example of the implementation of these methods:
 
        public void AddActionSetNavigation(string title, Action.Step currentStep, List<Action.Step> newSteps)
        {
            this.Actions.Add(new Action()
            {
                Mode = ActionMode.SetNavigation,
                Title = title,
                CurrentStep = currentStep,
                NewSteps = newSteps != null ? newSteps : new List<Action.Step>(),
                Type = ActionType.Navigation
            });
        }
 
UpdateData Action
The method adds a data update action to the control, allowing for data to be refreshed within the control without having to reload the page if the data source has been modified after the form was opened. 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 default controls that handle lists of values (CheckBoxList, ListBox, RadioButtonList, DropDownList, etc.) already have this action set.
 
SetNavigation Action
The method adds an action that allows the user to quickly open a section or item. For example, given a DropDownList with a list of categories, you may want to allow the user to add a new category, edit the selected one, or open the dedicated section.
DataWeb will automatically save the item's data before setting the navigation. The navigation is added to the existing one, so when the user closes the category, they are returned to the starting item, enhancing the user experience.
 
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 should be provided with the title of the action (localized in the example using Localizer) and the new navigation steps. The optional parameter currentStep allows moving to an adjacent section to the current one.
 
SetLocalAction Action
The method adds an action that allows the user to execute client-side code located in the custom Vue.js component.
For example, in the control User_AccountNewsletter, we find the addition of the local (client) action Edit:
 
using System;
using System.Collections.Generic;
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 : ControlBase
    {
        private readonly IStringLocalizer localizer;

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

        public override Task InitAsync()
        {
            AddActionSetLocalAction(localizer["DataWeb.Form.AccountNewsletter_Edit"], "Edit", ActionType.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)
        {
            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)
        {
            return Task.FromResult(false);
        }
    }
}
 
And in the code of the Vue.js component, we intercept the selection of the Edit action:
 
    public startLocalAction(action: any) {
        switch (action.name) {
            case "Edit":
                this.edit();
                break;
        }
    }
 
Action SetRemoteAction
The method adds an action that will be executed on the server side, using a dedicated C# method.
Here, for example, we are setting up an action for email notification:
 
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
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 MyApp.DataWeb.Data.Controls
{
    public class Product_ActionListAreaName :  ActionList
    {
        private readonly IStringLocalizer localizer;
        private readonly ProductStore productStore;

        public Product_ActionListAreaName(Form form, IServiceProvider serviceProvider) : base(form, serviceProvider)
        {
            localizer = serviceProvider.GetService<IStringLocalizer>();
            productStore = serviceProvider.GetService<ProductStore>();
        }

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

            AddActionSetRemoteAction("Notify status", "NotifyStatus", ActionType.Notification);
        }

        public override async Task<ActionResult> SetCustomActionAsync(Action action, IUser user, string itemId = null, NavigationContext navigationContext = null)
        {
            var result = await base.SetCustomActionAsync(action, user, itemId, navigationContext);
            if (!result.IsValid)
            {
                return result;
            }

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

            return result;
        }

        public async Task<ActionResult> NotifyStatus(string itemId = null)
        {
            bool isSuccess = await productStore.SendEmailAsync(itemId);

            return new ActionResult { IsValid = isSuccess };
        }
    }
}
 
It should be noted that executing a remote action always updates the form by reloading the data. Additionally, before performing the action, the data is validated and saved.