Open Closed

Feature modal do not follow the hierarchy in Angular Ui #8970


User avatar
0
cangunaydin created
  • ABP Framework version: v9.1
  • UI Type: Angular
  • Database System: EF Core (PostgreSQL)
  • Tiered (for MVC) or Auth Server Separated (for Angular): no (but does not matter)

When you have a feature in 3 layers structure like

* Signage
    * Playlist
        * ShareOfVoice

in angular ui you can not see the branches in this order. it is always 2 layers. here is the sample code.

public class BookStoreFeatureDefinitionProvider : FeatureDefinitionProvider
{
    public override void Define(IFeatureDefinitionContext context)
    {
        var group = context.AddGroup(BookStoreFeatures.GroupName,
        L("Feature:BookStore"));


     

        var signageFeature = group.AddFeature(BookStoreFeatures.Signage.Default,
            "false",
            L("Feature:BookStoreSignage"),
            L("Feature:BookStoreSignageDescription"),
            new ToggleStringValueType());

        var playlistFeature = signageFeature.CreateChild(BookStoreFeatures.Signage.Playlist.Default,
            "false",
            L("Feature:BookStorePlaylist"),
            L("Feature:BookStorePlaylistDescription"),
            new ToggleStringValueType());
        var playlistShareOfVoiceFeature = playlistFeature.CreateChild(BookStoreFeatures.Signage.Playlist.ShareOfVoice,
            "false",
            L("Feature:BookStorePlaylistShareOfVoice"),
            L("Feature:BookStorePlaylistShareOfVoiceDescription"),
            new ToggleStringValueType());

    }
    private static LocalizableString L(string name)
    {
        return LocalizableString.Create<BookStoreResource>(name);
    }
}

here is the output for it.


3 Answer(s)
  • User Avatar
    0
    sumeyye.kurtulus created
    Support Team Angular Expert

    Hello, thank you for reporting this, and it will be fixed within the next release. I am refunding your ticket as well.

  • User Avatar
    0
    cangunaydin created

    Is there a way that I can fix it in my version ? Can you give step by step instructions

  • User Avatar
    0
    sumeyye.kurtulus created
    Support Team Angular Expert

    Yes, you can use this workaround until we release the solution:

    1. Patch the feature management setting tab.
    //app.component.ts
    import { SettingTabsService } from '@abp/ng.setting-management/config';
    import { NewFeatureManagementTabComponent } from './new-feature-management-tab/new-feature-management-tab.component';
    
    @Component(...)
    export class AppComponent {
      protected settingTabs = inject(SettingTabsService);
    
      constructor() {
        this.settingTabs.patch('AbpFeatureManagement::Permission:FeatureManagement', {
          name: 'New Feature Management',
          order: 100,
          requiredPolicy: 'FeatureManagement.ManageHostFeatures',
          component: NewFeatureManagementTabComponent,
        });
      }
    }
    
    1. Implement NewFeatureManagementTabComponent as follows:
    // new-feature-management-tab.component.ts
    import { LocalizationModule } from '@abp/ng.core';
    import { Component } from '@angular/core';
    import { NewFeatureManagementComponent } from '../new-feature-management/new-feature-management.component';
    import { FeatureManagementTabComponent } from '@abp/ng.feature-management';
    
    @Component({
      selector: 'app-new-feature-management-tab',
      imports: [LocalizationModule, NewFeatureManagementComponent],
      template: `
        &lt;p class=&quot;pt-2 text-wrap&quot;&gt;
          {{ 'AbpFeatureManagement::ManageHostFeaturesText' | abpLocalization }}
        &lt;/p&gt;
    
        &lt;button class=&quot;btn btn-primary&quot; type=&quot;button&quot; (click)=&quot;openFeaturesModal()&quot;&gt;
          &lt;i class=&quot;me-1 fa fa-cog&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt;
          {{ 'AbpFeatureManagement::ManageHostFeatures' | abpLocalization }}
        &lt;/button&gt;
        @if (visibleFeatures) {
        &lt;app-new-feature-management
          [(visible)]=&quot;visibleFeatures&quot;
          providerName=&quot;T&quot;
          [providerKey]=&quot;providerKey&quot;
        /&gt;
        }
      `,
      styleUrl: './new-feature-management-tab.component.scss',
    })
    export class NewFeatureManagementTabComponent extends FeatureManagementTabComponent {}
    
    1. Add NewFeatureManagementComponent.
    // new-feature-management.component.ts
    import { LocalizationModule } from '@abp/ng.core';
    import { ThemeSharedModule } from '@abp/ng.theme.shared';
    import { Component } from '@angular/core';
    import { CommonModule, NgTemplateOutlet } from '@angular/common';
    import { FormsModule } from '@angular/forms';
    import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
    import { FeatureManagementComponent, FreeTextInputDirective } from '@abp/ng.feature-management';
    
    @Component({
      selector: 'app-new-feature-management',
      imports: [
        CommonModule,
        ThemeSharedModule,
        LocalizationModule,
        FormsModule,
        NgbNavModule,
        FreeTextInputDirective,
        NgTemplateOutlet,
      ],
      template: `
        @if (visible) {
        &lt;abp-modal [(visible)]=&quot;visible&quot; [busy]=&quot;modalBusy&quot; [options]=&quot;{ size: &#39;lg&#39; }&quot;&gt;
          &lt;ng-template #abpHeader&gt;
            &lt;h3&gt;
              {{ 'AbpFeatureManagement::Features' | abpLocalization }}
              @if (providerTitle) { - {{ providerTitle }}
              }
            &lt;/h3&gt;
          &lt;/ng-template&gt;
    
          &lt;ng-template #abpBody&gt;
            &lt;div class=&quot;row&quot;&gt;
              @if (groups.length) {
              &lt;div class=&quot;col-md-4&quot;&gt;
                &lt;ul
                  ngbNav
                  #nav=&quot;ngbNav&quot;
                  [(activeId)]=&quot;selectedGroupDisplayName&quot;
                  class=&quot;nav-pills&quot;
                  orientation=&quot;vertical&quot;
                &gt;
                  @for (group of groups; track group.name) {
                  &lt;li [ngbNavItem]=&quot;group.displayName&quot;&gt;
                    &lt;a ngbNavLink&gt;{{ group.displayName }}&lt;/a&gt;
                    &lt;ng-template ngbNavContent&gt;
                      &lt;h4&gt;{{ selectedGroupDisplayName }}&lt;/h4&gt;
                      &lt;hr class=&quot;mt-2 mb-3&quot; /&gt;
    
                      @for (feature of features[group.name]; track feature.id || i; let i = $index) {
                      &lt;div class=&quot;mt-2&quot; [ngStyle]=&quot;feature.style&quot; (keyup.enter)=&quot;save()&quot;&gt;
                        @switch (feature.valueType?.name) { @case (valueTypes.ToggleStringValueType) {
                        &lt;div class=&quot;form-check&quot; [class.px-4]=&quot;!!feature.parentName&quot;&gt;
                          &lt;input
                            class=&quot;form-check-input&quot;
                            type=&quot;checkbox&quot;
                            [id]=&quot;feature.name&quot;
                            [(ngModel)]=&quot;feature.value&quot;
                            (ngModelChange)=&quot;onCheckboxClick($event, feature)&quot;
                          /&gt;
    
                          &lt;label class=&quot;form-check-label&quot; [htmlFor]=&quot;feature.name&quot;&gt;{{
                            feature.displayName
                          }}&lt;/label&gt;
                          &lt;ng-container
                            *ngTemplateOutlet=&quot;descTmp; context: { $implicit: feature.description }&quot;
                          &gt;&lt;/ng-container&gt;
                        &lt;/div&gt;
                        } @case (valueTypes.FreeTextStringValueType) {
                        &lt;div class=&quot;mb-3 form-group&quot; [class.px-2]=&quot;!!feature.parentName&quot;&gt;
                          &lt;label [htmlFor]=&quot;feature.name&quot; class=&quot;form-label&quot;&gt;{{
                            feature.displayName
                          }}&lt;/label&gt;
                          &lt;input
                            class=&quot;form-control&quot;
                            type=&quot;text&quot;
                            [id]=&quot;feature.name&quot;
                            [(ngModel)]=&quot;feature.value&quot;
                            [abpFeatureManagementFreeText]=&quot;feature&quot;
                          /&gt;
    
                          &lt;ng-container
                            *ngTemplateOutlet=&quot;descTmp; context: { $implicit: feature.description }&quot;
                          &gt;&lt;/ng-container&gt;
                        &lt;/div&gt;
                        } @case (valueTypes.SelectionStringValueType) { @if
                        (feature.valueType.itemSource?.items?.length) {
                        &lt;div class=&quot;mb-3 form-group&quot; [class.px-2]=&quot;!!feature.parentName&quot;&gt;
                          &lt;label [htmlFor]=&quot;feature.name&quot; class=&quot;form-label&quot;&gt;{{
                            feature.displayName
                          }}&lt;/label&gt;
                          &lt;select class=&quot;form-select&quot; [id]=&quot;feature.name&quot; [(ngModel)]=&quot;feature.value&quot;&gt;
                            @for ( item of feature.valueType.itemSource?.items; track item.value ) {
                            &lt;option [ngValue]=&quot;item.value&quot;&gt;
                              {{
                                item.displayText?.resourceName + '::' + item.displayText?.name
                                  | abpLocalization
                              }}
                            &lt;/option&gt;
                            }
                          &lt;/select&gt;
                          &lt;ng-container
                            *ngTemplateOutlet=&quot;descTmp; context: { $implicit: feature.description }&quot;
                          &gt;&lt;/ng-container&gt;
                        &lt;/div&gt;
                        } } @default {
                        {{ feature.displayName }}
                        } }
                      &lt;/div&gt;
                      }
                    &lt;/ng-template&gt;
                  &lt;/li&gt;
                  }
                &lt;/ul&gt;
              &lt;/div&gt;
    
              &lt;ng-template #descTmp let-description&gt;
                @if (description) {
                &lt;small class=&quot;d-block form-text text-muted&quot;&gt;{{ description }}&lt;/small&gt;
                }
              &lt;/ng-template&gt;
    
              &lt;div class=&quot;col-md-8&quot;&gt;&lt;div class=&quot;py-0&quot; [ngbNavOutlet]=&quot;nav&quot;&gt;&lt;/div&gt;&lt;/div&gt;
              } @if (!groups.length) {
              &lt;div class=&quot;col&quot;&gt;
                {{ 'AbpFeatureManagement::NoFeatureFoundMessage' | abpLocalization }}
              &lt;/div&gt;
              }
            &lt;/div&gt;
          &lt;/ng-template&gt;
    
          &lt;ng-template #abpFooter&gt;
            &lt;button abpClose type=&quot;button&quot; class=&quot;btn btn-link&quot;&gt;
              {{ 'AbpFeatureManagement::Cancel' | abpLocalization }}
            &lt;/button&gt;
    
            @if (groups.length) {
            &lt;abp-button
              buttonClass=&quot;btn btn-outline-primary&quot;
              [disabled]=&quot;modalBusy&quot;
              (click)=&quot;resetToDefault()&quot;
              aria-hidden=&quot;true&quot;
            &gt;
              {{ 'AbpFeatureManagement::ResetToDefault' | abpLocalization }}
            &lt;/abp-button&gt;
            } @if (groups.length) {
            &lt;abp-button
              iconClass=&quot;fa fa-check&quot;
              [disabled]=&quot;modalBusy&quot;
              (click)=&quot;save()&quot;
              aria-hidden=&quot;true&quot;
            &gt;
              {{ 'AbpFeatureManagement::Save' | abpLocalization }}
            &lt;/abp-button&gt;
            }
          &lt;/ng-template&gt;
        &lt;/abp-modal&gt;
        }
      `,
      styleUrl: './new-feature-management.component.scss',
    })
    export class NewFeatureManagementComponent extends FeatureManagementComponent {}
    
    
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
Do you need assistance from an ABP expert?
Schedule a Meeting
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v9.3.0-preview. Updated on April 16, 2025, 12:13