nm-search
src/app/shared/widgets/buy/search/search.component.ts
AfterViewInit
OnInit
OnDestroy
changeDetection | ChangeDetectionStrategy.OnPush |
selector | nm-search |
styleUrls | search.component.scss |
templateUrl | ./search.component.html |
constructor(_widgetframeService: WidgetframeService, formBuilder: FormBuilder, _listService: ListService, localStorageService: LocalStorageService, _changeDetectorRef: ChangeDetectorRef)
|
||||||||||||||||||
Parameters :
|
Protected configureWidget | ||||||
configureWidget(configuration: WidgetConfig)
|
||||||
Decorators : WidgetConfigure
|
||||||
Parameters :
Returns :
void
|
doFreetextSearch | ||||
doFreetextSearch(value: )
|
||||
Parameters :
Returns :
void
|
doListSearch | ||||
doListSearch(value: )
|
||||
Parameters :
Returns :
void
|
doSearch | ||||
doSearch(value: )
|
||||
Parameters :
Returns :
void
|
keyDownFunction | ||||
keyDownFunction(event: )
|
||||
Parameters :
Returns :
void
|
ngAfterViewChecked |
ngAfterViewChecked()
|
Returns :
void
|
ngAfterViewInit |
ngAfterViewInit()
|
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
Public ngOnInit |
ngOnInit()
|
Returns :
void
|
onChange | ||||
onChange(event: )
|
||||
Parameters :
Returns :
void
|
onListValueChange | ||||
onListValueChange(value: )
|
||||
Parameters :
Returns :
void
|
resetFromLocalStorage | ||||||
resetFromLocalStorage(entry: LocalStorageEntry)
|
||||||
Parameters :
Returns :
void
|
setSearchTerm | ||||
setSearchTerm(value: )
|
||||
Parameters :
Returns :
void
|
Private switchToFreetextSearch |
switchToFreetextSearch()
|
Returns :
void
|
Private switchToListSearch |
switchToListSearch()
|
Returns :
void
|
toggleMode | ||||
toggleMode($event: )
|
||||
Parameters :
Returns :
void
|
Private updateTextField | ||||||
updateTextField(value: string)
|
||||||
Parameters :
Returns :
void
|
Public _id |
_id:
|
Type : string
|
Decorators : WidgetId
|
Private attributes |
attributes:
|
Type : any
|
Public autofocus |
autofocus:
|
Type : boolean
|
Default value : false
|
Private clearChannel |
clearChannel:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetInput
|
Clears search term |
Public cols |
cols:
|
Type : any[]
|
Public configuration |
configuration:
|
Type : WidgetConfig<SearchWidgetConfiguration>
|
Decorators : WidgetConfiguration
|
Public disableSearchOnEmptyField |
disableSearchOnEmptyField:
|
Type : boolean
|
Default value : false
|
Public enableListsearch |
enableListsearch:
|
Type : boolean
|
Public enableListSearchOnly |
enableListSearchOnly:
|
Type : boolean
|
Public enableTemplating |
enableTemplating:
|
Type : boolean
|
Default value : true
|
Private encodeLink |
encodeLink:
|
Type : boolean
|
Default value : true
|
Public freetextSearchOnButton |
freetextSearchOnButton:
|
Type : boolean
|
Default value : false
|
Public freetextSearchOnEnter |
freetextSearchOnEnter:
|
Type : boolean
|
Default value : true
|
Public hide |
hide:
|
Type : boolean
|
Default value : false
|
Private hideChannel |
hideChannel:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetInput
|
Hides the search widget |
Public infotext |
infotext:
|
Type : string
|
Private inputChannel |
inputChannel:
|
Type : Subject<any>
|
Default value : new BehaviorSubject<any>(null)
|
Decorators : WidgetInput
|
Sets the value of the search-field and performs a search |
inputComponent |
inputComponent:
|
Decorators : ViewChild
|
Public inputLink |
inputLink:
|
Type : string
|
Public inputLinklistSearch |
inputLinklistSearch:
|
Type : string
|
Public listsearch |
listsearch:
|
Type : boolean
|
Default value : false
|
Private localStorageIsListSearchEntry |
localStorageIsListSearchEntry:
|
Type : LocalStorageEntry
|
Private localStorageListSearchEntry |
localStorageListSearchEntry:
|
Type : LocalStorageEntry
|
Private localStorageSearchEntry |
localStorageSearchEntry:
|
Type : LocalStorageEntry
|
Public placeholderkey |
placeholderkey:
|
Type : string
|
Private profile |
profile:
|
Type : Subject<any>
|
Default value : new ReplaySubject<any>(1)
|
Decorators : WidgetOutput
|
Deprecated |
Public removeListSearchButton |
removeListSearchButton:
|
Type : boolean
|
Private resetChannel |
resetChannel:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetInput
|
Resets the search |
Private saveSearchTerm |
saveSearchTerm:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetInput
|
Triggers saving the search input in the local storage |
Public searchform |
searchform:
|
Type : FormGroup
|
Private searchTerm |
searchTerm:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetOutput
|
Emits the search term everytime it changes |
Public startListSearchLabel |
startListSearchLabel:
|
Type : string
|
Private storageValue |
storageValue:
|
Type : Subject<any>
|
Default value : new ReplaySubject<any>(1)
|
Private sub |
sub:
|
Public subheader |
subheader:
|
Type : string
|
Public title |
title:
|
Type : string
|
Private unsubscribe |
unsubscribe:
|
Default value : NgUnsubscribe.create()
|
Public wikiLink |
wikiLink:
|
Type : string
|
Public withHeader |
withHeader:
|
Type : boolean
|
Default value : true
|
import {
combineLatest as observableCombineLatest,
Subject,
Observable,
ReplaySubject,
BehaviorSubject,
} from "rxjs";
import { distinctUntilChanged, filter, takeUntil } from "rxjs/operators";
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnInit,
ViewChild,
OnDestroy,
} from "@angular/core";
import { WidgetframeService } from "../../widgetframe/widgetframe.service";
import { WidgetConfig } from "../../widget.configuration";
import {
WidgetComponent,
WidgetConfiguration,
WidgetConfigure,
WidgetId,
WidgetInput,
WidgetOutput,
} from "../../widget.metadata";
import { FormBuilder, FormGroup } from "@angular/forms";
import { ListService } from "../../list/index";
import * as uriTemplates_ from "uri-templates";
import {
DeletionMode,
Scope,
} from "../../../components/local-storage/local-storage-constants";
import {
LocalStorageEntry,
LocalStorageService,
} from "../../../components/local-storage/local-storage.service";
import { getOrDefault } from "../../widget.configuration";
import { NgUnsubscribe } from "../../../ng-unsubscribe";
const uriTemplates = uriTemplates_;
export interface SearchWidgetConfiguration {
/** Enables/Disables the list search option */
enableListsearch?: boolean;
/** Enables/Disables list search */
enableListSearchOnly?: boolean;
/** Enables/Disables searching with empty search term */
disableSearchOnEmptyField?: boolean;
/** Enables/Disables using a template for the search and it's required for list search */
enableTemplating?: boolean;
/** Specifies whether the widget should show the header or not @default(true) */
withHeader?: boolean;
/** Sets the link to the wiki documents to further explain the functionality */
wikiLink?: string;
/** Sets link used for templating */
links?: any[];
/** Sets local storage key name for saving the search term */
"localstorage-search"?: string;
/** Sets the scope of the widget to differenciate between local storage keys */
scope?: string;
/** Sets local storage key name for the list search for saving the search term */
"localstorage-listsearch"?: string;
/** Sets the placeholder key for the search field @default(elasticsearch.search.paceholder) */
placeholderkey?: string;
/** Enables freetext searching on enter clicked @default(true) */
"freetext-search-on-enter"?: boolean;
/** Enables list searching on input change @default(true) */
"list-search-on-input"?: boolean;
/** Enables freetext searching on button clicked @default(false) */
"freetext-search-on-button"?: boolean;
/** Sets subheader */
subheader?: string;
/** Sets label for the button starting the list search @default(searchlist.start) */
startListSearchLabel?: string;
/** Sets label for the button removing the list search @default(false) */
removeListSearchButton?: boolean;
/** Enables setting the search term parameter to 'searchValue' instead of the 'q' default */
searchValue?: boolean;
/** Sets info text to explain the widget freetext search functionality */
infoText?: string;
/** Sets title of the widget header in the freetext mode */
title?: string;
/** Sets info text to explain the widget list search functionality */
"infoText-listsearch"?: string;
/** Sets title of the widget header in the list search mode */
"title-listsearch"?: string;
/** Shows/Hides the widget frame border */
withBorder?: boolean;
/** Specifies the widget frame header type (default: primary) */
header?: string;
/** Specifies the widget frame width */
width?: string;
/** Auto focus field. */
autofocus?: boolean;
}
@WidgetComponent("nm-search")
@Component({
selector: "nm-search",
templateUrl: "./search.component.html",
styleUrls: ["./search.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SearchWidgetComponent implements AfterViewInit, OnInit, OnDestroy {
public cols: any[];
private attributes: any;
public inputLink: string;
public wikiLink: string;
public infotext: string;
public title: string;
public subheader: string;
public inputLinklistSearch: string;
public enableListsearch: boolean;
public enableListSearchOnly: boolean;
public enableTemplating: boolean = true;
public withHeader: boolean = true;
public freetextSearchOnEnter: boolean = true;
public freetextSearchOnButton: boolean = false;
public placeholderkey: string;
public disableSearchOnEmptyField: boolean = false;
public autofocus: boolean = false;
private encodeLink: boolean = true;
public removeListSearchButton: boolean;
public startListSearchLabel: string;
private localStorageIsListSearchEntry: LocalStorageEntry;
private localStorageSearchEntry: LocalStorageEntry;
private localStorageListSearchEntry: LocalStorageEntry;
public hide: boolean = false;
private unsubscribe = NgUnsubscribe.create();
@WidgetConfiguration()
public configuration: WidgetConfig<SearchWidgetConfiguration>;
/**
* If templating is enabled emits the url that can be used to fetch the list-search data (if in list-search mode), else uses inputLink and fills it with the current search
*/
@WidgetOutput("uri")
private uri: Subject<any> = new ReplaySubject<any>(1);
/**
* Emits the user input if templating is disabled and search mode is list
*/
@WidgetOutput("listSearchOutput")
private listSearchOutput: Subject<any> = new ReplaySubject<any>(1);
/**
* Emits the current search mode ('freeTextSearch' or 'listSearch')
*/
@WidgetOutput("modeischanged")
private modeIsChanged: Subject<any> = new ReplaySubject<any>(1);
/**
* Deprecated
*/
@WidgetOutput("profile")
private profile: Subject<any> = new ReplaySubject<any>(1);
/**
* Resets the search
*/
@WidgetInput("reset")
private resetChannel: Subject<any> = new Subject<any>();
/**
* Hides the search widget
*/
@WidgetInput("hide")
private hideChannel: Subject<any> = new Subject<any>();
/**
* Sets the value of the search-field and performs a search
*/
@WidgetInput("set")
private inputChannel: Subject<any> = new BehaviorSubject<any>(null);
/**
* Clears search term
*/
@WidgetInput("cleartextsearch")
private clearChannel: Subject<any> = new Subject<any>();
/**
* Emits every time a search is performed (meant to force a reset of category-search, which are often used in conjunction with this)
*/
@WidgetOutput("clearcategorysearch")
private clearCategorySearch: Subject<any> = new Subject<any>();
/**
* Triggers when return is pressed on the search field (if the mode is not list-search)
*/
@WidgetOutput("returnPressed")
private returnPressed: Subject<any> = new Subject<any>();
/**
* Emits the search term everytime it changes
*/
@WidgetOutput("searchTerm")
private searchTerm: Subject<any> = new Subject<any>();
/**
* Triggers saving the search input in the local storage
*/
@WidgetInput("saveSearchTerm")
private saveSearchTerm: Subject<any> = new Subject<any>();
private storageValue: Subject<any> = new ReplaySubject<any>(1);
/**
* Emits data on list search input, if enableTemplating = true
*/
@WidgetOutput("listSearchOnChangeOutput")
private listSearchOnChangeOutput: Subject<any> = new ReplaySubject<any>(1);
@WidgetId()
public _id: string;
@ViewChild("inputComponent") inputComponent;
private sub;
public searchform: FormGroup;
public listsearch: boolean = false;
constructor(
private _widgetframeService: WidgetframeService,
private formBuilder: FormBuilder,
private _listService: ListService,
private localStorageService: LocalStorageService,
private _changeDetectorRef: ChangeDetectorRef
) {
this.searchform = this.formBuilder.group({
searchInput: "",
});
}
setSearchTerm(value) {
this.searchform.value.searchInput = value;
if (this.localStorageListSearchEntry) {
this.localStorageListSearchEntry.value = value;
}
}
keyDownFunction(event) {
if (this.freetextSearchOnEnter && !this.listsearch) {
this.doFreetextSearch(this.searchform.value.searchInput);
this.returnPressed.next(new Date());
this._changeDetectorRef.markForCheck();
}
}
onChange(event) {
this.searchTerm.next(this.searchform.value.searchInput);
if (!this.freetextSearchOnEnter && !this.freetextSearchOnButton) {
this.doFreetextSearch(this.searchform.value.searchInput);
this._changeDetectorRef.markForCheck();
}
}
doListSearch(value) {
this.clearCategorySearch.next(Date.now);
if (this.localStorageListSearchEntry) {
this.localStorageListSearchEntry.value = value;
}
this._changeDetectorRef.markForCheck();
if (this.enableTemplating && this.encodeLink) {
this._widgetframeService
.postData(this.inputLinklistSearch, value)
.subscribe((data) => {
this.uri.next(data._links.results.href);
});
} else {
this.listSearchOutput.next(value);
}
}
doFreetextSearch(value) {
this.clearCategorySearch.next(Date.now);
if (this.localStorageSearchEntry) {
this.localStorageSearchEntry.value = value;
}
if (this.enableTemplating && this.encodeLink) {
let uriParams;
if (this.configuration.configuration["searchValue"]) {
uriParams = { searchValue: value };
} else {
uriParams = { q: value };
}
if (!(this.disableSearchOnEmptyField && uriParams["q"].length === 0)) {
let link = uriTemplates(this.inputLink).fill(uriParams);
this.uri.next(link);
}
} else {
if (!(this.disableSearchOnEmptyField && value.length === 0)) {
this.uri.next(value);
}
}
this._changeDetectorRef.markForCheck();
}
toggleMode($event) {
if ($event.checked) {
this.switchToListSearch();
} else {
this.switchToFreetextSearch();
}
}
private switchToFreetextSearch() {
this.modeIsChanged.next("freeTextSearch");
this.listsearch = false;
this.localStorageIsListSearchEntry.value = "false";
this.infotext = this.configuration.configuration["infoText"];
this.title = this.configuration.configuration["title"];
this.resetFromLocalStorage(this.localStorageSearchEntry);
}
private switchToListSearch() {
this.modeIsChanged.next("listSearch");
this.listsearch = true;
this.localStorageIsListSearchEntry.value = "true";
this.infotext = this.configuration.configuration["infoText-listsearch"];
this.title = this.configuration.configuration["title-listsearch"];
this.resetFromLocalStorage(this.localStorageListSearchEntry);
}
resetFromLocalStorage(entry: LocalStorageEntry) {
if (entry) {
let value = entry.value;
this.storageValue.next(value);
this.updateTextField(value);
}
}
private updateTextField(value: string) {
this.searchform.setValue({
searchInput: value || "",
});
window.setTimeout(() => {
this.searchform.patchValue({
searchInput: value || "",
});
this._changeDetectorRef.markForCheck();
}, 1);
}
doSearch(value) {
this.updateTextField(value);
if (this.listsearch || this.enableListSearchOnly) {
this.doListSearch(value);
} else {
this.doFreetextSearch(value);
}
}
onListValueChange(value) {
this.listSearchOnChangeOutput.next(value);
if (this.localStorageListSearchEntry && this.removeListSearchButton) {
this.localStorageListSearchEntry.value = value;
}
}
@WidgetConfigure()
protected configureWidget(configuration: WidgetConfig) {
this.enableListsearch = this.configuration.configuration[
"enableListsearch"
];
this.enableListSearchOnly = this.configuration.configuration[
"enableListSearchOnly"
];
this.disableSearchOnEmptyField = this.configuration.configuration[
"disableSearchOnEmptyField"
];
if (this.configuration.configuration["enableTemplating"] != undefined) {
this.enableTemplating = this.configuration.configuration[
"enableTemplating"
];
}
if (this.configuration.configuration["withHeader"] != undefined) {
this.withHeader = this.configuration.configuration["withHeader"];
}
this.wikiLink = this.configuration.configuration["wikiLink"];
if (this.configuration.configuration["links"]) {
this.inputLink = this.configuration.configuration["links"]["search"];
this.inputLinklistSearch = this.configuration.configuration["links"][
"listSearch"
];
}
if (this.configuration.configuration["localstorage-search"]) {
this.localStorageSearchEntry = this.localStorageService.getLocalStorageEntry(
this.configuration.configuration["scope"] +
this.configuration.configuration["localstorage-search"],
Scope.GLOBAL,
DeletionMode.RESET
);
}
if (this.configuration.configuration["localstorage-listsearch"]) {
this.localStorageListSearchEntry = this.localStorageService.getLocalStorageEntry(
this.configuration.configuration["scope"] +
this.configuration.configuration["localstorage-listsearch"],
Scope.GLOBAL,
DeletionMode.RESET
);
}
this.localStorageIsListSearchEntry = this.localStorageService.getLocalStorageEntry(
this.configuration.configuration["scope"] + "nm-product-listsearch",
Scope.GLOBAL,
DeletionMode.RESET
);
this.placeholderkey = this.configuration.configuration["placeholderkey"]
? this.configuration.configuration["placeholderkey"]
: "elasticsearch.search.paceholder";
this.freetextSearchOnEnter =
this.configuration.configuration["freetext-search-on-enter"] !== undefined
? this.configuration.configuration["freetext-search-on-enter"]
: true;
this.encodeLink =
this.configuration.configuration["list-search-on-input"] !== undefined
? this.configuration.configuration["list-search-on-input"]
: true;
this.freetextSearchOnButton =
this.configuration.configuration["freetext-search-on-button"] !==
undefined
? this.configuration.configuration["freetext-search-on-button"]
: false;
if (this.freetextSearchOnButton) {
this.freetextSearchOnEnter = false;
}
this.withHeader =
this.configuration.configuration["withHeader"] !== undefined
? this.configuration.configuration["withHeader"]
: true;
this.subheader =
this.configuration.configuration["subheader"] !== undefined
? this.configuration.configuration["subheader"]
: "";
this.startListSearchLabel = getOrDefault(
this.configuration.configuration["startListSearchLabel"],
"searchlist.start"
);
this.removeListSearchButton = getOrDefault(
this.configuration.configuration["removeListSearchButton"],
false
);
this.autofocus = getOrDefault(
this.configuration.configuration["autofocus"],
false
);
if (
this.localStorageIsListSearchEntry.exists() &&
(this.enableListsearch || this.enableListSearchOnly)
) {
this.listsearch =
this.localStorageIsListSearchEntry.value === "true" ? true : false;
}
if (this.listsearch || this.enableListSearchOnly) {
this.switchToListSearch();
} else {
this.switchToFreetextSearch();
}
this.resetChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((reset) => {
this.searchform.value.searchInput = null;
if (this.localStorageSearchEntry) {
this.localStorageSearchEntry.clear();
}
if (this.localStorageListSearchEntry) {
this.localStorageListSearchEntry.clear();
}
this.doSearch("");
this._changeDetectorRef.markForCheck();
});
this.clearChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((reset) => {
this.localStorageSearchEntry.clear();
if (this.localStorageListSearchEntry) {
this.localStorageListSearchEntry.clear();
}
this.updateTextField("");
this._changeDetectorRef.markForCheck();
});
this.saveSearchTerm
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((save) => {
this.localStorageSearchEntry.value = this.searchform.value.searchInput;
});
this.hideChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((hide) => {
this.hide = hide;
});
}
public ngOnInit() {
observableCombineLatest(
this.storageValue,
this.inputChannel,
(storage, input) => {
if (input) {
return input;
}
return storage;
}
)
.pipe(
filter((value) => value),
distinctUntilChanged(),
takeUntil(this.unsubscribe)
)
.subscribe((value) => this.doSearch(value));
}
ngAfterViewInit() {
if (!this.listsearch) {
this.searchTerm.next(this.searchform.value.searchInput);
}
if (this.inputComponent && this.inputComponent.first) {
setTimeout(() => {
this.inputComponent.first.nativeElement.focus();
this._changeDetectorRef.markForCheck();
}, 1);
}
}
ngAfterViewChecked() {
this._changeDetectorRef.markForCheck();
}
ngOnDestroy() {
this.unsubscribe.destroy();
}
}
<nm-widgetframe
*ngIf="!hide"
[header]="configuration.configuration['header']"
[configuration]="configuration"
[width]="configuration.configuration['width']"
[infoTitle]="title"
[infoText]="infotext"
[infoPlacement]="'bottom'"
[wikiLink]="wikiLink"
widgetId="{{ _id }}"
[toolbarInvisible]="!withHeader"
[withBorder]="configuration.configuration.withBorder"
>
<div slot="title" class="nm-widgetframe__title" style="width: 100%">
<span> {{ title | translate }} </span>
<ng-container *ngIf="enableListsearch">
<mat-slide-toggle
pTooltip="{{ 'infotext.switchsearchmode' | translate }}"
[(ngModel)]="listsearch"
tooltipPosition="bottom"
(change)="toggleMode($event)"
></mat-slide-toggle>
</ng-container>
</div>
<div slot="content" class="nm-widgetframe__content">
<div *ngIf="subheader" class="nm-subheader">
{{ subheader | translate }}
</div>
<form
class="nm-searchForm"
[formGroup]="searchform"
(keydown.enter)="keyDownFunction($event)"
[style.width]="configuration.configuration.width"
>
<mat-form-field *ngIf="!enableListSearchOnly && !listsearch">
<input
matInput
#inputComponent
id="inputComponent"
(input)="onChange($event)"
class="nm-searchinput"
placeholder="{{ placeholderkey | translate }}"
assetType="search"
formControlName="searchInput"
[nmAutofocus]="configuration.configuration.autofocus"
/>
<span matPrefix>
<mat-icon class="mat-24">search</mat-icon>
</span>
</mat-form-field>
<div *ngIf="listsearch || enableListSearchOnly" style="margin-top: 20px">
<label class="nm-small-lable nm-textarea-label">{{
"searchlist.placeholder" | translate
}}</label>
<mat-form-field>
<textarea
matInput
#textareaComponent
*ngIf="enableTemplating"
id="textareaComponent"
style="width: 100%; min-height: 30px; height: 140px"
(input)="onListValueChange(searchform.value.searchInput)"
formControlName="searchInput"
>
</textarea>
<textarea
matInput
#textareaComponent
*ngIf="!enableTemplating"
(input)="doListSearch(searchform.value.searchInput)"
id="textareaComponent"
style="width: 100%; min-height: 30px; height: 120px"
formControlName="searchInput"
>
</textarea>
</mat-form-field>
<div class="nm-search__listSearchButtons">
<button
mat-raised-button
color="primary"
*ngIf="enableTemplating && !removeListSearchButton"
(click)="doListSearch(searchform.value.searchInput)"
class="listsearch"
>
{{ startListSearchLabel | translate }}
</button>
</div>
</div>
</form>
</div>
</nm-widgetframe>