Project

General

Profile

1
import {Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild} from "@angular/core";
2
import {AbstractControl, FormArray, FormControl} from "@angular/forms";
3
import {HelperFunctions} from "../../utils/HelperFunctions.class";
4
import {Observable, of, Subscription} from "rxjs";
5
import {MatSelect} from "@angular/material/select";
6
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
7
import {map, startWith} from "rxjs/operators";
8

    
9

    
10
export interface Option {
11
  icon?: string,
12
  iconClass?: string,
13
  value: any,
14
  label: string
15
}
16

    
17
@Component({
18
  selector: '[dashboard-input]',
19
  template: `
20
    <div *ngIf="label && type != 'checkbox'"
21
         class="uk-text-bold uk-form-label uk-margin-small-bottom">{{label + (required ? ' *' : '')}}</div>
22
    <div *ngIf="hint" class="uk-margin-bottom uk-text-small uk-form-hint">{{hint}}</div>
23
    <div class="uk-grid uk-flex uk-flex-middle" [class.uk-grid-small]="gridSmall" uk-grid>
24
      <ng-content></ng-content>
25
      <div [class.uk-hidden]="hideControl" class="uk-width-expand uk-position-relative"
26
           [class.uk-flex-first]="!extraLeft">
27
        <ng-template [ngIf]="icon && formControl.enabled">
28
          <span class="uk-text-muted" [ngClass]="iconLeft?('left'):'right'">
29
            <icon [name]="icon"></icon>
30
          </span>
31
        </ng-template>
32
        <ng-template [ngIf]="formControl.disabled">
33
          <span class="uk-text-muted left">
34
            <icon [name]="'lock'"></icon>
35
          </span>
36
        </ng-template>
37
        <ng-template [ngIf]="type === 'text'">
38
          <input class="uk-input input-box uk-text-small"
39
                 [attr.uk-tooltip]="formControl.disabled?'title: This field is not editable; pos: bottom-left':''"
40
                 [placeholder]="placeholder" [formControl]="formControl"
41
                 [class.uk-form-danger]="formControl.invalid && formControl.touched">
42
        </ng-template>
43
        <ng-template [ngIf]="type === 'textarea'">
44
          <textarea class="uk-textarea input-box uk-text-small"
45
                    [attr.uk-tooltip]="formControl.disabled?'title: This field is not editable; pos: bottom-left':''"
46
                    [rows]="rows" [placeholder]="placeholder"
47
                    [formControl]="formControl" [class.uk-form-danger]="formControl.invalid && formControl.touched">
48
          </textarea>
49
        </ng-template>
50
        <ng-template [ngIf]="type === 'select'">
51
          <div class="input-box"
52
               [attr.uk-tooltip]="formControl.disabled?'title: This field is not editable; pos: bottom-left':null"
53
               [class.clickable]="formControl.enabled"
54
               [class.uk-form-danger]="formControl.invalid && formControl.touched" (click)="openSelect()">
55
            <mat-form-field class="uk-width-1-1">
56
              <mat-select #select [required]="required" [value]="null"
57
                          (openedChange)="stopPropagation()" [formControl]="formControl"
58
                          [disableOptionCentering]="true">
59
                <mat-option *ngIf="placeholder" class="uk-hidden" [value]="''">{{placeholder}}</mat-option>
60
                <mat-option *ngFor="let option of options" [value]="option.value">
61
                  {{option.label}}
62
                </mat-option>
63
              </mat-select>
64
            </mat-form-field>
65
          </div>
66
        </ng-template>
67
        <ng-template [ngIf]="type === 'chips'">
68
          <div class="input-box"
69
               [attr.uk-tooltip]="formControl.disabled?'title: This field is not editable; pos: bottom-left':null"
70
               [class.clickable]="formControl.enabled"
71
               [class.uk-form-danger]="formControl.invalid && formControl.touched" (click)="openSelect()">
72
            <mat-form-field class="uk-width-1-1">
73
              <mat-chip-list #chipList aria-label="Page selection">
74
                <mat-chip *ngFor="let chip of formAsArray.controls; let i=index"
75
                          (removed)="removed(i)"
76
                          [removable]="removable">
77
                  {{chip.value[chipLabel]}}
78
                  <span (click)="removed(i)"
79
                        class="mat-chip-remove mat-chip-trailing-icon" uk-icon="close"></span>
80
                </mat-chip>
81
                <div class="uk-width-expand uk-position-relative uk-text-small">
82
                  <input #searchInput class="uk-width-1-1" [formControl]="searchControl" [matAutocomplete]="auto" [matChipInputFor]="chipList">
83
                  <div *ngIf="placeholder && !searchControl.value" class="placeholder uk-width-1-1"
84
                       (click)="searchInput.focus()">{{placeholder}}</div>
85
                </div>
86
              </mat-chip-list>
87
              <mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
88
                <mat-option *ngFor="let option of filteredOptions | async" [value]="option.value">
89
                  {{option.label}}
90
                </mat-option>
91
              </mat-autocomplete>
92
            </mat-form-field>
93
          </div>
94
        </ng-template>
95
        <span *ngIf="formControl.invalid && formControl.errors.error"
96
              class="uk-text-danger input-message">{{formControl.errors.error}}</span>
97
        <span *ngIf="warning" class="uk-text-warning input-message">{{warning}}</span>
98
        <span *ngIf="note" class="input-message">{{note}}</span>
99
      </div>
100
    </div>
101
    <mat-checkbox *ngIf="type === 'checkbox'" [formControl]="formControl">{{label}}</mat-checkbox>
102
  `,
103
  styleUrls: ['input.component.css']
104
})
105
export class InputComponent implements OnInit, OnDestroy, OnChanges {
106
  @Input('formInput') formControl: AbstractControl;
107
  @Input('type') type: 'text' | 'textarea' | 'select' | 'checkbox' | 'chips' = 'text';
108
  @Input('label') label: string;
109
  @Input('rows') rows: number = 3;
110
  @Input('options') options: Option[];
111
  @Input('hint') hint = null;
112
  @Input('placeholder') placeholder = '';
113
  @ViewChild('select') select: MatSelect;
114
  @Input() extraLeft: boolean = true;
115
  @Input() gridSmall: boolean = false;
116
  @Input() hideControl: boolean = false;
117
  @Input() icon: string = null;
118
  @Input() iconLeft: boolean = false;
119
  @Input() warning: string = null;
120
  @Input() note: string = null;
121
  @Input() removable: boolean = true;
122
  @Input() chipLabel: string = null;
123
  public filteredOptions: Observable<Option[]>;
124
  public searchControl: FormControl;
125
  public required: boolean = false;
126
  private initValue: any;
127
  private subscriptions: any[] = [];
128
  @ViewChild('searchInput') searchInput;
129
  
130
  constructor() {
131
  }
132
  
133
  ngOnInit(): void {
134
    this.reset();
135
  }
136
  
137
  ngOnChanges(changes: SimpleChanges) {
138
    if(changes.formControl) {
139
      this.reset();
140
    }
141
  }
142
  
143
  get formAsArray(): FormArray {
144
    return (<FormArray> this.formControl);
145
  }
146
  
147
  reset() {
148
    this.unsubscribe();
149
    this.initValue = HelperFunctions.copy(this.formControl.value);
150
    if(this.options && this.type === 'chips') {
151
      this.filteredOptions = of(this.options);
152
      this.searchControl = new FormControl('');
153
      this.filteredOptions = this.searchControl.valueChanges.pipe(startWith(''),
154
        map(option => this.filter(option)));
155
    }
156
    if (this.formControl && this.formControl.validator) {
157
      let validator = this.formControl.validator({} as AbstractControl);
158
      this.required = (validator && validator.required);
159
    }
160
    this.subscriptions.push(this.formControl.valueChanges.subscribe(value => {
161
      value = (value === '')?null:value;
162
      if(this.initValue === value || (this.initValue === '' && value === null)) {
163
        this.formControl.markAsPristine();
164
      }
165
    }));
166
    if (!this.formControl.value) {
167
      this.formControl.setValue('');
168
    }
169
  }
170
  
171
  unsubscribe() {
172
    this.subscriptions.forEach(subscription => {
173
      if(subscription instanceof Subscription) {
174
        subscription.unsubscribe();
175
      }
176
    });
177
  }
178
  
179
  openSelect() {
180
    if(this.select) {
181
      this.select.open();
182
    }
183
  }
184
  
185
  ngOnDestroy(): void {
186
    this.unsubscribe();
187
  }
188
  
189
  stopPropagation() {
190
    event.stopPropagation();
191
  }
192
  
193
  removed(index: number) {
194
    this.formAsArray.removeAt(index);
195
    this.formAsArray.markAsDirty();
196
  }
197
  
198
  selected(event: MatAutocompleteSelectedEvent): void {
199
    this.formAsArray.push(new FormControl(event.option.value));
200
    this.formAsArray.markAsDirty();
201
    this.searchControl.setValue('');
202
    this.searchInput.nativeElement.value = '';
203
  }
204
  
205
  private filter(value: string): Option[] {
206
    let options = this.options.filter(option => !this.formAsArray.value.find(value => option.label === value[this.chipLabel]));
207
    if (!value || value.length == 0) {
208
      return options;
209
    }
210
    const filterValue = value.toString().toLowerCase();
211
    return options.filter(option => option.label.toLowerCase().indexOf(filterValue) != -1);
212
  }
213
}
(2-2/3)