如何以角度动态创建n级嵌套的展开/折叠组件

时间:2018-09-19 12:32:55

标签: javascript html angular angular5

我正在使用div开发n级嵌套表的脚本。

因此,共有5到6列,行数为n,每列的第一列都必须具有展开/折叠按钮,单击该按钮,我将调用API,该API会向我提供与选定行过滤器有关的数据。

以前,当我使用核心JavaScript和jQuery时,我使用文档选择器的find方法来标识扩展/折叠按钮的父级,并仅使用innerHTML在该特定div之后推送动态创建的HTML或jQuery的append方法

我对棱角有些陌生,并且工作不多。请帮我解决这个问题。

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;
       }
   }

    }

基于拆分和报告数据绘制表格的代码。

让我们假设splitopt对象中的国家/地区只有一个对象,而不是checkIfHaveMoreSplits()返回0,这意味着我不必提供扩展按钮,如果不是{{1 }},展开按钮就会出现在这里。

点击扩展按钮后,我将从0中选择下一个元素,并调用API以获取具有拆分参数作为载体的报告,依此类推。

splitopt

我正在管理一个数组,该数组确定我可以展开折叠元素的深度

让我们假设数组具有三个元素,即国家/地区,运营商和操作系统

因此,我将绘制的第一个表具有表中的所有国家,并单击扩展按钮,单击该按钮,我将发送所选国家/地区并获取该特定国家/地区的运营商。收到响应后,我想基于响应创建自定义HTML,并在所选行后附加html。

以下是屏幕截图,也包括完整的工作流程:)

步骤1 enter image description here

第2步

enter image description here

第3步

enter image description here

2 个答案:

答案 0 :(得分:2)

我建议为要显示的每个动态HTML片段编写一个自定义的角度组件。然后,您可以编写一个循环组件,该循环组件将根据您提供的类型列表 public function add() { $this->load->helper('ckeditor'); // for loading ckeditor $this->load->library('form_validation'); $this->form_validation->set_rules($this->validation_rules); if ($this->form_validation->run() == TRUE) { $file_name = ''; if ($_FILES["file"]["size"] > 0) { $this->load->library('upload', $this->fu_config); if (!$this->upload->do_upload('file')) { $this->session->set_flashdata('error', $this->upload->display_errors()); redirect('files/add', 'refresh'); } else { $file = $this->upload->data(); $file_name = $file['file_name']; } } // preparing for insertion foreach ($this->validation_rules as $k => $v) { $fields[] = $v['field']; } $data = $this->files_model->array_from_post($fields); $data['file'] = $file_name; if ($this->files_model->save($data)) { $this->session->set_flashdata('success', 'New Event Added Successfully.'); } else { $this->session->set_flashdata('error', 'sorry, Event cannot be Added.'); } redirect('files/add/', 'refresh'); } else { $files = new stdClass(); // Go through all the known fields and get the post values foreach ($this->validation_rules as $key => $field) { $files->$field['field'] = set_value($field['field']); } } $data = array( 'method' => 'add', 'main_content' => 'form', 'editData' => $files, 'ckeditor' => array( 'id' => 'description', 'path' => 'js/ckeditor', ) ); $this->load->view('admin_wrapper', $data); } 嵌套组件。像这样:

*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);
  }
}

现在让您的api返回类似的内容

// 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));
  }
}

请根据您的特定需求进行调整。例如,您将必须管理通过组件层次结构的状态传递(使用组件状态,可观察的服务,MobX,NgRx-选择您的毒药)。

希望这会有所帮助:-)

答案 1 :(得分:1)

我不将解决方案放在这里,因为我不知道您的js代码是什么。但是您可能要考虑使用ViewContainerRef动态添加元素。希望这会有所帮助