SUMMARY
- Given below are code snippets of a directive called
appSearchbox and its usage in HTML.
- The
input is 2-way binded using ruleSearchText.
- The requirement for this directive is to dynamically add a 'X' mark to its end when there is a text, and when clicked it should clear the text in the input box.
QUESTION / PROBLEM
- How to clear the
ruleSearchText variable value from the directive, so that the change gets triggered back again in ngOnChanges
- The function
clearInputField clears the value, but it doesnt trigger ngOnchanges and also any other events that listens for the value change in the underlying component.
CODE EXAMPLE
<input
#search
name="search"
type="text"
class="form-control underlined"
placeholder="Search Rule Name"
[(ngModel)]="ruleSearchText"
[appSearchbox]="ruleSearchText"
/>
import {
Directive,
ElementRef,
Input,
OnChanges,
OnDestroy,
Renderer2
} from '@angular/core';
/**
* Directive to add CANCEL button functionality for input boxes.
* Clicking cancel button will clear input box value.
*
* @export
* @class SearchboxDirective
* @implements {OnChanges}
* @implements {OnDestroy}
*/
@Directive({
selector: '[appSearchbox]',
})
export class SearchboxDirective implements OnChanges, OnDestroy {
/**
* Use inputs variable that holds value to bind with attribute.
*
* @example <input name="search" type="text" [(ngModel)]="searchTextVar" [appSearchbox]="searchTextVar"/>
*
* @memberof SearchboxDirective
*/
@Input() public appSearchbox;
public cancelContainer;
public cancelStyle;
public unlistener;
/**
* Creates an instance of SearchboxDirective.
* @constructor
* @param {ElementRef} el
* @param {Renderer2} renderer
* @memberof SearchboxDirective
*/
public constructor(private el: ElementRef, private renderer: Renderer2) {
this.cancelStyle = {
top: '50%',
right: '0.5rem',
transform: 'translateY(-50%)',
cursor: 'pointer',
};
}
/**
* Triggers when to ADD/REMOVE cancel icon to inputbox.
*
* @param {*} changes
* @memberof SearchboxDirective
*/
public ngOnChanges(changes) {
if (
changes.appSearchbox.currentValue === '' ||
changes.appSearchbox.currentValue === undefined
) {
this.toggleCancelIcon(false);
} else if (changes.appSearchbox.currentValue !== '') {
this.toggleCancelIcon(true);
}
}
/**
* Removes/cleans CLICK event listener of cancel icon.
*
* @memberof SearchboxDirective
*/
public ngOnDestroy() {
if (this.unlistener) {
this.unlistener();
}
}
/**
* Show / Hide cancel icon.
*
* @param {true|false} state
* @memberof SearchboxDirective
*/
private toggleCancelIcon(state) {
if (state === true) {
const cancelBtn = this.renderer
.parentNode(this.el.nativeElement)
.querySelector('#inputCancelBtn');
if (!cancelBtn) {
this.cancelStyle['height'] = `${this.el.nativeElement.clientHeight}px`;
this.renderer.setStyle(
this.el.nativeElement,
'padding-right',
'2.5rem'
);
this.cancelContainer = this.renderer.createElement('div');
this.renderer.setAttribute(
this.cancelContainer,
'id',
'inputCancelBtn'
);
this.renderer.setAttribute(
this.cancelContainer,
'class',
'd-inline-block position-absolute'
);
this.unlistener = this.renderer.listen(
this.cancelContainer,
'click',
() => {
this.cleatInputField();
}
);
this.setRendererStyles(this.cancelContainer, this.cancelStyle);
const cancelButton = this.renderer.createElement('span');
this.renderer.setAttribute(
cancelButton,
'class',
'icon icon-close-thin'
);
this.renderer.appendChild(this.cancelContainer, cancelButton);
this.renderer.appendChild(
this.renderer.parentNode(this.el.nativeElement),
this.cancelContainer
);
}
} else if (state === false) {
const cancelBtn = this.renderer
.parentNode(this.el.nativeElement)
.querySelector('#inputCancelBtn');
if (cancelBtn) {
this.renderer.removeChild(
this.renderer.parentNode(this.el.nativeElement),
cancelBtn
);
this.renderer.setStyle(
this.el.nativeElement,
'padding-right',
'init'
);
}
}
}
/**
* Loop through styles object and set given elements styles using Renderer2 setStyle method.
*
* @param {*} element
* @param {Object} styles
* @memberof SearchboxDirective
*/
private setRendererStyles(element, styles) {
for (const [key, value] of Object.entries(styles)) {
this.renderer.setStyle(element, key, value);
}
}
private clearInputField() {
this.el.nativeElement.value = '';
this.toggleCancelIcon(false);
}
}
EXAMPLE

You can use
outputin the directive to pass the 'X' event form directive to the component like this:In the directive(appSearchbox) add this method on clearing text and the output decorator:
In the parent component add this: