如何使用打字稿和 angular 在动态下拉列表中创建 "submenu"?
How to create a "submenu" in a dynamic dropdown list with typescript and 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[]
}
在这里创建 "submenu" 的方法是什么?
这是子菜单的样子:
编辑 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;
}
}
最简单的方法是使用 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>
您好,我为这个问题创建了一个示例,我尽量保持简单,我认为它可以处理您想要的那种子菜单,单击 > 显示子菜单,然后单击子菜单选择它
我是用cdk portal和templates搭建的,主要逻辑是菜单组件
我基本上为子菜单项创建了一个模板,当您单击箭头时我会在 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 overlay 了解不多,这是我能找到的最好的文档
https://netbasal.com/creating-powerful-components-with-angular-cdk-2cef53d81cea
有很多方法可以实现这一点,但我尝试制作一个非常简单的方法来满足您的需求
这就是我创建动态下拉列表的方式:
.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[]
}
在这里创建 "submenu" 的方法是什么?
这是子菜单的样子:
编辑 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;
}
}
最简单的方法是使用 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>
您好,我为这个问题创建了一个示例,我尽量保持简单,我认为它可以处理您想要的那种子菜单,单击 > 显示子菜单,然后单击子菜单选择它
我是用cdk portal和templates搭建的,主要逻辑是菜单组件
我基本上为子菜单项创建了一个模板,当您单击箭头时我会在 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 overlay 了解不多,这是我能找到的最好的文档 https://netbasal.com/creating-powerful-components-with-angular-cdk-2cef53d81cea
有很多方法可以实现这一点,但我尝试制作一个非常简单的方法来满足您的需求