File

packages/core/src/lib/list/list.ts

Description

A more sophisticated Collection of Objects with arbitrary content. It comes with features like resolve functions, identifiers, display formatting and sorting.

Extends

Collection

Index

Properties
Methods
Accessors

Constructor

constructor(values?: Array, config: ListConfig, pagination?: Pagination)

Constructs the List. Populates the items and instantiates the fields.

Parameters :
Name Type Optional
values Array<T> Yes
config ListConfig<T> No
pagination Pagination<T> Yes

Properties

Protected change
Type : Subject<List<T>>
Default value : new Subject()

Subject that should be nexted when loading is finished

Public change$
Type : Observable<List<T>>
Default value : this.change.asObservable()

Observable that is nexted when the list has changed.

Public config
Type : ListConfig<T>

The List Configuration, click on ListConfig for details. Can be given an optional ListConfig.

Public fields
Type : Array<Field>

Array of Properties that are relevant for each item. The fields are populated on construction via getFields method.

groups
Type : []
Default value : []

Current Value Groups (Different Unique Values).

Public page
Type : Array<Item<T>>
Default value : []

The items of the current page

Public pagination
Type : Pagination<T>

The list's pagination (Optional)

Public items
Type : Array<T>
Inherited from Collection
Defined in Collection:10

The items must all have the same type T.

Protected update
Type : Subject<Collection<T>>
Default value : new Subject()
Inherited from Collection
Defined in Collection:12

Subject that is nexted when the items update

Public update$
Type : Observable<Collection<T>>
Default value : this.update.asObservable()
Inherited from Collection
Defined in Collection:14

Subject that is nexted when the items change

Methods

add
add(item: Item, unique?: boolean, event: boolean)

Adds the given item to the list and assigns the list config to the item

Parameters :
Name Type Optional Default value
item Item<T> No
unique boolean Yes
event boolean No true
Returns : any
clearFilter
clearFilter(property?: string)

Clears the filter for given property or all properties if none given.

Parameters :
Name Type Optional
property string Yes
Returns : void
Public filter
filter(property: string, value: any, operator: string)

Filters the list after the given property and value

Parameters :
Name Type Optional Default value
property string No
value any No ''
operator string No 'exact'
Returns : void
Public filterableFields
filterableFields()

Returns an array of all sortable fields

Returns : any
Protected getFields
getFields()

Distills Array of item properties. Either uses keys of config.fields or parses the item properties directly.

Returns : Array<Field>
getFilterValue
getFilterValue(property?: string, filterOptions)

Returns the filter

Parameters :
Name Type Optional Default value
property string Yes
filterOptions No this.config.filter
Returns : any
groupBy
groupBy(property)

Returns an Array of all unique values of the given property

Parameters :
Name Optional
property No
Returns : void
Protected hideOverflowFields
hideOverflowFields()

Sets all fields that exceed the maxColumns to hidden

Returns : void
id
id(identifier: any)

Resolves the item with the given Array index or identifier (if configured)

Parameters :
Name Type Optional
identifier any No
Returns : Item<T>
isEmptyFilter
isEmptyFilter(query: null | undefined | string | Array | Object)

Helper function. Returns true if the given query value is empty (also recognizes empty array)

Parameters :
Name Type Optional
query null | undefined | string | Array<any> | Object No
Returns : boolean
isFiltered
isFiltered(property?: string, filterOptions)

Returns true if the given property has a filter set. If no property is given it returns true when no property has a filter.

Parameters :
Name Type Optional Default value
property string Yes
filterOptions No this.config.filter
Returns : boolean
Public isOverTheMax
isOverTheMax(field: Field)

Returns true if the given field index in the visible fields is higher than maxColumns.

Parameters :
Name Type Optional
field Field No
Returns : boolean
Public isSorted
isSorted(property: string, desc?: boolean)

Returns true if the given sort state is active. You can either just check for a property + desc flag

Parameters :
Name Type Optional
property string No
desc boolean Yes
Returns : boolean
Public load
load(config?: ListConfig)

Loads the list page with the given config or, if none given, uses the current config. Reapplies grouping (if any) and calls the change Subject.

Parameters :
Name Type Optional
config ListConfig<T> Yes
Returns : void
setFilter
setFilter(filterOptions: object)
Parameters :
Name Type Optional Default value
filterOptions object No {}
Returns : void
Public sortableFields
sortableFields()

Returns an array of all sortable fields

Returns : any
Protected sortProperty
sortProperty(property: string, desc?: boolean)

Changes the config's sort variables to reflect the given sorting

Parameters :
Name Type Optional
property string No
desc boolean Yes
Returns : void
toggleSelectMode
toggleSelectMode()

Toggles selectMode of list config

Returns : void
toggleSort
toggleSort(property: string, desc?: boolean)

Sorts with given sorting, using the Sorter

Parameters :
Name Type Optional
property string No
desc boolean Yes
Returns : void
Public toggleVisibility
toggleVisibility(field)
Parameters :
Name Optional
field No
Returns : void
Public trackItem
trackItem(index, item)

Item tracking for *ngFor.

Parameters :
Name Optional
index No
item No
Returns : any
add
add(item: T, unique?: boolean, event: boolean)
Inherited from Collection
Defined in Collection:71

Adds the given item to the Collection. If the unique flag is set, the item will only be added if it is not contained.

Parameters :
Name Type Optional Default value
item T No
unique boolean Yes
event boolean No true
Example :
```typescript</p>
<ul>
<li>numbers.add(4);</li>
<li>```</li>
</ul>
Returns : boolean
addAll
addAll(items: Array, unique: boolean, event: boolean)
Inherited from Collection
Defined in Collection:89

Adds the given items to the Collection. If the unique flag is set, only items that are not contained will be added.

Parameters :
Name Type Optional Default value
items Array<T> No []
unique boolean No false
event boolean No true
Example :
```typescript</p>
<ul>
<li>numbers.addAll([5, 6, 7]);</li>
<li>```</li>
</ul>
Returns : void
has
has(item: T)
Inherited from Collection
Defined in Collection:42

Checks if the Collection contains the given item.

Parameters :
Name Type Optional
item T No
Example :
```typescript</p>
<ul>
<li>numbers.has(2); //true</li>
<li>```</li>
</ul>
Returns : boolean
hasAll
hasAll(items: Array)
Inherited from Collection
Defined in Collection:53

Checks if the Collection contains all given items.

Parameters :
Name Type Optional Default value
items Array<T> No []
Example :
```typescript</p>
<ul>
<li>numbers.has([1,2]); //true</li>
<li>```</li>
</ul>
Returns : boolean
index
index(item: T)
Inherited from Collection
Defined in Collection:31

Returns the index of the given item

Parameters :
Name Type Optional
item T No
Returns : number
isEmpty
isEmpty()
Inherited from Collection
Defined in Collection:160

Returns true if the collection is empty

Returns : boolean
move
move(item: T, index: number, event: boolean)
Inherited from Collection
Defined in Collection:165

Moves the given item to the given array index.

Parameters :
Name Type Optional Default value
item T No
index number No
event boolean No true
Returns : void
remove
remove(item: T, event: boolean)
Inherited from Collection
Defined in Collection:106

Removes the given item from the Collection.

Parameters :
Name Type Optional Default value
item T No
event boolean No true
Example :
```typescript</p>
<ul>
<li>numbers.remove(4);</li>
<li>```</li>
</ul>
Returns : boolean
removeAll
removeAll(items?: Array, event: boolean)
Inherited from Collection
Defined in Collection:123

Removes all items from the Collection.

Parameters :
Name Type Optional Default value
items Array<T> Yes
event boolean No true
Example :
```typescript</p>
<ul>
<li>numbers.removeAll();</li>
<li>```</li>
</ul>
Returns : void
replaceWith
replaceWith(items: Array, event: boolean)
Inherited from Collection
Defined in Collection:147

Replaces all current items with the given items.

Parameters :
Name Type Optional Default value
items Array<T> No
event boolean No true
Returns : void
toggle
toggle(item: T, event: boolean)
Inherited from Collection
Defined in Collection:138

Toggles the item in and out of collection

Parameters :
Name Type Optional Default value
item T No
event boolean No true
Returns : void

Accessors

display
getdisplay()

Getter for items, calls transform

List

List is a Collection with list specific features:

  • It instantiates each array value as an Item
  • It instantiates each field property config as a Field.
  • It supports getting items by identifier via the id method (if identifier is set)
  • It supports filtering, sorting, grouping and pagination.
this.trees = new List([{
  name: 'Appletree',
  height: 10,
  fruits: true
}, {
  name: 'Lemontree',
  height: 8,
  fruits: true
}, {
  name: 'Birch',
  height: 20,
  fruits: false
}, {
  name: 'Cinnamon',
  height: 10,
  fruits: true
}], {
  size: 3,
  fields: {
    name: {
      label: 'Name',
      view: 'string',
      required: true,
      input: CoolStringComponent,
      output: CoolStringComponent
    },
    height: {
      label: 'Höhe',
      group: (h) => h > 10 ? 'Höher als 10m' : 'Niedriger als 10m',
      view: 'number',
      required: true,
      validate: (value) => {
        if (value < 1) {
          return 'Der Wert muss positiv sein.'
        }
      }
    },
    fruits: {
      label: 'Früchte',
      display: (value) => value ? 'ja' : 'nein',
      view: 'boolean'
    },
  },
});

Here is an easy example of using a List instance in a template with Angular:

<ul>
    <li *ngFor="let tree of trees.items">
        {{tree.display('name')}}
        Früchte? {{tree.display('fruits')}}
    </li>
</ul>
<button (click)="trees.toggleSort('name')">Nach Name sortieren</button>

For all the features, have a look at the List documentation.

import { Observable, Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { Collection } from '../collection/collection';
import { Field } from '../field/field';
import { Item } from '../item/item';
import { Pagination } from '../pagination/pagination';
import { Sorter } from '../sorter/sorter';
import { ListConfig } from './list-config.interface';

/**
 * A more sophisticated Collection of Objects with arbitrary content.
 * It comes with features like resolve functions, identifiers, display formatting and sorting.
 */
export class List<T> extends Collection<Item<T>> {
  /**
   * Array of Properties that are relevant for each item. The fields are populated on construction
   * via getFields method.
   */
  public fields: Array<Field>;
  /**
   * The List Configuration, click on ListConfig for details. Can be given an optional ListConfig.
   */
  public config: ListConfig<T>;
  /**
   * Current Value Groups (Different Unique Values).
   */
  groups = [];
  /** The list's pagination (Optional) */
  public pagination: Pagination<T>;
  /** The items of the current page */
  public page: Array<Item<T>> = [];
  /** Subject that should be nexted when loading is finished */
  protected change: Subject<List<T>> = new Subject();
  /** Observable that is nexted when the list has changed. */
  public change$: Observable<List<T>> = this.change.asObservable();

  /** Getter for items, calls transform */
  get display() {
    if (!this.config || !this.config.display) {
      return this.items;
    }
    return this.config.display(this.items);
  }

  /**
   * Constructs the List. Populates the items and instantiates the fields.
   */
  constructor(values?: Array<T>, config: ListConfig<T> = {}, pagination?: Pagination<T>) {
    super([]);
    if (values) {
      super.addAll(values.map((value) => new Item(value, Object.assign({}, config))), false, false);
    }
    this.config = Object.assign({ page: 1, maxColumns: 8 }, config || {});
    this.fields = this.getFields();
    this.hideOverflowFields();
    this.pagination = pagination || new Pagination(this.config, this.items.length);
    this.change$.subscribe(() => {
      this.pagination.select(this.config.page || 1, true);
    });
    if (!pagination) {
      // load if no custom pagination was given
      this.pagination.change$.pipe(debounceTime(200)).subscribe((_config) => this.load(_config));
      this.load();
    }
  }

  /** Loads the list page with the given config or, if none given, uses the current config.
   * Reapplies grouping (if any) and calls the change Subject. */
  public load(config?: ListConfig<T>) {
    if (config) {
      Object.assign(this.config, config);
    }
    this.page = this.pagination.slice(this.items);
    this.groupBy(this.config.sortBy);
    this.change.next(this);
  }

  /** Adds the given item to the list and assigns the list config to the item*/
  add(item: Item<T>, unique?: boolean, event: boolean = true) {
    item.useConfig(this.config);
    return super.add(item, unique, event);
  }

  /**
   * Distills Array of item properties. Either uses keys of config.fields or parses the item
   * properties directly.
   */
  protected getFields(): Array<Field> {
    if (this.config && this.config.fields) {
      return Object.keys(this.config.fields)
        .filter((key) => this.config.fields[key].list !== false)
        .map((field) => new Field(field, this.config.fields[field]));
    }
    const fields = [];
    this.items.forEach((item) => {
      item.getProperties().forEach((property) => {
        if (!fields.find((f) => f.property === property)) {
          fields.push(new Field(property, { type: typeof item.resolve(property) }));
        }
      });
    });
    return fields;
  }

  public toggleVisibility(field) {
    field.hideInList = !field.hideInList;
    this.change.next(this);
  }

  /** Sets all fields that exceed the maxColumns to hidden */
  protected hideOverflowFields() {
    if (this.config && this.config.maxColumns) {
      this.fields
        .filter((f) => !f.hideInList)
        .forEach((field, index) => {
          if (index >= this.config.maxColumns && field.hideInList === undefined) {
            field.hideInList = true;
          }
        });
    }
  }

  /**
   * Resolves the item with the given Array index or identifier (if configured)
   */
  id(identifier: any): Item<T> {
    if (identifier === undefined) {
      throw new Error(`cannot get item with identifier "${identifier}"`);
    }
    return (
      this.items.find((item, key) => {
        if (!item.config.identifier) {
          return false;
        }
        return item.id() === identifier;
      }) || this.items[identifier]
    );
  }

  /** Filters the list after the given property and value */
  public filter(property: string, value: any = '', operator: string = 'exact') {
    this.config.filter = { [property]: value };
    if (value === null) {
      this.load();
      return;
      // this.page = [].concat(this.items);
    }
    // TODO find way to filter with pagination and without loosing filtered out items
    this.page = this.items
      .filter((item) => {
        return item
          .resolve(property)
          .toLowerCase()
          .includes(value.toLowerCase()); // TODO: better filter
      })
      .slice(0, this.config.size || 100);
  }

  setFilter(filterOptions = {}) {
    if (!this.isFiltered(null, filterOptions) && !this.isFiltered()) {
      return;
    }
    if (this.isEmptyFilter(filterOptions)) {
      return this.clearFilter();
    }
    filterOptions = Object.keys(filterOptions).reduce((filtered, key) => {
      if (this.isEmptyFilter(filterOptions[key])) {
        delete filtered[key];
      }
      return filtered;
    }, filterOptions);

    this.load({
      page: 1,
      filter: filterOptions,
    });
  }

  /** Clears the filter for given property or all properties if none given. */
  clearFilter(property?: string) {
    if (property) {
      return this.filter(property, null);
    }
    this.load({
      page: 1,
      filter: {},
    });
  }

  /** Helper function. Returns true if the given query value is empty (also recognizes empty array) */
  isEmptyFilter(query: null | undefined | string | Array<any> | Object) {
    return (
      query === '' ||
      query === null ||
      query === undefined ||
      (Array.isArray(query) && !query.length) ||
      (typeof query === 'object' && Object.keys(query).length === 0)
    );
  }

  /** Returns true if the given property has a filter set. If no property is given it returns true when no property has a filter. */
  isFiltered(property?: string, filterOptions = this.config.filter) {
    if (!filterOptions) {
      return false;
    }
    if (!property) {
      return Object.keys(filterOptions).filter((key) => !this.isEmptyFilter(filterOptions[key])).length > 0;
    }
    return !this.isEmptyFilter(filterOptions[property]);
  }

  /** Returns the filter */
  getFilterValue(property?: string, filterOptions = this.config.filter) {
    if (!property) {
      property = this.config.label;
    }
    if (!filterOptions || !property) {
      return undefined;
    }
    return filterOptions[property];
  }

  /** Changes the config's sort variables to reflect the given sorting */
  protected sortProperty(property: string, desc?: boolean) {
    if (desc !== undefined) {
      if (this.config.desc === desc) {
        delete this.config.sortBy;
        delete this.config.desc;
      } else {
        this.config.desc = desc;
        this.config.sortBy = property;
      }
      return;
    } else if (property !== this.config.sortBy) {
      delete this.config.desc;
      this.config.sortBy = property;
    } else if (this.config.desc) {
      delete this.config.sortBy;
    }
    this.config.desc = this.config.desc === undefined ? desc || false : !this.config.desc;
  }

  /** Returns true if the given sort state is active. You can either just check for a property + desc flag */
  public isSorted(property: string, desc?: boolean) {
    if (typeof desc === 'undefined') {
      return this.config.sortBy === property;
    }
    return this.config.sortBy === property && this.config.desc === desc;
  }

  /** Sorts with given sorting, using the Sorter */
  toggleSort(property: string, desc?: boolean) {
    this.sortProperty(property, desc);
    Sorter.sort(this.items, property, this.config.desc);
    this.load(this.config);
  }
  /** Toggles selectMode of list config */
  toggleSelectMode() {
    this.config = Object.assign({}, this.config, {
      selectMode: !this.config.selectMode,
    });
    this.change.next(this);
  }

  /** Returns an Array of all unique values of the given property */
  groupBy(property) {
    delete this.groups;
    const page = this.pagination ? this.pagination.getPage() : 0;
    if (!property || !this.config.fields || !this.config.fields[property] || !this.config.fields[property].group) {
      this.groups = [
        {
          page,
          sortBy: this.config.sortBy,
          desc: this.config.desc,
        },
      ];
      return;
    }
    const groups = [];
    this.page.forEach((item) => {
      const value = item.group(property);
      if (!groups.find((g) => g.value === value)) {
        groups.push({
          value,
          page,
          property: this.config.sortBy,
          desc: this.config.desc,
        });
      }
    });
    this.groups = groups;
  }

  /** Item tracking for *ngFor. */
  public trackItem(index, item) {
    return index;
  }
  /** Returns an array of all sortable fields */
  public sortableFields() {
    return this.fields.filter((field) => field.sortable);
  }
  /** Returns an array of all sortable fields */
  public filterableFields() {
    return this.fields.filter((field) => field.filterable);
  }
  /** Returns true if the given field index in the visible fields is higher than maxColumns.  */
  public isOverTheMax(field: Field) {
    return this.fields.filter((f) => !f.hideInList).indexOf(field) >= this.config.maxColumns;
  }
}

result-matching ""

    No results matching ""