Как динамически создать вложенный компонент развернуть / свернуть n уровня - PullRequest
0 голосов
/ 19 сентября 2018

Я разрабатываю сценарий n-уровня вложенных таблиц с использованием div.

Таким образом, имеется от 5 до 6 столбцов с n числом строк, каждый первый столбец должен быть развернут / свернут кнопкой нащелчок по которому я вызываю в API, который дает мне данные, относящиеся к выбранным фильтрам строк.

Ранее, когда я работал с основными JavaScript и jQuery, я использовал find метод селектора документа для определения родителяразверните / сверните кнопку и нажмите динамически созданный HTML после этого конкретного div только с использованием innerHTML или append метода jQuery

Я немного новичок в 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;
       }
   }

    }

Код для рисования таблицы на основе данных разделения и отчета.

Предположим, что в стране есть только один объект для страны splitopt объект, чем checkIfHaveMoreSplits() возвращает 0, что означает, что мне не нужно давать кнопку расширения, и если это не 0, эта кнопка расширения появится там.

При нажатии на кнопку расширения я выберу следующийэлемент из 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>

Я управляю одним массивом, который определяет, насколько глубоко я могу развернуть элемент свертывания

Предположим, что массив состоит из трех элементов, а именно страны, носителя и ОС

Итак, первая таблица, которую я нарисую, со всеми странами в таблице с кнопкой расширения, по нажатию которой я будуотправить селстрана и получить перевозчиков этой конкретной страны.После получения ответа я хочу создать собственный HTML-код на основе ответа и добавить html после выбранной строки.

Вот скриншоты, также состоящие из полного рабочего процесса:)

Шаг 1 enter image description here

Шаг 2

enter image description here

Шаг 3

enter image description here

Ответы [ 2 ]

0 голосов
/ 24 сентября 2018

Я бы предложил написать собственный угловой компонент для каждой из ваших динамических частей 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 - выберите яд).

Надеюсь, это немного поможет: -)

0 голосов
/ 23 сентября 2018

Я не ставлю здесь решение, так как не знаю, какой у вас код js.Но вы можете рассмотреть возможность использования ViewContainerRef для динамического добавления элементов.Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...