File

packages/data/src/lib/entry-select/entry-select.component.ts

Description

Shows entries of a selection and is able to pick new ones from a crud list https://components.entrecode.de/entries/entry-select?e=1

Extends

SelectComponent

Implements

OnChanges OnInit

Metadata

encapsulation ViewEncapsulation.None
providers { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EntrySelectComponent), multi: true, }
selector ec-entry-select
templateUrl ./entry-select.component.html

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor(modelConfig: ModelConfigService, resourceService: ResourceService, symbol: SymbolService, sdk: SdkService, elementRef: ElementRef, auth: AuthService, cdr: ChangeDetectorRef)
Parameters :
Name Type Optional
modelConfig ModelConfigService No
resourceService ResourceService No
symbol SymbolService No
sdk SdkService No
elementRef ElementRef No
auth AuthService No
cdr ChangeDetectorRef No

Inputs

config
Type : CrudConfig<EntryResource>

The config that should be merged into the generated config

focusEvent
Type : EventEmitter<boolean>
Default value : new EventEmitter()

The event that focuses the input

model
Type : string

The model to pick from, alternative to field with model property set.

solo
Type : boolean

Wether or not the selection should be solo

value
Type : Array<EntryResource>

The value that should be prefilled

config
Type : ListConfig<T>
Inherited from SelectComponent
Defined in SelectComponent:48

Configuration Object for List

disabled
Type : ListConfig<T>
Inherited from SelectComponent
Defined in SelectComponent:50

If true, the input will be disabled

focusEvent
Type : EventEmitter<boolean>
Default value : new EventEmitter()
Inherited from SelectComponent
Defined in SelectComponent:76

Event emitter to focus input

formControl
Type : FormControl
Inherited from SelectComponent
Defined in SelectComponent:88

The formControl that is used.

list
Type : List<T>
Inherited from SelectComponent
Defined in SelectComponent:70

The Instance of the List

placeholder
Type : string
Inherited from SelectComponent
Defined in SelectComponent:56

Input placeholder

selection
Type : Selection<T>
Inherited from SelectComponent
Defined in SelectComponent:54

The used selection

solo
Type : boolean
Inherited from SelectComponent
Defined in SelectComponent:74

Wether or not the selection should be solo

value
Type : Array<T> | T
Inherited from SelectComponent
Defined in SelectComponent:52

The visible items

values
Type : Array<T>
Inherited from SelectComponent
Defined in SelectComponent:72

Available Items

Outputs

add
Type : EventEmitter<Item<EntryResource>>

Emits when an entry is being added

remove
Type : EventEmitter<Item<EntryResource>>

Emits when an entry is being removed

add
Type : EventEmitter<Item<T>>
Inherited from SelectComponent
Defined in SelectComponent:64

Emits when an item is being added

changed
Type : EventEmitter<Selection<T>>
Inherited from SelectComponent
Defined in SelectComponent:58

Event emitter on item selection

enter
Type : EventEmitter<SelectComponent<T>>
Inherited from SelectComponent
Defined in SelectComponent:66

Emits the query when enter is pressed

enterPressed
Type : Subject<string>
Inherited from SelectComponent
Defined in SelectComponent:68

Subject that is nexted when enter is pressed

itemClick
Type : EventEmitter<Item<T>>
Inherited from SelectComponent
Defined in SelectComponent:60

Event emitter on selected item click

remove
Type : EventEmitter<Item<T>>
Inherited from SelectComponent
Defined in SelectComponent:62

Emits when an item is being removed

Methods

defaultPlaceholder
defaultPlaceholder()
Returns : any
editItem
editItem(item: Item, e)

Is called when a selected item has been clicked.

Parameters :
Name Type Optional
item Item<EntryResource> No
e No
Returns : void
filterDropdownList
filterDropdownList(list, query)
Parameters :
Name Optional
list No
query No
Returns : void
focusSearchbar
focusSearchbar()
Returns : void
formSubmitted
formSubmitted(form: Form)

Is called when the nested entry-form has been saved. Selects the fresh entry and clears the form

Parameters :
Name Type Optional
form Form<EntryResource> No
Returns : void
Public hasMethod
hasMethod(method: string)

Returns true if the given method is part of the methods array (or if there is no methods array)

Parameters :
Name Type Optional
method string No
Returns : boolean
initConfig
initConfig()

Generates the config and sets up form control

Returns : void
ngOnChanges
ngOnChanges()

Fires initConfig

Returns : void
ngOnInit
ngOnInit()

Fires initConfig

Returns : void
onChange
onChange()
Returns : void
pasteValue
pasteValue(e)
Parameters :
Name Optional
e No
Returns : void
removeItem
removeItem(item, skipDelete, e?)
Parameters :
Name Optional
item No
skipDelete No
e Yes
Returns : void
togglePop
togglePop(e?, noFocus)
Parameters :
Name Optional Default value
e Yes
noFocus No false
Returns : void
useConfig
useConfig(config: CrudConfig)

Calls super.useConfig and then creates special dropdownConfig with just entryTitle as field

Parameters :
Name Type Optional Default value
config CrudConfig<EntryResource> No {}
useModel
useModel(model)
Parameters :
Name Optional
model No
Returns : void
activate
activate(e)
Inherited from SelectComponent
Parameters :
Name Optional
e No
Returns : void
addItem
addItem(item: Item)
Inherited from SelectComponent

Adds the given ite, emits add output if observed

Parameters :
Name Type Optional
item Item<any> No
Returns : void
cancelDrag
cancelDrag(item, e, target)
Inherited from SelectComponent

is called when the drag stops in any kind of way.

Parameters :
Name Optional Default value
item No
e No
target No e.target
Returns : void
canRemove
canRemove()
Inherited from SelectComponent
Returns : boolean
Public clickItem
clickItem(item, e?)
Inherited from SelectComponent

Is called when a selected item is clicked

Parameters :
Name Optional
item No
e Yes
Returns : void
filterDropdownList
filterDropdownList(listComponent: ListComponent, query)
Inherited from SelectComponent
Parameters :
Name Type Optional
listComponent ListComponent<any> No
query No
Returns : void
focus
focus(e)
Inherited from SelectComponent
Parameters :
Name Optional
e No
Returns : void
focusSearchbar
focusSearchbar()
Inherited from SelectComponent
Returns : void
getArray
getArray(value)
Inherited from SelectComponent
Parameters :
Name Optional
value No
Returns : any
getParentTree
getParentTree(el, tree: [])
Inherited from SelectComponent
Parameters :
Name Type Optional Default value
el No
tree [] No []
Returns : any
handleKey
handleKey(e, list)
Inherited from SelectComponent
Parameters :
Name Optional
e No
list No
Returns : void
hasSoloSelection
hasSoloSelection()
Inherited from SelectComponent
Returns : boolean
initSelection
initSelection()
Inherited from SelectComponent

creates the collection from the config

Returns : void
Public listItemClicked
listItemClicked(item, list?)
Inherited from SelectComponent

Select handler. Toggles selection.

Parameters :
Name Optional
item No
list Yes
Returns : void
ngOnChanges
ngOnChanges()
Inherited from SelectComponent
Returns : void
ngOnInit
ngOnInit()
Inherited from SelectComponent
Returns : void
onChange
onChange()
Inherited from SelectComponent

Fires on selection change. Hides dropdown if solo

Returns : void
onDragStart
onDragStart(item, e, target)
Inherited from SelectComponent

is called when an element is dragged by the user. hides element in selection

Parameters :
Name Optional Default value
item No
e No
target No e.target
Returns : void
onDrop
onDrop(e)
Inherited from SelectComponent

called when the element is dropped. moves item in selection.

Parameters :
Name Optional
e No
Returns : void
preventDefault
preventDefault(e)
Inherited from SelectComponent
Parameters :
Name Optional
e No
Returns : void
registerOnChange
registerOnChange(fn)
Inherited from SelectComponent

registers change method. (handled by angular)

Parameters :
Name Optional
fn No
Returns : void
registerOnTouched
registerOnTouched()
Inherited from SelectComponent
Returns : void
removeItem
removeItem(item: Item, e?)
Inherited from SelectComponent

Removes the given item from selection

Parameters :
Name Type Optional
item Item<any> No
e Yes
Returns : void
setDisabledState
setDisabledState(isDisabled)
Inherited from SelectComponent
Parameters :
Name Optional
isDisabled No
Returns : void
use
use(value, event)
Inherited from SelectComponent

Uses the given value as selection items

Parameters :
Name Optional Default value
value No
event No true
Returns : void
useConfig
useConfig(config: ListConfig)
Inherited from SelectComponent

Initializes either with values, collection or list. Creates Selection with config.

Parameters :
Name Type Optional Default value
config ListConfig<T> No {}
Returns : void
writeValue
writeValue(value: any)
Inherited from SelectComponent

Called when the model changes

Parameters :
Name Type Optional
value any No
Returns : void

Properties

Public cdr
Type : ChangeDetectorRef
Public config
Type : CrudConfig<EntryResource>

The config that is being generated.

confirmDelete
Type : ResourceDeletePopComponent
Decorators :
@ViewChild(ResourceDeletePopComponent, {static: true})

THe nested delete confirmation pop

Protected control
Type : FormControl

The form control that is used

dropdown
Type : PopComponent
Decorators :
@ViewChild('dropdown')

The dropdown pop with the list to select from

Public dropdownConfig
Type : CrudConfig<EntryResource>

The config for the dropdown crud list

dropdownList
Type : any
Decorators :
@ViewChild('dropdownList')

The nested full EntryListComponent

Public elementRef
Type : ElementRef
entryListPop
Type : EntryListPopComponent
Decorators :
@ViewChild(EntryListPopComponent, {static: true})

The nested entry list pop

entryPop
Type : EntryPopComponent
Decorators :
@ViewChild(EntryPopComponent)

The nested entry pop

Protected group
Type : FormGroup

The form group that is used

Protected item
Type : Item<any>

The item that is targeted by the input

lightModel
Type : any

The current lightModel (part of root response)

models

Model list that is only loaded when needing to pick the model first.

ready
Type : Promise<CrudConfig<EntryResource>>

Promise that resolves when the config is ready

Public resourceService
Type : ResourceService
Public sdk
Type : SdkService
searchbar
Type : SearchbarComponent
Decorators :
@ViewChild(SearchbarComponent)

The nested searchbar

Public symbol
Type : SymbolService
Public cdr
Type : ChangeDetectorRef
Inherited from SelectComponent
Defined in SelectComponent:90
dragged
Type : Item<T>
Inherited from SelectComponent
Defined in SelectComponent:46

the current dragged element

dropdown
Type : PopComponent
Decorators :
@ViewChild('dropdown', {static: true})
Inherited from SelectComponent
Defined in SelectComponent:78

The selection dropdown

dropdownList
Type : ListComponent<any>
Decorators :
@ViewChild(ListComponent, {static: true})
Inherited from SelectComponent
Defined in SelectComponent:82

The list in the dropdown

dropdownLoader
Type : LoaderComponent
Decorators :
@ViewChild('dropdownLoader', {static: true})
Inherited from SelectComponent
Defined in SelectComponent:80

The loader inside the dropdown

Public elementRef
Type : ElementRef
Inherited from SelectComponent
Defined in SelectComponent:90
propagateChange
Default value : () => {...}
Inherited from SelectComponent

Propagates formControl/ngModel changes

searchbar
Type : SearchbarComponent
Decorators :
@ViewChild(SearchbarComponent)
Inherited from SelectComponent
Defined in SelectComponent:84

The nested searchbar

toggleItem
Type : Subject<Item<T>>
Default value : new Subject()
Inherited from SelectComponent
Defined in SelectComponent:86

Subject that is nexted when an item is being selected (clicked or entered on)

import {
  ChangeDetectorRef,
  Component,
  ElementRef,
  EventEmitter,
  forwardRef,
  Input,
  OnChanges,
  OnInit,
  Output,
  ViewChild,
  ViewEncapsulation,
} from '@angular/core';
import { FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Form, Item } from '@ec.components/core';
import { PopComponent, SearchbarComponent, SelectComponent, SymbolService } from '@ec.components/ui';
import EntryResource from 'ec.sdk/lib/resources/publicAPI/EntryResource';
import { AuthService } from '../auth/auth.service';
import { CrudConfig } from '../crud/crud-config.interface';
import { EntryListPopComponent } from '../entry-list-pop/entry-list-pop.component';
import { EntryPopComponent } from '../entry-pop/entry-pop.component';
import { ModelConfigService } from '../model-config/model-config.service';
import { ResourceService } from '../resource-config/resource.service';
import { ResourceDeletePopComponent } from '../resource-delete-pop/resource-delete-pop.component';
import { SdkService } from '../sdk/sdk.service';

// import LiteEntryResource from "ec.sdk/lib/resources/publicAPI/LiteEntryResource";

/** Shows entries of a selection and is able to pick new ones from a crud list
 * <example-url>https://components.entrecode.de/entries/entry-select?e=1</example-url>
 */
@Component({
  selector: 'ec-entry-select',
  templateUrl: './entry-select.component.html',
  encapsulation: ViewEncapsulation.None,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => EntrySelectComponent),
      multi: true,
    },
  ],
})
export class EntrySelectComponent extends SelectComponent<EntryResource> implements OnChanges, OnInit {
  /** The item that is targeted by the input */
  protected item: Item<any>;
  /** The form group that is used */
  protected group: FormGroup;
  /** The form control that is used */
  protected control: FormControl;
  /** The value that should be prefilled */
  @Input() value: Array<EntryResource>;
  /** The model to pick from, alternative to field with model property set. */
  @Input() model: string;
  /** The config that is being generated. */
  public config: CrudConfig<EntryResource>;
  /** The config for the dropdown crud list */
  public dropdownConfig: CrudConfig<EntryResource>;
  /** Wether or not the selection should be solo */
  @Input() solo: boolean;
  /** The event that focuses the input */
  @Input() focusEvent: EventEmitter<boolean> = new EventEmitter();
  /** The config that should be merged into the generated config */
  // tslint:disable-next-line:no-input-rename
  @Input('config') crudConfig: CrudConfig<EntryResource>;
  /** The dropdown pop with the list to select from */
  @ViewChild('dropdown') dropdown: PopComponent;
  /** The nested entry pop */
  @ViewChild(EntryPopComponent) entryPop: EntryPopComponent;
  /** The nested entry list pop */
  @ViewChild(EntryListPopComponent, { static: true }) entryListPop: EntryListPopComponent;
  /** The nested full EntryListComponent */
  @ViewChild('dropdownList') dropdownList: any;
  /** The nested searchbar */
  @ViewChild(SearchbarComponent) searchbar: SearchbarComponent;
  /** THe nested delete confirmation pop */
  @ViewChild(ResourceDeletePopComponent, { static: true }) confirmDelete: ResourceDeletePopComponent;
  /** Emits when an entry is being removed */
  @Output() remove: EventEmitter<Item<EntryResource>> = new EventEmitter();
  /** Emits when an entry is being added */
  @Output() add: EventEmitter<Item<EntryResource>> = new EventEmitter();
  /** The current lightModel (part of root response) */
  lightModel: any;
  /** Model list that is only loaded when needing to pick the model first. */
  models;
  /** Promise that resolves when the config is ready */
  ready: Promise<CrudConfig<EntryResource>>;

  constructor(
    private modelConfig: ModelConfigService,
    public resourceService: ResourceService,
    public symbol: SymbolService,
    public sdk: SdkService,
    public elementRef: ElementRef,
    private auth: AuthService,
    public cdr: ChangeDetectorRef,
  ) {
    super(elementRef, cdr);
  }

  removeItem(item, skipDelete, e?) {
    super.removeItem(item, e);
    if (!skipDelete && this.config.deleteOnRemove) {
      if (this.config.safeDelete) {
        this.confirmDelete.confirm(item.getBody());
      } else {
        item.getBody().delete();
      }
    }
    if (e) {
      e.preventDefault();
      e.stopPropagation();
    }
  }

  togglePop(e?, noFocus = false) {
    if (this.disabled) {
      if (!this.selection.isEmpty()) {
        this.editItem(this.selection.display[0], e);
      }
      return;
    }
    this.ready.then(() => {
      if (this.dropdown) {
        this.dropdown.show(e);
      } else if (this.entryListPop && !this.config.disableListPop) {
        this.entryListPop.show(e);
      } else if (this.entryPop && !this.config.disableCreatePop) {
        this.entryPop.show();
      }
      if (this.searchbar && !noFocus) {
        this.focusEvent.emit(true);
      }
    });
  }

  defaultPlaceholder() {
    if (this.config && this.config.disableSearchbar && this.config.disableListPop) {
      return this.symbol.resolve('entry.select.placeholder.new');
    }
    return this.symbol.resolve('entry.select.placeholder.select');
  }

  /** Calls super.useConfig and then creates special dropdownConfig with just entryTitle as field  */
  useConfig(config: CrudConfig<EntryResource> = {}): Promise<CrudConfig<EntryResource>> {
    super.useConfig(config);
    this.dropdownConfig = Object.assign({}, this.config, {
      disableHeader: true,
      fields: this.config.dropdownFields || {
        [this.config.label]: Object.assign({}, (this.config.fields || {})[this.config.label]),
        _modified: { hideInList: true },
      },
    });
    return this.auth.getAllowedModelMethods(this.model, this.config.methods).then((methods) => {
      this.cdr.markForCheck();
      this.config.methods = methods;
      return this.config;
    });
  }

  /** Returns true if the given method is part of the methods array (or if there is no methods array) */
  public hasMethod(method: string) {
    return this.config && this.config.methods && this.config.methods.indexOf(method) !== -1;
  }

  useModel(model) {
    this.model = model;
    this.modelConfig.getLightModel(model).then((lightModel) => (this.lightModel = lightModel));
    this.initConfig();
  }

  /** Generates the config and sets up form control */
  initConfig() {
    if (!this.formControl) {
      this.formControl = new FormControl(this.value || []);
    }
    if (this.config) {
      this.ready = this.useConfig(this.config);
      return;
    }
    if (!this.model && this.sdk.api) {
      this.sdk.api.modelList().then((modelList) => {
        this.models = Object.keys(modelList).map((model) => modelList[model]);
      });
      return;
    }
    this.modelConfig.getLightModel(this.model).then((model) => (this.lightModel = model));

    this.ready = this.modelConfig
      .generateConfig(this.model) // , (this.config || {}).fields
      .then((config) => {
        this.config = Object.assign(config, this.crudConfig, {
          solo: this.solo,
          selectMode: false,
          disableSelectSwitch: true,
        });
        return this.useConfig(this.config);
      });
  }

  /** Fires initConfig */
  ngOnInit() {
    this.initConfig();
    this.focusEvent.subscribe((focus) => {
      if (focus) {
        this.togglePop(null, true);
      }
    });
  }

  /** Fires initConfig */
  ngOnChanges() {
    this.initConfig();
  }

  /** Is called when a selected item has been clicked. */
  editItem(item: Item<EntryResource>, e) {
    if (!this.hasMethod('put')) {
      return;
    }
    e.preventDefault();
    e.stopPropagation();
    item
      .getBody()
      .resolve()
      .then((entry) => {
        this.entryPop.edit(entry);
      });
  }
  /** Is called when the nested entry-form has been saved. Selects the fresh entry and clears the form */
  formSubmitted(form: Form<EntryResource>) {
    if (!this.selection.has(form)) {
      this.toggleItem.next(form);
    } else {
      // already in selection => update body
      const index = this.selection.index(form);
      this.selection.items[index].body = form.getBody();
    }
  }

  onChange() {
    super.onChange();
    if (this.hasSoloSelection() && this.entryListPop) {
      this.entryListPop.hide();
      return;
    }
  }

  focusSearchbar() {
    if (!this.entryListPop || !this.entryListPop.active) {
      this.focusEvent.emit(true);
    }
  }

  pasteValue(e) {
    const value = e.clipboardData.getData('text');
    if (this.config.identifierPattern && value.match(this.config.identifierPattern)) {
      this.preventDefault(e);
      this.sdk.api
        .entry(this.model, value)
        .then((entry) => this.addItem(new Item(entry, this.config)))
        .catch((error) => this.searchbar.filterList(value));
    }
  }

  filterDropdownList(list, query) {
    if (list) {
      this.dropdown.show();
      list.filter(this.lightModel.titleField, query);
    }
  }
}
<div *ngIf="!model">
  <select class="input" (change)="useModel($event.target.value)">
    <option>{{'entry.select.placeholder.model'|symbol}}</option>
    <option *ngFor="let model of models" [value]="model.title">
      {{model.title}}
    </option>
  </select>
</div>

<div class="ec-entry-select">
  <div class="ec-select" [class.ec-select_solo]="solo" [class.is-empty]="selection?.isEmpty()" [class.has-searchbar]="!config?.disableSearchbar" (click)="togglePop($event)" >
    <ul class="ec-select-selection" dndDropzone (dndDrop)="onDrop($event)">
      <li *ngIf="config?.disableSearchbar&&selection.isEmpty()">
        <span class="ec-select__placeholder">
          {{config?.placeholder||placeholder||defaultPlaceholder()}}
        </span>
      </li>
      <li [dndDisableIf]="solo||config?.disableDrag" [class.is-dragged]="selected===dragged" [class.is-draggable]="solo||!config?.disableDrag"
        dndEffectAllowed="move" [dndDraggable]="i" *ngFor="let selected of selection?.display;let i = index" (dndStart)="onDragStart(selected,$event)"
        (dndCanceled)="cancelDrag(selected,$event)" (dndEnd)="cancelDrag(selected,$event)">
        <span [class.ec-select-selected]="!solo" [class.ec-select-selected_solo]="solo" (click)="!solo&&editItem(selected,$event)">
          {{selected.display()}}
          <a (click)="removeItem(selected,false,$event)" *ngIf="!config?.disableRemove&&!disabled">&times;</a>
        </span>
      </li>
      <li dndPlaceholderRef>
        <div class="drag-placeholder" style="width: 48px;"></div>
      </li>
      <li *ngIf="!config?.disableSearchbar" class="ec-select__searchbar">
        <ec-searchbar [disabled]="disabled" [focusEvent]="focusEvent" [autofocus]="false" [property]="lightModel?.titleField" [placeholder]="placeholder||defaultPlaceholder()"
        (keypressed)="handleKey($event,dropdownList)" (focus)="focus($event)" (pasted)="pasteValue($event)" (queryChanged)="filterDropdownList(dropdownList,$event)"></ec-searchbar>
      </li>
    </ul>
    <ec-pop *ngIf="!config?.disableSearchbar" class="ec-select-options" #dropdown [hideOnClickOutside]="true">
      <ec-loader class="ec-loader loader is-local" #dropdownLoader></ec-loader>
      <ec-entry-list #dropdownList [selection]="selection" *ngIf="dropdown.activated" [model]="model" [config]="dropdownConfig"
        [loader]="dropdownLoader" (changed)="searchbar.updatedList($event)" (columnClicked)="listItemClicked($event,dropdownList)"></ec-entry-list>
    </ec-pop>
  </div>
  <!-- Fully fledged entryListPop -->
  <ec-entry-list-pop (columnClicked)="listItemClicked($event)" [model]="model" class="ec-entry-list-pop" [config]="config"
    [selection]="selection" #entryListPop></ec-entry-list-pop>
  <ec-resource-delete-pop [question]="'entry.select.delete.question' | symbol" #confirmDelete></ec-resource-delete-pop>

  <ec-entry-pop #entryPop *ngIf="config" [model]="model" [config]="config" (submitted)="formSubmitted($event)"
    (deleted)="removeItem($event,true)"></ec-entry-pop>

  <nav class="ec-entry-select__controls">
    <a (click)="editItem(selection?.display[0],$event)" class="btn btn_clear btn_square" *ngIf="solo&&!config?.disableCreatePop&&hasMethod('put')&&!selection?.isEmpty()">
      <ec-icon name="edit"></ec-icon>
    </a>
    <a (click)="entryPop.create()" class="btn btn_clear btn_square" *ngIf="!disabled&&!config?.disableCreatePop&&hasMethod('post')&&(!solo||selection?.isEmpty())">
      <ec-icon name="add"></ec-icon>
    </a>
    <a (click)="entryListPop.show($event)" class="btn btn_clear btn_square" *ngIf="!config?.disableListPop&&hasMethod('get')&&!disabled">
      <ec-icon name="search"></ec-icon>
    </a>
  </nav>
</div>
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""