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.

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

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.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    cangunaydin created

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

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • 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 {}
    
    
    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.