Ответ 1
Я бы предложил написать пользовательский угловой компонент для каждой из ваших динамических HTML-фрагментов, которые вы хотите отобразить. Затем вы можете написать повторяющийся компонент, который будет *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));
}
}
Теперь сделайте свой api возвратите что-то вроде
{
"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 - выберите ваш яд).
Надеюсь, это немного поможет :-)