Open Closed

Extending app service/controller(Organization Unit) by adding new method #496


User avatar
0
khalid created

I have been following the guide on how to customize the app modules with the intention of extending module entities with new properties and exposing these with a modified service/controller .

I have extended OrganizationUnit from a module with the following properties: and then mapped these new properties to the same database table as OrganizationUnit by updating DbContext.OnModelCreating and EfCoreEntityExtensionMappings and and Dto Extensions and  created a database-migration

And here is user interface for inserting new organization unit and its work fine

Summary To summarize, I wish to achieve the following:

How do I add new method from applications services and controllers in a way that enables me to get list of UnitCategory as dropdown list? I have implemented the following based on the solution #438 :

 public interface IAppOrganizationUnitAppService : IOrganizationUnitAppService
    {
        Task<PagedResultDto<LookupDto>> GetUnitCategoryLookupAsync(LookupRequestDto input);

    }

 [RemoteService(IsEnabled = false)] // If you use dynamic controller feature you can disable remote service. Prevent creating duplicate controller for the application service.
    [Dependency(ReplaceServices = true)]
    [ExposeServices( typeof(OrganizationUnitAppService),typeof(IAppOrganizationUnitAppService))]
    public class AppOrganizationUnitAppService : OrganizationUnitAppService, IAppOrganizationUnitAppService
    {
        private readonly IMODRepository<OrganizationUnitCategory, Guid> _unitCategoryRepository;

        public AppOrganizationUnitAppService(
            OrganizationUnitManager organizationUnitManager,
            IdentityUserManager userManager,
            IOrganizationUnitRepository organizationUnitRepository,
            IIdentityUserRepository identityUserRepository,
            IIdentityRoleRepository identityRoleRepository,
            IMODRepository<OrganizationUnitCategory, Guid> unitCategoryRepository)
        :base(organizationUnitManager,userManager, organizationUnitRepository, identityUserRepository, identityRoleRepository)
        {
            _unitCategoryRepository = unitCategoryRepository;
        }

        public  async Task<PagedResultDto<LookupDto>> GetUnitCategoryLookupAsync(LookupRequestDto input)
        {
            var query = _unitCategoryRepository.AsQueryable()
                .WhereIf(!string.IsNullOrWhiteSpace(input.Filter), x => x.ArabicName != null && x.ArabicName.Contains(input.Filter));

            var lookupData = await query.PageBy(input.SkipCount, input.MaxResultCount).ToDynamicListAsync<OrganizationUnitCategory>();

            var totalCount = query.Count();

            return new PagedResultDto<LookupDto>
            {
                TotalCount = totalCount,
                Items = ObjectMapper.Map<List<OrganizationUnitCategory>, List<LookupDto>>(lookupData)
            };
        }
    }

 [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(OrganizationUnitController), typeof(IAppOrganizationUnitAppService))]
    [RemoteService(true, Name = "AbpIdentity")]
    [Route("api/identity/organization-units", Order = 0)]
    [ControllerName("OrganizationUnit")]
    [Area("identity")]
    public class AppOrganizationUnitController : OrganizationUnitController, IAppOrganizationUnitAppService
    {
        public AppOrganizationUnitController(IOrganizationUnitAppService organizationUnitAppService)
            : base(organizationUnitAppService)
        {

        }

        [HttpGet]
        [Route("Unit-Category-lookup")]
        public Task<PagedResultDto<LookupDto>> GetUnitCategoryLookupAsync(LookupRequestDto input)
        {
            return ((IAppOrganizationUnitAppService)OrganizationUnitAppService).GetUnitCategoryLookupAsync(input);
        }


    }

However when I attempt to start the HttpApi.Host project I now get the following exception:

  • ABP Framework version: v3.2.0
  • UI type: Angular
  • Tiered (MVC) or Identity Server Seperated (Angular)Identity Server Seperated (Angular)
  • Exception message and stack trace:
  • Steps to reproduce the issue:

7 Answer(s)
  • User Avatar
    0
    alper created
    Support Team Director

    hi

    In the current version, there's automatic way of making a combobox for navigation property. But it's in our roadmap.

    AppOrganizationUnitController has not been registered this exception is weird! because AppOrganizationUnitController is derived from OrganizationUnitController which is derived from AbpController. so it must be already registered to the DI system.

    public class OrganizationUnitController : AbpController, IOrganizationUnitAppService, IApplicationService, IRemoteService
    

    did you create the controller in *.HttpApi

  • User Avatar
    0
    khalid created

    Yes I created the controller in *.HttpApi

  • User Avatar
    0
    alper created
    Support Team Director

    I guess you don't need to put GetUnitCategoryLookupAsync into AppOrganizationUnitController. You can create a new controller. Can you try it.

  • User Avatar
    0
    khalid created

    Hi, I have created this controller only to inlcude GetUnitCategoryLookupAsync and I need it to be appear under the same path in swagger. If I put it under a new controller, it will create it under a new path ,and I guess combobox won't appear in UI !!

    I am really stuck with this issue. I would be appreciative if you could help me with it.

  • User Avatar
    0
    alper created
    Support Team Director

    there won't be a combobox even if you implement this. As I mentioned, this feature will come in the next releases. (4.1) what you can do is; you can replace the New Organisation razor page.

    This document explains how to customize UI => https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface


    If you create your own view in the Pages/Identity/OrganizationUnits directory (as embedded resource) it'll overwrite the existing page

    Create CreateModal.cshtml file in your web project under Pages/Identity/OrganizationUnits/CreateModal.cshtml

    Add the below code to your CreateModal.cshtml and make your customizations

    CreateModal.cshtml

    @page
    @using Microsoft.AspNetCore.Mvc.Localization
    @using Microsoft.Extensions.Localization
    @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
    @using Volo.Abp.Identity.Localization
    @using Volo.Abp.Identity.Web.Pages.Identity.OrganizationUnits
    @using Volo.Abp.Localization
    @using Volo.Abp.ObjectExtending
    @model CreateModalModel
    @inject IHtmlLocalizer<IdentityResource> L
    @inject IStringLocalizerFactory StringLocalizerFactory
    @{
        Layout = null;
    }
    <form asp-page="/Identity/OrganizationUnits/CreateModal" autocomplete="off">
        <abp-modal>
            <abp-modal-header title="@L["NewOrganizationUnit"].Value"></abp-modal-header>
            <abp-modal-body>
                <abp-input asp-for="OrganizationUnit.ParentId"/>
                <abp-input asp-for="OrganizationUnit.DisplayName"/>
                @foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties<CreateModalModel.OrganizationUnitInfoModel>())
                {
                    if (propertyInfo.Type.IsEnum)
                    {
                        <abp-select asp-for="OrganizationUnit.ExtraProperties[propertyInfo.Name]"
                                    label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)">
                        </abp-select>
                    }
                    else
                    {
                        <abp-input type="@propertyInfo.GetInputType()"
                                   asp-for="OrganizationUnit.ExtraProperties[propertyInfo.Name]"
                                   label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
                                   asp-format="@propertyInfo.GetInputFormatOrNull()"
                                   value="@propertyInfo.GetInputValueOrNull(Model.OrganizationUnit.ExtraProperties[propertyInfo.Name])"/>
                    }
                }
            </abp-modal-body>
            <abp-modal-footer buttons="@(AbpModalButtons.Cancel | AbpModalButtons.Save)"></abp-modal-footer>
        </abp-modal>
    </form>
    

    CreateModal.cshtml.cs

    public class CreateModalModel : IdentityPageModel
        {
            [BindProperty]
            public OrganizationUnitInfoModel OrganizationUnit { get; set; }
    
            protected IOrganizationUnitAppService OrganizationUnitAppService { get; }
    
            public CreateModalModel(IOrganizationUnitAppService organizationUnitAppService)
            {
                OrganizationUnitAppService = organizationUnitAppService;
                OrganizationUnit = new OrganizationUnitInfoModel();
            }
    
            public virtual Task OnGetAsync(Guid? parentId)
            {
                OrganizationUnit.ParentId = parentId;
                return Task.CompletedTask;
            }
    
            public virtual async Task<IActionResult> OnPostAsync()
            {
                ValidateModel();
    
                var input = ObjectMapper.Map<OrganizationUnitInfoModel, OrganizationUnitCreateDto>(OrganizationUnit);
                await OrganizationUnitAppService.CreateAsync(input);
    
                return NoContent();
            }
    
            public class OrganizationUnitInfoModel : ExtensibleObject
            {
                [HiddenInput]
                public Guid? ParentId { get; set; }
                
                [Required]
                public string DisplayName { get; set; }
            }
        }
    
  • User Avatar
    0
    alkaabi created

    Hi,

    Unfortunately, We are not using Razor UI for our project. We are using angular. I dont think it will be an easy task to replace organization unit component using the Replaceable Component Api. Is it?

  • User Avatar
    0
    alper created
    Support Team Director

    If you are using Angular then check out https://docs.abp.io/en/commercial/latest/ui/angular/dynamic-form-extensions

Made with ❤️ on ABP v9.1.0-preview. Updated on December 05, 2024, 12:19