如何在 angular 中动态创建 n 级嵌套 expand/collapse 组件
How to create n level nested expand/collapse component dynamically in angular
我正在使用 div.
开发 n 级嵌套 table 的脚本
所以有 5 到 6 列,行数 n
,每一列的第一列都必须有 expand/collapse 按钮,点击它我会调用 API我的数据对应于 selected 行过滤器。
以前当我使用核心 JavaScript 和 jQuery 时,我使用 find
文档 select 的方法或识别 expand/collapse 的父级仅使用 jQuery
的 innerHTML
或 append
方法在特定 div 之后动态创建按钮和推送 HTML
我对 angular 有点陌生,工作不多。请帮我解决这个问题。
splitOpt
是一个对象数组,我将根据这些对象拆分报表数据。
this.splitOpt = [
{
id: "country",
label: "Country"
},
{
id:"os".
label:"Operating System"
},
{
id:"osv".
label:"Operating System Version"
}
]
获取报告的功能
getReport() {
// apiFilters are array of object having some values to filter report data
var apiFilters: any = [{}];
for (var i = 0; i < this.sFilters.length; i++) {
if (this.sFilters[i][0].values.length > 0) {
var k;
k = this.sFilters[i][0].id
apiFilters[0][k] = this.sFilters[i][0].values;
}
}
var split = this.splitOpt[0].id;
this._apis.getReportData(split, apiFilters[0]).subscribe(response => {
if (response.status == 1200) {
this.reportData = response.data.split_by_data;
}
})
}
检查是否有更多分割的函数
checkIfHaveMoreSplits(c){
if(this.splitOpt.length > 0) {
var index = this.splitOpt.findIndex(function(v) {
return v.id == c
})
if (typeof(this.splitOpt[index+1]) != "undefined"){
return this.splitOpt[index+1];
} else {
return 0;
}
}
}
根据拆分和报告数据绘制 table 的代码。
假设 splitopt
对象中的国家/地区只有一个对象而不是 checkIfHaveMoreSplits()
returns 0
这意味着我不必提供展开按钮,如果0
展开按钮不会出现在那里。
单击展开按钮后,我将 select 来自 splitopt
的下一个元素并调用 API 以获取将拆分参数作为载体等的报告。
<div class="table" >
<div class="row" *ngFor="let rData of reportData; let i = index;" >
<div class="col" >
<button
class="btn btn-sm"
*ngIf="checkIfHaveMoreSplits(splitbykey) !== 0"
(click)="splitData(splitbykey)"
>+</button>
{{rData[splitbykey]}}
</div>
<div class="col">{{rData.wins}}</div>
<div class="col">{{rData.conversions}}</div>
<div class="col">{{rData.cost}}</div>
<div class="col">{{rData.bids}}</div>
<div class="col">{{rData.impressions}}</div>
<div class="col">{{rData.rev_payout}}</div>
</div>
我正在管理一个数组,它标识我可以展开折叠元素的深度
让我们假设数组具有三个元素,即国家、运营商和 os
所以首先 table 我将绘制 table 中的所有国家,点击展开按钮我将发送 selected 国家并获得该特定的运营商国家。收到响应后,我想根据响应创建自定义 HTML 并在 selected 行之后附加 html。
这里是截图以及完整的工作流程:)
步骤 1
第 2 步
步骤 3
我不把解决方案放在这里,因为我不知道你的js代码是什么。但是您可能要考虑使用 ViewContainerRef 动态追加元素。希望这有帮助
我建议为每个要显示的动态 HTML 部分编写自定义 angular 组件。然后你可以编写一个循环组件,它将 *ngIf
你的嵌套组件基于你提供的类型列表。像这样:
// dynamic.component.ts
export type DynamicComponentType = 'country' | 'os' | 'osv';
export interface IOptions { /* whatever options you need for your components */ }
export type DynamicComponentOptions = { type: DynamicComponentType, options: IOptions};
@Component({
selector: 'app-dynamic',
template = `
<app-country *ngIf="current.type == 'country'" [options]="current.options" />
<app-os *ngIf="current.type == 'os'" [options]="current.options" />
<app-osv *ngIf="current.type == 'osv'" [options]="current.options" />
<ng-container *ngIf="!!subTypes">
<button (click)="dynamicSubComponentShow = !dynamicSubComponentShow" value="+" />
<app-dynamic *ngIf="dynamicSubComponentShow" [options]="subOptions" />
</ng-container>`,
// other config
})
export class DynamicComponent {
@Input() options: DynamicComponentOptions[];
get current(): DynamicComponentOptions {
return this.options && this.options.length && this.options[0];
}
get subOptions(): DynamicComponentOptions[] {
return this.options && this.options.length && this.options.slice(1);
}
dynamicSubComponentShow = false;
// component logic, other inputs, whatever else you need to pass on to the specific components
}
CountryComponent
的示例。其他组件看起来相似。
// country.component.ts
@Component({
selector: 'app-country',
template: `
<div>Country label</div>
<p>Any other HTML for the country component using the `data` observable i.e.</p>
<span>x: {{ (data$ | async)?.x }}</span>
<span>y: {{ (data$ | async)?.y }}</span>
`,
})
export class CountryComponent {
@Input() options: IOptions;
data$: Observable<{x: string, y: number}>;
constructor(private countryService: CountryService) {
// load data specific for this country based on the input options
// or use it directly if it already has all your data
this.data$ = countryService.getCountryData(this.options);
}
}
// my.component.ts
@Component({
template: `
<div class="table" >
<div class="row" *ngFor="let rData of reportData$ | async; let i = index;" >
<div class="col" >
<app-dynamic [options]="options$ | async"></app-dynamic>
</div>
...
</div>
</div>`,
// other cmp config
})
export class MyComponent {
options$: Observable<DynamicComponentOptions[]>;
reportData$: Observable<ReportData>;
constructor(private reportService: ReportService){
// simplified version of your filter calculation
let apiFilters: {} = this.sFilters
.map(f => f[0])
.filter(f => f && f.values && f.values.length)
.reduce((f, acc) => acc[f.id] = f.values && acc, {});
this.reportData$ = reportService.getReportData(this.splitOpt[0].id, apiFilters).pipe(
filter(r => r.status == 1200),
map(r => r.data.split_by_data)
);
this.options$ = this.reportData$.pipe(map(d => d.YOUR_OPTIONS));
}
}
现在让你的apireturn像
{
"status": 1200,
"data": {
"YOUR_OPTIONS": [{
"type": "country"
"options" { "id": 1, ... } // options for your country component initialization
}, {
"type": "os",
"options" { "id": 11, ... } // options for your os component initialization
}, ...],
// your other report data for the main grid
}
}
请根据您的具体需要进行调整。例如,您将必须通过组件层次结构管理状态传递(使用组件状态、可观察服务、MobX、NgRx - 选择你的毒药)。
希望对您有所帮助 :-)
我正在使用 div.
开发 n 级嵌套 table 的脚本所以有 5 到 6 列,行数 n
,每一列的第一列都必须有 expand/collapse 按钮,点击它我会调用 API我的数据对应于 selected 行过滤器。
以前当我使用核心 JavaScript 和 jQuery 时,我使用 find
文档 select 的方法或识别 expand/collapse 的父级仅使用 jQuery
innerHTML
或 append
方法在特定 div 之后动态创建按钮和推送 HTML
我对 angular 有点陌生,工作不多。请帮我解决这个问题。
splitOpt
是一个对象数组,我将根据这些对象拆分报表数据。
this.splitOpt = [
{
id: "country",
label: "Country"
},
{
id:"os".
label:"Operating System"
},
{
id:"osv".
label:"Operating System Version"
}
]
获取报告的功能
getReport() {
// apiFilters are array of object having some values to filter report data
var apiFilters: any = [{}];
for (var i = 0; i < this.sFilters.length; i++) {
if (this.sFilters[i][0].values.length > 0) {
var k;
k = this.sFilters[i][0].id
apiFilters[0][k] = this.sFilters[i][0].values;
}
}
var split = this.splitOpt[0].id;
this._apis.getReportData(split, apiFilters[0]).subscribe(response => {
if (response.status == 1200) {
this.reportData = response.data.split_by_data;
}
})
}
检查是否有更多分割的函数
checkIfHaveMoreSplits(c){
if(this.splitOpt.length > 0) {
var index = this.splitOpt.findIndex(function(v) {
return v.id == c
})
if (typeof(this.splitOpt[index+1]) != "undefined"){
return this.splitOpt[index+1];
} else {
return 0;
}
}
}
根据拆分和报告数据绘制 table 的代码。
假设 splitopt
对象中的国家/地区只有一个对象而不是 checkIfHaveMoreSplits()
returns 0
这意味着我不必提供展开按钮,如果0
展开按钮不会出现在那里。
单击展开按钮后,我将 select 来自 splitopt
的下一个元素并调用 API 以获取将拆分参数作为载体等的报告。
<div class="table" >
<div class="row" *ngFor="let rData of reportData; let i = index;" >
<div class="col" >
<button
class="btn btn-sm"
*ngIf="checkIfHaveMoreSplits(splitbykey) !== 0"
(click)="splitData(splitbykey)"
>+</button>
{{rData[splitbykey]}}
</div>
<div class="col">{{rData.wins}}</div>
<div class="col">{{rData.conversions}}</div>
<div class="col">{{rData.cost}}</div>
<div class="col">{{rData.bids}}</div>
<div class="col">{{rData.impressions}}</div>
<div class="col">{{rData.rev_payout}}</div>
</div>
我正在管理一个数组,它标识我可以展开折叠元素的深度
让我们假设数组具有三个元素,即国家、运营商和 os
所以首先 table 我将绘制 table 中的所有国家,点击展开按钮我将发送 selected 国家并获得该特定的运营商国家。收到响应后,我想根据响应创建自定义 HTML 并在 selected 行之后附加 html。
这里是截图以及完整的工作流程:)
步骤 1
第 2 步
步骤 3
我不把解决方案放在这里,因为我不知道你的js代码是什么。但是您可能要考虑使用 ViewContainerRef 动态追加元素。希望这有帮助
我建议为每个要显示的动态 HTML 部分编写自定义 angular 组件。然后你可以编写一个循环组件,它将 *ngIf
你的嵌套组件基于你提供的类型列表。像这样:
// dynamic.component.ts
export type DynamicComponentType = 'country' | 'os' | 'osv';
export interface IOptions { /* whatever options you need for your components */ }
export type DynamicComponentOptions = { type: DynamicComponentType, options: IOptions};
@Component({
selector: 'app-dynamic',
template = `
<app-country *ngIf="current.type == 'country'" [options]="current.options" />
<app-os *ngIf="current.type == 'os'" [options]="current.options" />
<app-osv *ngIf="current.type == 'osv'" [options]="current.options" />
<ng-container *ngIf="!!subTypes">
<button (click)="dynamicSubComponentShow = !dynamicSubComponentShow" value="+" />
<app-dynamic *ngIf="dynamicSubComponentShow" [options]="subOptions" />
</ng-container>`,
// other config
})
export class DynamicComponent {
@Input() options: DynamicComponentOptions[];
get current(): DynamicComponentOptions {
return this.options && this.options.length && this.options[0];
}
get subOptions(): DynamicComponentOptions[] {
return this.options && this.options.length && this.options.slice(1);
}
dynamicSubComponentShow = false;
// component logic, other inputs, whatever else you need to pass on to the specific components
}
CountryComponent
的示例。其他组件看起来相似。
// country.component.ts
@Component({
selector: 'app-country',
template: `
<div>Country label</div>
<p>Any other HTML for the country component using the `data` observable i.e.</p>
<span>x: {{ (data$ | async)?.x }}</span>
<span>y: {{ (data$ | async)?.y }}</span>
`,
})
export class CountryComponent {
@Input() options: IOptions;
data$: Observable<{x: string, y: number}>;
constructor(private countryService: CountryService) {
// load data specific for this country based on the input options
// or use it directly if it already has all your data
this.data$ = countryService.getCountryData(this.options);
}
}
// my.component.ts
@Component({
template: `
<div class="table" >
<div class="row" *ngFor="let rData of reportData$ | async; let i = index;" >
<div class="col" >
<app-dynamic [options]="options$ | async"></app-dynamic>
</div>
...
</div>
</div>`,
// other cmp config
})
export class MyComponent {
options$: Observable<DynamicComponentOptions[]>;
reportData$: Observable<ReportData>;
constructor(private reportService: ReportService){
// simplified version of your filter calculation
let apiFilters: {} = this.sFilters
.map(f => f[0])
.filter(f => f && f.values && f.values.length)
.reduce((f, acc) => acc[f.id] = f.values && acc, {});
this.reportData$ = reportService.getReportData(this.splitOpt[0].id, apiFilters).pipe(
filter(r => r.status == 1200),
map(r => r.data.split_by_data)
);
this.options$ = this.reportData$.pipe(map(d => d.YOUR_OPTIONS));
}
}
现在让你的apireturn像
{
"status": 1200,
"data": {
"YOUR_OPTIONS": [{
"type": "country"
"options" { "id": 1, ... } // options for your country component initialization
}, {
"type": "os",
"options" { "id": 11, ... } // options for your os component initialization
}, ...],
// your other report data for the main grid
}
}
请根据您的具体需要进行调整。例如,您将必须通过组件层次结构管理状态传递(使用组件状态、可观察服务、MobX、NgRx - 选择你的毒药)。
希望对您有所帮助 :-)