Skip to content

Instantly share code, notes, and snippets.

@nettakogo87
Created August 16, 2019 14:51
Show Gist options
  • Save nettakogo87/947d6e08a18ee88876713c2ee6484fa5 to your computer and use it in GitHub Desktop.
Save nettakogo87/947d6e08a18ee88876713c2ee6484fa5 to your computer and use it in GitHub Desktop.
Angular material control
<mat-chip-list
#chipList
[disabled]="itemsCtrl.disabled"
[required]="required"
>
<mat-chip
*ngFor="let selectedItem of selectedItems; let index = index"
color="primary"
selected="false"
[removable]="itemsCtrl.enabled"
(removed)="removeByIndex(index)">
<ng-template
[ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{$implicit: selectedItem}">
</ng-template>
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
<input
matInput
#itemsInput
[formControl]="itemsCtrl"
[placeholder]="placeholder"
[matAutocomplete]="autoItem"
[matChipInputFor]="chipList"
[attr.aria-label]="placeholder"
(focus)="onFocus()"
(blur)="onBlur()">
</mat-chip-list>
<mat-autocomplete
#autoItem="matAutocomplete"
(optionSelected)="select($event)">
<mat-option
*ngFor="let filteredItem of filteredItems | async"
[value]="filteredItem">
<ng-template
[ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{$implicit: filteredItem}">
</ng-template>
</mat-option>
</mat-autocomplete>
// ANGULAR
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ContentChild,
DoCheck,
ElementRef,
HostBinding,
Input,
OnChanges,
OnDestroy,
Optional,
Output,
Self,
SimpleChanges,
TemplateRef,
ViewChild,
} from '@angular/core';
import {
ControlValueAccessor,
FormControl,
FormGroupDirective,
NgControl,
NgForm,
} from '@angular/forms';
import {
ErrorStateMatcher,
MatAutocompleteSelectedEvent,
MatFormFieldControl,
} from '@angular/material';
// RXJS
import { Observable } from 'rxjs';
class ChipsAutocompleteBase {
constructor(
public _parentForm: NgForm,
public _parentFormGroup: FormGroupDirective,
public ngControl: NgControl,
public _defaultErrorStateMatcher: ErrorStateMatcher,
) {}
}
const ChipsAutocompleteComponentMixinBase =
mixinDisabled(mixinErrorState(ChipsAutocompleteBase));
@Component({
selector: 'evv-chips-autocomplete',
templateUrl: './chips-autocomplete.component.html',
styleUrls: ['./chips-autocomplete.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: MatFormFieldControl,
useExisting: ChipsAutocompleteComponent,
},
],
})
export class ChipsAutocompleteComponent<T>
extends ChipsAutocompleteComponentMixinBase
implements MatFormFieldControl<any>, ControlValueAccessor, DoCheck, OnChanges, OnDestroy {
private static _nextId = 0;
@Input()
public placeholder;
@Input()
public disabled: boolean;
@Input()
public value;
@Input()
public filteredItems: Observable<T[]>;
@Input()
public required = false;
@Input()
public multi = false;
@Output()
public searchChanged: Observable<string>;
@ViewChild('chipList')
public chipList;
@ViewChild('itemsInput')
public itemsInput: ElementRef;
@ContentChild(TemplateRef)
public templateRef: TemplateRef<T>;
@HostBinding('class.floating')
public get shouldLabelFloat(): boolean {
return this.focused || !this.empty;
}
public focused = false;
public controlType = 'evv-chips-autocomplete';
public id = `evv-chips-autocomplete-${ChipsAutocompleteComponent._nextId++}`;
public describedBy = '';
public itemsCtrl = new FormControl('');
protected _onChange = (value: any) => {};
protected _onTouched = () => {};
private _selectedItems: T[] = [];
constructor(
@Optional() @Self() public ngControl: NgControl,
@Optional() public _parentForm: NgForm,
@Optional() public _parentFormGroup: FormGroupDirective,
public _defaultErrorStateMatcher: ErrorStateMatcher,
private _changeDetectorRef: ChangeDetectorRef,
private elRef: ElementRef<HTMLElement>,
) {
super(_parentForm, _parentFormGroup, ngControl, _defaultErrorStateMatcher);
this.ngControl.valueAccessor = this;
this.searchChanged = this.itemsCtrl.valueChanges;
}
public get empty(): boolean {
return !this.itemsCtrl.value && (this._selectedItems.length === 0);
}
public get selectedItems(): T[] {
return this._selectedItems;
}
public ngOnChanges(changes: SimpleChanges): void {
if (changes) {
this.stateChanges.next();
}
}
public ngOnDestroy(): void {
this.stateChanges.complete();
}
public ngDoCheck(): void {
this.updateErrorState();
}
public onFocus(): void {
if (!this.disabled) {
this.focused = true;
}
}
public onBlur(): void {
this.focused = false;
if (!this.disabled) {
this.stateChanges.next();
this.ngControl.control.markAsTouched();
}
}
public onContainerClick(event: MouseEvent): void {
if ((event.target as Element).tagName.toLowerCase() !== 'input') {
this.elRef.nativeElement.querySelector('input')!.focus();
}
}
public setDescribedByIds(ids: string[]): void {
this.describedBy = ids.join(' ');
}
public registerOnChange(fn: any): void {
this._onChange = fn;
}
public registerOnTouched(fn: any): void {
this._onTouched = fn;
}
public setDisabledState(isDisabled: boolean): void {
if (isDisabled) {
this.itemsCtrl.disable();
} else {
this.itemsCtrl.enable();
}
this.stateChanges.next();
}
public writeValue(items: T[]): void {
this._selectedItems = items.slice();
}
public select(event: MatAutocompleteSelectedEvent): void {
if (event.option.value) {
if (!this.multi) {
this._selectedItems.length = 0;
}
this._selectedItems.push(event.option.value);
this._onChange(this._selectedItems.slice());
}
this.itemsInput.nativeElement.value = '';
this.itemsCtrl.setValue('');
}
public removeByIndex(index: number): void {
this._selectedItems.splice(index, 1);
this.itemsCtrl.setValue('');
this._onChange(this._selectedItems.slice());
}
}
<mat-form-field>
<mat-label>Select Recipients</mat-label>
<evv-chips-autocomplete
#selectRecipientsInput
class="pad-bottom-sm"
formControlName="recipients"
[filteredItems]="filteredRecipients"
[multi]="multi"
[required]="required"
(searchChanged)="recipientSearchChanged($event)">
<ng-template let-recipient>
<evv-option-recipient [recipient]="recipient">
</evv-option-recipient>
</ng-template>
</evv-chips-autocomplete>
<mat-error>
<ng-container *ngIf="multi">Recipients are required!</ng-container>
<ng-container *ngIf="!multi">Recipient is required!</ng-container>
</mat-error>
</mat-form-field>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment