Created
August 16, 2019 14:51
-
-
Save nettakogo87/947d6e08a18ee88876713c2ee6484fa5 to your computer and use it in GitHub Desktop.
Angular material control
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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