File

packages/ui/src/lib/list/list-header/list-header.component.ts

Description

This component renders, as the name states, the header of a list.

Implements

OnChanges

Metadata

changeDetection ChangeDetectionStrategy.OnPush
selector ec-list-header
templateUrl ./list-header.component.html

Index

Properties
Methods
Inputs

Constructor

constructor(listConfig: ListConfigService, cdr: ChangeDetectorRef)
Parameters :
Name Type Optional
listConfig ListConfigService No
cdr ChangeDetectorRef No

Inputs

list
Type : List<any>

The list instance

selection
Type : Selection<any>

The selection instance. This is optional. If It is not provided, no checkbox will be visible.

Methods

clearFilter
clearFilter()
Returns : void
Public filterField
filterField(property)

opens the given filter pop and closes all others

Parameters :
Name Optional
property No
Returns : void
initFilterForm
initFilterForm(filterForm)
Parameters :
Name Optional
filterForm No
Returns : void
inputReady
inputReady(input)
Parameters :
Name Optional
input No
Returns : void
isLastColumn
isLastColumn(field: Field)

Yields true if the given field is the last column of the current list header

Parameters :
Name Type Optional
field Field No
Returns : boolean
ngOnChanges
ngOnChanges(changes?)
Parameters :
Name Optional
changes Yes
Returns : void
resetFilter
resetFilter()
Returns : void
setFilter
setFilter(field, value)
Parameters :
Name Optional
field No
value No
Returns : void
toggledFilterPop
toggledFilterPop(active)
Parameters :
Name Optional
active No
Returns : void
Public toggleVisibility
toggleVisibility(field: Field)

Toggles the fields visibility in the list

Parameters :
Name Type Optional
field Field No
Returns : void

Properties

Public cdr
Type : ChangeDetectorRef
filteredField
Type : any
filterForm
Type : FormComponent<any>
filterFormConfig
Type : ListConfig<any>

The config for the filter form

filterInput
Type : InputComponent
filterPop
Type : PopComponent
Decorators :
@ViewChild('filterPop', {static: true})

The pop dropdowns that contain the filtering

Public listConfig
Type : ListConfigService

List Header

This document conceptualizes the ec-list-header.

Idea

The list-header displays the field labels in columns. If a fields are sortable or filterable, a clickeable icons will be shown.

Filtering

If a field has the option "filterable" set to true, a search icon will be shown. When clicking the icon, an input field will appear above the list header which can be used to filter the list by the field property.

  • The input field will be autofocused
  • No validation errors should be shown
  • When the input value changes, the list will load after a short debounce.
  • When the input is closed, the filter will be cleared.
  • When another field's search icon is clicked, all previous filters will be cleared.

import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, ViewChild } from '@angular/core';
import { Field, List, ListConfig, Selection } from '@ec.components/core';
import { FormComponent } from '../../form/form.component';
import { PopComponent } from '../../pop/pop.component';
import { ListConfigService } from '../list-config.service';
import { InputComponent } from '../../io/input/input.component';

/** This component renders, as the name states, the header of a list.*/
@Component({
  selector: 'ec-list-header',
  templateUrl: './list-header.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ListHeaderComponent implements OnChanges {
  /** The list instance */
  @Input() list: List<any>;
  /** The selection instance. This is optional. If It is not provided, no checkbox will be visible.*/
  @Input() selection: Selection<any>;
  /** The pop dropdowns that contain the filtering */
  @ViewChild('filterPop', { static: true }) filterPop: PopComponent;
  /** The config for the filter form */
  filterFormConfig: ListConfig<any>;
  filteredField: any;
  filterForm: FormComponent<any>;
  filterInput: InputComponent;

  constructor(public listConfig: ListConfigService, public cdr: ChangeDetectorRef) {}

  setFilter(field, value) {
    this.list.setFilter({ [field.property]: value });
  }

  inputReady(input) {
    this.filterInput = input;
    input.focus(true);
  }

  initFilterForm(filterForm) {
    // is called when form is ready
    this.filterForm = filterForm;
    if (this.list.config.defaultFilter) {
      this.filterField(this.list.config.defaultFilter);
    }
  }

  ngOnChanges(changes?) {
    if (!changes.list) {
      return;
    }
    if (!this.list || !this.list.config || !this.list.config.fields) {
      return;
    }
    /* this.list.change$.subscribe(() => {
      if (this.filterInput) {
        // this.filterInput.focus(true);
      }
    }); */
    this.filterFormConfig = {
      ...this.list.config,
      fields: this.list.filterableFields().reduce((fields, field) => {
        return {
          ...fields,
          [field.property]: {
            ...this.list.config.fields[field.property],
            view: field.getView('filter'),
            required: false,
            readOnly: false,
            immutable: false,
            disabled: false,
            create: true,
            form: true,
            autofocus: true,
            nestedCrudConfig: {
              ...field.nestedCrudConfig,
              methods: ['get'],
            },
          },
        };
      }, {}),
    };
  }

  /** opens the given filter pop and closes all others */
  public filterField(property) {
    if (this.filteredField) {
      if (this.filteredField.property === property) {
        /* this.filterPop.hide(); */
        if (this.filterInput) {
          this.filterInput.focus(true);
        }
        return;
      }
      this.clearFilter();
    }
    // patch current filter value to control
    const control = this.filterForm.group.get(property);
    if (!control) {
      console.warn('no control found for ' + property + '. Is it filterable?', this.list.config, this.filterForm.group);
      return;
    }
    this.filterForm.group.get(property).patchValue(this.list.getFilterValue(property));
    this.filteredField = this.filterForm.form.getField(property);
    setTimeout(() => this.filterPop.show());
  }

  resetFilter() {
    if (!this.filteredField || !this.list || !this.list.isFiltered(this.filteredField.property)) {
      return;
    }
    this.filterForm.group.get(this.filteredField.property).reset();
    this.list.clearFilter();
  }

  clearFilter() {
    if (!this.filteredField || !this.list.isFiltered(this.filteredField.property)) {
      delete this.filteredField;
      return;
    }
    this.resetFilter();
    delete this.filteredField;
  }

  toggledFilterPop(active) {
    if (!active) {
      this.clearFilter();
    }
    if (this.filterInput) {
      this.filterInput.focus(true);
    }
  }

  /** Toggles the fields visibility in the list */
  public toggleVisibility(field: Field) {
    this.list.toggleVisibility(field);
    this.listConfig.storeConfig(this.list);
  }

  /** Yields true if the given field is the last column of the current list header */
  isLastColumn(field: Field) {
    if (!this.list || !this.list.fields) {
      return;
    }
    const visibleColumns = this.list.fields.filter((f) => !f.hideInList);
    return field.property === visibleColumns[visibleColumns.length - 1].property;
  }
}
<ec-pop (toggle)="toggledFilterPop($event)" [hideOnEscape]="false" #filterPop>
  <div class="ec-searchbar">
    <div *ngIf="filteredField" data-grid="no-gutter">
      <div data-col="auto">
        <div class="input-group">
          <div class="input-group__addon" data-flex="center">
            <ec-icon name="search"></ec-icon>
          </div>
          <ec-input
            (ready)="inputReady($event)"
            (changed)="setFilter(filteredField, $event)"
            [lazy]="true"
            [component]="filteredField.getComponent('filter') || filteredField.input"
            [field]="filteredField"
            [group]="filterForm?.group"
            [debounce]="200"
            *ngIf="filterPop.active && filterForm?.group"
            #filterInput
            data-col="auto"
          ></ec-input>
          <div
            class="input-group__addon"
            *ngIf="list?.isFiltered(filteredField.property)"
            data-flex="center center-items"
          >
            <a class="btn btn_clear btn_square btn_small is-ink-light" (click)="resetFilter()">
              <ec-icon name="clear"></ec-icon>
            </a>
          </div>
        </div>
      </div>
      <div data-col="fit">
        <div tabindex="0" class="dropdown dropdown_right">
          <span class="btn btn_clear btn_square">
            <ec-icon name="filter"></ec-icon>
          </span>
          <div class="dropdown-options">
            <div *ngFor="let field of list?.sortableFields()">
              <a
                class="dropdown-option sorting-option"
                [class.is-sorted-desc]="list.isSorted(field.property, true)"
                [class.is-sorted-asc]="list.isSorted(field.property, false)"
              >
                <span class="sorting-option-indicator">
                  <span class="asc" (click)="list.toggleSort(field.property, false)"></span>
                  <span class="desc" (click)="list.toggleSort(field.property, true)"></span>
                </span>
                <span (click)="list.toggleSort(field.property)">{{ field.getLabel() }}</span>
              </a>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</ec-pop>
<div *ngIf="filterFormConfig">
  <ec-form
    [config]="filterFormConfig"
    [value]="list?.config?.filter"
    [empty]="true"
    #filterForm
    (ready)="initFilterForm($event)"
    [lazy]="true"
  ></ec-form>
  <div class="ec-list-header">
    <div class="ec-list-cell ec-list-item__selector" *ngIf="list?.config?.selectMode && !list?.isEmpty()">
      <input
        type="checkbox"
        (click)="selection?.toggleAll(list?.page)"
        [checked]="selection?.hasAll(list?.page)"
        [disabled]="selection?.config?.solo"
      />
    </div>
    <div
      class="ec-list-cell"
      *ngFor="let field of list?.fields; let i = index"
      [ngClass]="'ec-list-cell_' + field.getView('output') + ' ' + (field?.classes || '')"
      [class.is-hidden]="field.hideInList"
      [class.is-sortable]="field.sortable"
      [class.is-sorted]="list.isSorted(field.property)"
      [class.is-filterable]="field.filterable"
      [class.is-filtered]="list.isFiltered(field.property)"
      [class.is-last-column]="isLastColumn(field)"
      [class.is-sorted-desc]="list.isSorted(field.property, true)"
      [class.is-sorted-asc]="list.isSorted(field.property, false)"
    >
      <!-- [class.is-filter-active]="filterPop.active"-->
      <a (click)="filterField(field.property)" class="is-filterable-control" *ngIf="field.filterable">
        <ec-icon name="search"></ec-icon>
      </a>
      <!-- <a (click)="toggleVisibility(field)" class="is-visibility-control">
        <ec-icon name="eye-closed"></ec-icon>
      </a> -->

      <span
        class="ec-list-column-title"
        (click)="field.sortable && list.toggleSort(field.property)"
        [title]="field.property"
      >
        {{ field.getLabel() }}
      </span>
      <span class="is-sortable-indicator" *ngIf="field.sortable">
        <span class="asc" (click)="list.toggleSort(field.property, false)"></span>
        <span class="desc" (click)="list.toggleSort(field.property, true)"></span>
      </span>
    </div>

    <div class="dropdown dropdown_right ec-list-column-filter" tabindex="0" *ngIf="!list?.config?.disableColumnFilter">
      <span>&#9776;</span>
      <div class="dropdown-options">
        <div
          class="dropdown-option"
          *ngFor="let field of list?.fields"
          (click)="toggleVisibility(field)"
          [class.is-active]="!field.hideInList"
          [class.is-hidden]="field.hideInColumnFilter"
        >
          {{ field.getLabel() }}
        </div>
      </div>
    </div>
  </div>
</div>
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""