Skip to content
This repository was archived by the owner on Oct 7, 2020. It is now read-only.

Commit 09dca7a

Browse files
committed
refactor(textfield): Use EventManager + refactoring
Textfield was the first Angular component I wrote for MDC, so it needed some attention..
1 parent df4443d commit 09dca7a

File tree

4 files changed

+78
-101
lines changed

4 files changed

+78
-101
lines changed

src/lib/textfield/index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
import { NgModule } from '@angular/core';
1+
import { NgModule } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { FormsModule } from '@angular/forms';
44

5-
import { TextfieldComponent } from './textfield.component';
6-
import { TextfieldHelptextDirective } from './textfield-helptext.directive';
5+
import {
6+
TextfieldComponent,
7+
TextfieldHelptextDirective,
8+
} from './textfield.component';
9+
import { TextfieldBoxComponent } from './textfield-box.component';
710

811
const TEXTFIELD_COMPONENTS = [
912
TextfieldComponent,
10-
TextfieldHelptextDirective
13+
TextfieldHelptextDirective,
14+
TextfieldBoxComponent,
1115
];
1216

1317
@NgModule({
@@ -16,3 +20,6 @@ const TEXTFIELD_COMPONENTS = [
1620
declarations: [TEXTFIELD_COMPONENTS],
1721
})
1822
export class TextfieldModule { }
23+
24+
export * from './textfield.component';
25+
export * from './textfield-box.component';

src/lib/textfield/textfield-adapter.d.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
addClassToHelptext: (className: string) => void;
77
removeClassFromHelptext: (className: string) => void;
88
helptextHasClass: (className: string) => boolean;
9-
registerInputFocusHandler: (EventListener) => void;
10-
deregisterInputFocusHandler: (EventListener) => void;
11-
registerInputBlurHandler: (EventListener) => void;
12-
deregisterInputBlurHandler: (EventListener) => void;
13-
registerInputInputHandler: (EventListener) => void;
14-
deregisterInputInputHandler: (EventListener) => void;
15-
registerInputKeydownHandler: (EventListener) => void;
16-
deregisterInputKeydownHandler: (EventListener) => void;
9+
registerInputFocusHandler: (handler: EventListener) => void;
10+
deregisterInputFocusHandler: (handler: EventListener) => void;
11+
registerInputBlurHandler: (handler: EventListener) => void;
12+
deregisterInputBlurHandler: (handler: EventListener) => void;
13+
registerInputInputHandler: (handler: EventListener) => void;
14+
deregisterInputInputHandler: (handler: EventListener) => void;
15+
registerInputKeydownHandler: (handler: EventListener) => void;
16+
deregisterInputKeydownHandler: (handler: EventListener) => void;
1717
setHelptextAttr: (name: string, value: string) => void;
1818
removeHelptextAttr: (className: string) => void;
1919
getNativeInput: () => HTMLInputElement;
20-
}
20+
}

src/lib/textfield/textfield-helptext.directive.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/lib/textfield/textfield.component.ts

Lines changed: 58 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
AfterViewInit,
33
Component,
4+
Directive,
45
ElementRef,
56
EventEmitter,
67
HostBinding,
@@ -14,8 +15,11 @@
1415
forwardRef
1516
} from '@angular/core';
1617
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
17-
import { MDCTextfieldAdapter } from './textfield-adapter';
18+
import { EventRegistry } from '../common/event-registry';
19+
import { toBoolean } from '../common/boolean-property';
20+
import { Ripple } from '.././ripple/ripple.directive';
1821

22+
import { MDCTextfieldAdapter } from './textfield-adapter';
1923
import { MDCTextfieldFoundation } from '@material/textfield';
2024

2125
export const MD_TEXTFIELD_CONTROL_VALUE_ACCESSOR: Provider = {
@@ -24,7 +28,23 @@ export const MD_TEXTFIELD_CONTROL_VALUE_ACCESSOR: Provider = {
2428
multi: true
2529
};
2630

27-
type UnlistenerMap = WeakMap<EventListener, Function>;
31+
@Directive({
32+
selector: '[mdc-textfield-helptext]'
33+
})
34+
export class TextfieldHelptextDirective {
35+
@Input() id: string;
36+
@Input() persistent: boolean;
37+
@Input() validation: boolean;
38+
@HostBinding('class.mdc-textfield-helptext') isHostClass = true;
39+
@HostBinding('class.mdc-textfield-helptext--persistent') get classPersistent(): string {
40+
return this.persistent ? 'mdc-textfield-helptext--persistent' : '';
41+
}
42+
@HostBinding('class.mdc-textfield-helptext--validation-msg') get classValidation(): string {
43+
return this.validation ? 'mdc-textfield-helptext--validation-msg' : '';
44+
}
45+
46+
constructor(public elementRef: ElementRef) { }
47+
}
2848

2949
@Component({
3050
selector: 'mdc-textfield',
@@ -55,22 +75,23 @@ type UnlistenerMap = WeakMap<EventListener, Function>;
5575
[type]="type"
5676
[id]="id"
5777
[attr.name]="name"
58-
[attr.aria-controls]="labelId"
5978
[(ngModel)]="value"
6079
[placeholder]="placeholder ? placeholder : ''"
6180
[tabindex]="tabindex"
6281
[maxlength]="maxlength"
6382
[disabled]="disabled"
6483
[required]="required"
65-
[attr.tabindex]="tabindex"
6684
(focus)="onFocus($event)"
6785
(keydown)="onKeyDown($event)"
6886
(blur)="onBlur($event)"
6987
(input)="onInput($event)" />
7088
<label #inputlabel [attr.for]="id" class="mdc-textfield__label" *ngIf="!placeholder">{{label}}</label>
7189
`,
7290
encapsulation: ViewEncapsulation.None,
73-
providers: [MD_TEXTFIELD_CONTROL_VALUE_ACCESSOR]
91+
providers: [
92+
MD_TEXTFIELD_CONTROL_VALUE_ACCESSOR,
93+
],
94+
viewProviders: [Ripple]
7495
})
7596
export class TextfieldComponent implements AfterViewInit, OnDestroy {
7697
@Input() id: string;
@@ -86,7 +107,6 @@ export class TextfieldComponent implements AfterViewInit, OnDestroy {
86107
}
87108
@Input() dense: boolean;
88109
@Input() required: boolean;
89-
@Input() labelId: string;
90110
@Input() label: string;
91111
@Input() placeholder: string;
92112
@Input() tabindex: number;
@@ -111,100 +131,87 @@ export class TextfieldComponent implements AfterViewInit, OnDestroy {
111131
}
112132
@ViewChild('input') public inputEl: ElementRef;
113133
@ViewChild('inputlabel') public inputLabel: ElementRef;
134+
@ViewChild(TextfieldHelptextDirective) helpText: TextfieldHelptextDirective;
114135

115136
onTouched: () => any = () => { };
116137

117138
private _controlValueAccessorChangeFn: (value: any) => void = (value) => { };
118-
private _unlisteners: Map<string, UnlistenerMap> = new Map<string, UnlistenerMap>();
119139

120140
private _mdcAdapter: MDCTextfieldAdapter = {
121141
addClass: (className: string) => {
122-
const { _renderer: renderer, _root: root } = this;
123-
renderer.addClass(root.nativeElement, className);
142+
this._renderer.addClass(this._root.nativeElement, className);
124143
},
125144
removeClass: (className: string) => {
126-
const { _renderer: renderer, _root: root } = this;
127-
renderer.removeClass(root.nativeElement, className);
145+
this._renderer.removeClass(this._root.nativeElement, className);
128146
},
129147
addClassToLabel: (className: string) => {
130-
const { _renderer: renderer, _root: root } = this;
131148
if (this.inputLabel) {
132149
if (this.label && !this.fullwidth) {
133-
renderer.addClass(this.inputLabel.nativeElement, className);
150+
this._renderer.addClass(this.inputLabel.nativeElement, className);
134151
}
135152
}
136153
},
137154
removeClassFromLabel: (className: string) => {
138-
const { _renderer: renderer, _root: root } = this;
139155
if (this.inputLabel) {
140156
if (this.label && !this.fullwidth) {
141-
renderer.removeClass(this.inputLabel.nativeElement, className);
157+
this._renderer.removeClass(this.inputLabel.nativeElement, className);
142158
}
143159
}
144160
},
145161
addClassToHelptext: (className: string) => {
146-
const { _renderer: renderer, _root: root } = this;
147-
if (root.nativeElement.attributes.getNamedItem('aria-controls')) {
148-
if (root.nativeElement.nextElementSibling) {
149-
renderer.addClass(renderer.nextSibling(root.nativeElement), className);
150-
}
162+
if (this.helpText) {
163+
this._renderer.addClass(this.helpText, className);
151164
}
152165
},
153166
removeClassFromHelptext: (className: string) => {
154-
const { _renderer: renderer, _root: root } = this;
155-
if (root.nativeElement.attributes.getNamedItem('aria-controls')) {
156-
renderer.removeClass(renderer.nextSibling(root.nativeElement), className);
167+
if (this.helpText) {
168+
this._renderer.removeClass(this.helpText, className);
157169
}
158170
},
159171
registerInputFocusHandler: (handler: EventListener) => {
160-
if (this._root) {
161-
this.listen_('focus', handler, this.inputEl);
172+
if (this.inputEl) {
173+
this._registry.listen_(this._renderer, 'focus', handler, this.inputEl);
162174
}
163175
},
164176
deregisterInputFocusHandler: (handler: EventListener) => {
165-
this.unlisten_('focus', handler);
177+
this._registry.unlisten_('focus', handler);
166178
},
167179
registerInputBlurHandler: (handler: EventListener) => {
168-
if (this._root) {
169-
this.listen_('blur', handler, this.inputEl);
180+
if (this.inputEl) {
181+
this._registry.listen_(this._renderer, 'blur', handler, this.inputEl);
170182
}
171183
},
172184
deregisterInputBlurHandler: (handler: EventListener) => {
173-
this.unlisten_('blur', handler);
185+
this._registry.unlisten_('blur', handler);
174186
},
175187
registerInputInputHandler: (handler: EventListener) => {
176-
if (this._root) {
177-
this.listen_('input', handler, this.inputEl);
188+
if (this.inputEl) {
189+
this._registry.listen_(this._renderer, 'input', handler, this.inputEl);
178190
}
179191
},
180192
deregisterInputInputHandler: (handler: EventListener) => {
181-
this.unlisten_('input', handler);
193+
this._registry.unlisten_('input', handler);
182194
},
183195
registerInputKeydownHandler: (handler: EventListener) => {
184-
if (this._root) {
185-
this.listen_('keydown', handler, this.inputEl);
196+
if (this.inputEl) {
197+
this._registry.listen_(this._renderer, 'keydown', handler, this.inputEl);
186198
}
187199
},
188200
deregisterInputKeydownHandler: (handler: EventListener) => {
189-
this.unlisten_('keydown', handler);
201+
this._registry.unlisten_('keydown', handler);
190202
},
191203
setHelptextAttr: (name: string, value: string) => {
192-
const { _renderer: renderer, _root: root } = this;
193-
if (root.nativeElement.attributes.getNamedItem('aria-controls')) {
194-
root.nativeElement.nextElementSibling ? renderer.setAttribute(root.nativeElement.nextElementSibling, name, value) : null;
204+
if (this.helpText) {
205+
this._renderer.setAttribute(this.helpText.elementRef.nativeElement, name, value);
195206
}
196207
},
197208
removeHelptextAttr: (name: string) => {
198-
const { _renderer: renderer, _root: root } = this;
199-
if (root.nativeElement.attributes.getNamedItem('aria-controls')) {
200-
return root.nativeElement.nextElementSibling ? renderer.removeAttribute(root.nativeElement.nextElementSibling, name) : null;
209+
if (this.helpText) {
210+
this._renderer.removeAttribute(this.helpText.elementRef.nativeElement, name);
201211
}
202212
},
203213
helptextHasClass: (className: string) => {
204-
const { _renderer: renderer, _root: root } = this;
205-
if (root.nativeElement.attributes.getNamedItem('aria-controls')) {
206-
return root.nativeElement.nextElementSibling ? root.nativeElement.nextElementSibling.classList.contains(className) : false;
207-
}
214+
return this.helpText ? this.helpText.elementRef.nativeElement.classList.contains(className) : false;
208215
},
209216
getNativeInput: () => {
210217
return this.inputEl ? this.inputEl.nativeElement : null;
@@ -218,7 +225,11 @@ export class TextfieldComponent implements AfterViewInit, OnDestroy {
218225
setDisabled: Function
219226
} = new MDCTextfieldFoundation(this._mdcAdapter);
220227

221-
constructor(private _renderer: Renderer2, private _root: ElementRef) { }
228+
constructor(
229+
private _renderer: Renderer2,
230+
private _root: ElementRef,
231+
private _registry: EventRegistry,
232+
public ripple: Ripple) { }
222233

223234
ngAfterViewInit() {
224235
this._foundation.init();
@@ -264,24 +275,4 @@ export class TextfieldComponent implements AfterViewInit, OnDestroy {
264275
registerOnTouched(fn: any) {
265276
this.onTouched = fn;
266277
}
267-
268-
listen_(type: string, listener: EventListener, ref: ElementRef = this._root) {
269-
if (!this._unlisteners.has(type)) {
270-
this._unlisteners.set(type, new WeakMap<EventListener, Function>());
271-
}
272-
const unlistener = this._renderer.listen(ref.nativeElement, type, listener);
273-
this._unlisteners.get(type).set(listener, unlistener);
274-
}
275-
276-
unlisten_(type: string, listener: EventListener) {
277-
if (!this._unlisteners.has(type)) {
278-
return;
279-
}
280-
const unlisteners = this._unlisteners.get(type);
281-
if (!unlisteners.has(listener)) {
282-
return;
283-
}
284-
unlisteners.get(listener)();
285-
unlisteners.delete(listener);
286-
}
287-
}
278+
}

0 commit comments

Comments
 (0)