如何在 ANGULAR 8 中双向绑定自定义指令?
How to 2-way bind custom directive in ANGULAR 8?
摘要
- 下面给出了一个名为
appSearchbox
的指令的代码片段及其在 HTML 中的用法。
input
使用 ruleSearchText
进行双向绑定。
- 这个指令的要求是当有文本时动态地在它的末尾添加一个'X'标记,当点击它应该清除输入框中的文本。
问题/问题
- 如何从指令中清除
ruleSearchText
变量值,以便在 ngOnChanges
中再次触发更改
- 函数
clearInputField
清除值,但它不会触发 ngOnchanges
以及侦听基础组件中值更改的任何其他事件。
代码示例
<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);
}
}
例子
您可以在指令中使用 output
将 'X' 事件表单指令传递给组件,如下所示:
<input
#search
name="search"
type="text"
class="form-control underlined"
placeholder="Search Rule Name"
[(ngModel)]="ruleSearchText"
[appSearchbox]="ruleSearchText"
(clearSearch)="clearText($event)"
/>
在指令 (appSearchbox) 中添加此方法以清除文本和输出装饰器:
@Output() clearSearch = new EventEmitter();
..
..
..
clear() {
this.clearSearch.emit(true);
}
在父组件中添加:
clearText(status) {
this.ruleSearchText = status ? '' : this.ruleSearchText;
}
摘要
- 下面给出了一个名为
appSearchbox
的指令的代码片段及其在 HTML 中的用法。 input
使用ruleSearchText
进行双向绑定。- 这个指令的要求是当有文本时动态地在它的末尾添加一个'X'标记,当点击它应该清除输入框中的文本。
问题/问题
- 如何从指令中清除
ruleSearchText
变量值,以便在ngOnChanges
中再次触发更改
- 函数
clearInputField
清除值,但它不会触发ngOnchanges
以及侦听基础组件中值更改的任何其他事件。
代码示例
<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);
}
}
例子
您可以在指令中使用 output
将 'X' 事件表单指令传递给组件,如下所示:
<input
#search
name="search"
type="text"
class="form-control underlined"
placeholder="Search Rule Name"
[(ngModel)]="ruleSearchText"
[appSearchbox]="ruleSearchText"
(clearSearch)="clearText($event)"
/>
在指令 (appSearchbox) 中添加此方法以清除文本和输出装饰器:
@Output() clearSearch = new EventEmitter();
..
..
..
clear() {
this.clearSearch.emit(true);
}
在父组件中添加:
clearText(status) {
this.ruleSearchText = status ? '' : this.ruleSearchText;
}