Как да създадете подменю в динамичен падащ списък с typescript и angular?

Ето как създадох динамичен падащ списък:

.html

<label> Move to </label>
    <select [(ngModel)] = "mSelectedCategoryNameMoveTo" 
            (click)     = "onMoveToSelected()" 
            [disabled]  = "mflagDisableMoveTo" >

        <option *ngFor = "let category of categories" [ngValue] = "category.name" >
            {{category.name}}
        </option>       

    </select>

Тук списъкът categories идва от .ts файл. Всички променливи и функции са дефинирани в съответния .ts файл.

Структурата category .ts е както следва:

export interface CategoryStructure
{
    id:          number
    name:        string
    description: string 
    blogIds:     number[]
}

Какъв би бил начинът да се създаде "подменю" тук?

Ето как изглежда едно подменю: въведете описание на изображението тук


person Aquarius_Girl    schedule 17.04.2020    source източник
comment
Можете ли също да кажете кой клавиш ще попълни вашето подменю?   -  person cse_vikashgupta    schedule 17.04.2020
comment
@Aquarius_Girl, ако щракнете върху елемент, ако елементът няма подменю, тогава какво се случва..?   -  person Kiran Mistry    schedule 17.04.2020
comment
Здравейте, въпросът дали този компонент е за целите на навигацията като на снимката или е контрола за формуляр за избор с подменюта, което означава, че искате да вземете избрания резултат   -  person Gabriel Guerrero    schedule 13.05.2020
comment
@GabrielGuerrero Точно както е показано на снимката.   -  person Aquarius_Girl    schedule 13.05.2020


Отговори (3)


Редактиране 2

Друга демонстрация на живо, свързана със стила на менюто:
https://stackblitz.com/edit/angular-ivy-zrzfuy


Редактиране

Демо на живо за реактивна форма: https://stackblitz.com/edit/angular-ivy-fhvvzs


Това е пример как се прави.

Файл .html

<select id="categoriesList" name="categoriesList" [ngModel]="categorySelected"
(ngModelChange)="setAnotherSelect(+$event)">
    <option value="-1"></option>
    <option *ngFor="let category of categories" value="{{ category.id }}">{{ category.name }}</option>
</select>

<select id="randomElementsList" name="randomElementsList" [ngModel]="randomElements" *ngIf="showRandomElements">
    <option value="-1"></option>
    <option *ngFor="let element of randomElements" value="{{ element.id }}">{{ element.name }}</option>
</select>

Файл model.ts

export interface ICategoryStructure
{
    id: number;
    name: string;
    description: string;
    blogIds: number[];
}

Файл component.ts

import { ICategoryStructure } from './myApp.model'

public categories: ICategoryStructure[] = [
    { id: 1, name: 'test1', description: 'description1', blogIds: [1, 2] },
    { id: 2, name: 'test2', description: 'description2', blogIds: [3, 4] },
    { id: 3, name: 'test3', description: 'description3', blogIds: [5, 6] },
  ];
  public categorySelected: number = -1;

  public randomElements: ICategoryStructure[] = [];
  public randomElementSelected: number = -1;
  public showRandomElements = false;

  public setAnotherSelect(numberId) {
    this.categorySelected = numberId;
    this.showRandomElements = true;
    this.randomElements = [];
    switch (numberId) {
        case 1:
            this.randomElements = [
              { id: 4, name: 'test4', description: 'description4', blogIds: [7, 8] },
              { id: 5, name: 'test5', description: 'description5', blogIds: [9, 10] },
              { id: 6, name: 'test6', description: 'description6', blogIds: [11, 12] },
            ];
            break;
        case 2:
            this.randomElements = [
              { id: 7, name: 'test7', description: 'description7', blogIds: [13, 14] },
              { id: 8, name: 'test8', description: 'description8', blogIds: [15, 16] },
              { id: 9, name: 'test9', description: 'description9', blogIds: [17, 18] },
            ];
            break;
        case 3:
            this.randomElements = [
              { id: 10, name: 'test10', description: 'description10', blogIds: [19, 20] },
              { id: 11, name: 'test11', description: 'description11', blogIds: [21, 22] },
              { id: 12, name: 'test12', description: 'description12', blogIds: [23, 24] },
            ];
            break;
        default:
            this.showRandomElements = false;
            break;
    }
  }

Демо на живо тук:
https://stackblitz.com/edit/angular-ivy-hzee7k

person Luciano    schedule 12.05.2020
comment
Освен това ще бъда благодарен, ако можете да добавите пример за същите реактивни форми. - person Aquarius_Girl; 12.05.2020
comment
@Aquarius_Girl С реактивната форма е доста подобно, но вижте новата демонстрация на живо - person Luciano; 12.05.2020
comment
Благодаря ти. Публикувах снимката на това как изглежда подменюто, само за да изясня въпроса. - person Aquarius_Girl; 12.05.2020
comment
@Aquarius_Girl Добре, вижте новата демонстрация на живо - person Luciano; 13.05.2020

Най-лесно би било да използвате силата на NgTemplateOutlet. Така че ще бъде нещо подобно, с този подход ще се създаде рекурсивно дъщерно дърво:

export interface CategoryStructure
{
    id:          number
    name:        string
    description: string 
    blogIds:     number[]
    children:    CategoryStructure[]
}

и го използвайте, както следва:

<ng-template #itemTemplate let-items>
    <li *ngFor="let item of items">
        <a>{{ item.name }}</a>
        <ul class="submenu" *ngIf="item?.children?.length > 0">
            <ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: item.children }"></ng-container>
        </ul>
    </li>
</ng-template>
<!-- Parent category rendering -->
<ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: categories }"></ng-container>

За да изобразите менюто, сякаш имате нещо като:

interface Item {
    name: string;
    icon?: string;
    route: string;
    children?: Item[];
}

// Component
public menu: Item[] = [
    {
        name: 'Parent 1',
        route: 'test',
        children: [
            {
                name: 'Child 1',
                route: 'test'
            },
            {
                name: 'Child 2',
                route: 'test'
            }
        ]
    },
    {
        name: 'Parent 2',
        route: 'test'
    },
]

// Template
<ul class="menu">
    <ng-template #menuRef let-items>
        <li *ngFor="let item of items">
            <a [routerLink]="item.route">
                <img [src]="item?.children?.length > 0 && !item.icon ? '/folder.png' : item.icon" />
                {{ item.name }}
            </a>
            <ul class="submenu" *ngIf="item?.children?.length > 0">
                <ng-container *ngTemplateOutlet="menuRef; context: { $implicit: item.children }"></ng-container>
            </ul>
        </li>
    </ng-template>
    <ng-container *ngTemplateOutlet="menuRef; context: { $implicit: menu }"></ng-container>
</ul>
person Timothy    schedule 12.05.2020
comment
Освен това ще бъда благодарен, ако можете да добавите пример за същите реактивни форми. - person Aquarius_Girl; 12.05.2020
comment
Частта за изобразяване на @Aquarius_Girl ще бъде идентична за реактивна форма и управлявана от шаблон форма. Различава се само обвързването на модела. За реактивен формуляр трябва ръчно да добавите / премахнете елементи от масив от формуляри. Вижте следния отговор stackoverflow.com/a/53297635/9590251 - person Timothy; 12.05.2020
comment
Публикувах снимката на това как изглежда подменюто, само за да изясня въпроса. - person Aquarius_Girl; 12.05.2020
comment
@Aquarius_Girl добави почти пълно решение. Просто ви трябва вложено дърво от менюта и това е всичко - person Timothy; 12.05.2020

Здравейте, създадох пример за този проблем. Опитвам се да го запазя възможно най-прост, мисля, че се справя с типа подменю, което искате, щракване върху > показва подменютата и щракване върху етикета на подменюто го избира

https://stackblitz.com/github/gabrielguerrero/stackoverflow-submenu?file=src%2Fapp%2Fcomponents%2Fmenu%2Fmenu.component.ts

Използвах cdk портал и шаблони, за да го изградя, основната логика е компонентът на менюто

По принцип създавам шаблон за елементите на подменюто, които показвам в cdk портал, когато щракнете върху стрелката

<app-menu-item
  [item]="item"
  (showSubMenu)="showSubmenu($event)"
></app-menu-item>

<ng-template let-items>
  <div class="sub-items">
    <app-menu-item
      *ngFor="let item of items"
      [item]="item"
      (click)="selected.emit(item)"
      (showSubMenu)="showSubmenu($event)"
    ></app-menu-item>
  </div>
</ng-template>

Това е кодът за показване на шаблона:

showSubmenu(event: SubItemsEvent) {
  const positionStrategy = this.overlay
    .position()
    .flexibleConnectedTo(event.element)
    .withPositions([
      {
        originX: 'end',
        originY: 'top',
        overlayX: 'start',
        overlayY: 'top',
      },
    ]);
  const overlayRef = this.overlay.create({
    positionStrategy,
    hasBackdrop: true,
  });
  this.subItemsOverlays.push(overlayRef);
  const portal = new TemplatePortal(this.template, this.viewContainerRef, {
    $implicit: event.item.subItems,
  });
  overlayRef.attach(portal);
}

В случай, че не знаете много за наслагването на cdk, тук е най-добрият документ, който успях да намеря за него https://netbasal.com/creating-powerful-components-with-angular-cdk-2cef53d81cea

Има много начини да приложите това, но аз се опитах да направя един много прост, който да покрие вашите нужди

person Gabriel Guerrero    schedule 15.05.2020