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(name);
    }
}

here is the output for it.

image.png


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: `
        <p class="pt-2 text-wrap">
          {{ 'AbpFeatureManagement::ManageHostFeaturesText' | abpLocalization }}
        </p>
    
        <button class="btn btn-primary" type="button" (click)="openFeaturesModal()">
          <i class="me-1 fa fa-cog" aria-hidden="true"></i>
          {{ 'AbpFeatureManagement::ManageHostFeatures' | abpLocalization }}
        </button>
        @if (visibleFeatures) {
        <app-new-feature-management
          [(visible)]="visibleFeatures"
          providerName="T"
          [providerKey]="providerKey"
        />
        }
      `,
      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) {
        <abp-modal [(visible)]="visible" [busy]="modalBusy" [options]="{ size: 'lg' }">
          <ng-template #abpHeader>
            <h3>
              {{ 'AbpFeatureManagement::Features' | abpLocalization }}
              @if (providerTitle) { - {{ providerTitle }}
              }
            </h3>
          </ng-template>
    
          <ng-template #abpBody>
            <div class="row">
              @if (groups.length) {
              <div class="col-md-4">
                <ul
                  ngbNav
                  #nav="ngbNav"
                  [(activeId)]="selectedGroupDisplayName"
                  class="nav-pills"
                  orientation="vertical"
                >
                  @for (group of groups; track group.name) {
                  <li [ngbNavItem]="group.displayName">
                    <a ngbNavLink>{{ group.displayName }}</a>
                    <ng-template ngbNavContent>
                      <h4>{{ selectedGroupDisplayName }}</h4>
                      <hr class="mt-2 mb-3" />
    
                      @for (feature of features[group.name]; track feature.id || i; let i = $index) {
                      <div class="mt-2" [ngStyle]="feature.style" (keyup.enter)="save()">
                        @switch (feature.valueType?.name) { @case (valueTypes.ToggleStringValueType) {
                        <div class="form-check" [class.px-4]="!!feature.parentName">
                          <input
                            class="form-check-input"
                            type="checkbox"
                            [id]="feature.name"
                            [(ngModel)]="feature.value"
                            (ngModelChange)="onCheckboxClick($event, feature)"
                          />
    
                          <label class="form-check-label" [htmlFor]="feature.name">{{
                            feature.displayName
                          }}</label>
                          <ng-container
                            *ngTemplateOutlet="descTmp; context: { $implicit: feature.description }"
                          ></ng-container>
                        </div>
                        } @case (valueTypes.FreeTextStringValueType) {
                        <div class="mb-3 form-group" [class.px-2]="!!feature.parentName">
                          <label [htmlFor]="feature.name" class="form-label">{{
                            feature.displayName
                          }}</label>
                          <input
                            class="form-control"
                            type="text"
                            [id]="feature.name"
                            [(ngModel)]="feature.value"
                            [abpFeatureManagementFreeText]="feature"
                          />
    
                          <ng-container
                            *ngTemplateOutlet="descTmp; context: { $implicit: feature.description }"
                          ></ng-container>
                        </div>
                        } @case (valueTypes.SelectionStringValueType) { @if
                        (feature.valueType.itemSource?.items?.length) {
                        <div class="mb-3 form-group" [class.px-2]="!!feature.parentName">
                          <label [htmlFor]="feature.name" class="form-label">{{
                            feature.displayName
                          }}</label>
                          <select class="form-select" [id]="feature.name" [(ngModel)]="feature.value">
                            @for ( item of feature.valueType.itemSource?.items; track item.value ) {
                            <option [ngValue]="item.value">
                              {{
                                item.displayText?.resourceName + '::' + item.displayText?.name
                                  | abpLocalization
                              }}
                            </option>
                            }
                          </select>
                          <ng-container
                            *ngTemplateOutlet="descTmp; context: { $implicit: feature.description }"
                          ></ng-container>
                        </div>
                        } } @default {
                        {{ feature.displayName }}
                        } }
                      </div>
                      }
                    </ng-template>
                  </li>
                  }
                </ul>
              </div>
    
              <ng-template #descTmp let-description>
                @if (description) {
                <small class="d-block form-text text-muted">{{ description }}</small>
                }
              </ng-template>
    
              <div class="col-md-8"><div class="py-0" [ngbNavOutlet]="nav"></div></div>
              } @if (!groups.length) {
              <div class="col">
                {{ 'AbpFeatureManagement::NoFeatureFoundMessage' | abpLocalization }}
              </div>
              }
            </div>
          </ng-template>
    
          <ng-template #abpFooter>
            <button abpClose type="button" class="btn btn-link">
              {{ 'AbpFeatureManagement::Cancel' | abpLocalization }}
            </button>
    
            @if (groups.length) {
            <abp-button
              buttonClass="btn btn-outline-primary"
              [disabled]="modalBusy"
              (click)="resetToDefault()"
              aria-hidden="true"
            >
              {{ 'AbpFeatureManagement::ResetToDefault' | abpLocalization }}
            </abp-button>
            } @if (groups.length) {
            <abp-button
              iconClass="fa fa-check"
              [disabled]="modalBusy"
              (click)="save()"
              aria-hidden="true"
            >
              {{ 'AbpFeatureManagement::Save' | abpLocalization }}
            </abp-button>
            }
          </ng-template>
        </abp-modal>
        }
      `,
      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 11, 2025, 10:10