Skip to content

Commit a86c340

Browse files
authored
feat(typeahead): removed old injector usage (#1321)
refs #1318
1 parent 3e53b7d commit a86c340

File tree

6 files changed

+78
-61
lines changed

6 files changed

+78
-61
lines changed

src/component-loader/component-loader.class.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ export class ComponentLoader<T> {
195195
}
196196

197197
private _subscribePositioning(): void {
198-
if (this._zoneSubscription) {
198+
if (this._zoneSubscription || !this.attachment) {
199199
return;
200200
}
201201

src/datepicker/datepicker.module.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { DatePickerComponent } from './datepicker.component';
77
import { DayPickerComponent } from './daypicker.component';
88
import { MonthPickerComponent } from './monthpicker.component';
99
import { YearPickerComponent } from './yearpicker.component';
10-
import { ComponentsHelper } from '../utils/components-helper.service';
1110
import { DatepickerConfig } from './datepicker.config';
1211

1312
@NgModule({
@@ -16,7 +15,7 @@ import { DatepickerConfig } from './datepicker.config';
1615
MonthPickerComponent, YearPickerComponent],
1716
exports: [DatePickerComponent, DatePickerInnerComponent, DayPickerComponent, FormsModule,
1817
MonthPickerComponent, YearPickerComponent],
19-
providers: [ComponentsHelper, DatepickerConfig]
18+
providers: [DatepickerConfig]
2019
})
2120
export class DatepickerModule {
2221
}

src/spec/typeahead.directive.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ describe('Directive: Typeahead', () => {
8686
});
8787

8888
it('should not set the container reference', () => {
89-
expect(directive.container).toBeFalsy();
89+
expect(directive._container).toBeFalsy();
9090
});
9191
});
9292

@@ -107,7 +107,7 @@ describe('Directive: Typeahead', () => {
107107
});
108108

109109
it('should set the container reference', () => {
110-
expect(directive.container).toBeTruthy();
110+
expect(directive._container).toBeTruthy();
111111
});
112112

113113
it('should result in a total of 2 matches, when \"Ala\" is entered', fakeAsync(() => {

src/typeahead/typeahead-container.component.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import { Component, ElementRef, TemplateRef, ViewEncapsulation } from '@angular/core';
22

33
import { Ng2BootstrapConfig, Ng2BootstrapTheme } from '../utils/ng2-bootstrap-config';
4-
import { positionService } from '../utils/position';
5-
import { TypeaheadOptions } from './typeahead-options.class';
64
import { TypeaheadUtils } from './typeahead-utils';
75
import { TypeaheadDirective } from './typeahead.directive';
86
import { TypeaheadMatch } from './typeahead-match.class';
97

108
const bs4 = `
119
<div class="dropdown-menu"
12-
[ngStyle]="{top: top, left: left, display: 'block'}"
1310
(mouseleave)="focusLost()">
1411
<template ngFor let-match let-i="index" [ngForOf]="matches">
1512
<h6 *ngIf="match.isHeader()" class="dropdown-header">{{match}}</h6>
@@ -38,7 +35,6 @@ const bs4 = `
3835

3936
const bs3 = `
4037
<ul class="dropdown-menu"
41-
[ngStyle]="{top: top, left: left, display: 'block'}"
4238
(mouseleave)="focusLost()">
4339
<template ngFor let-match let-i="index" [ngForOf]="matches">
4440
<li *ngIf="match.isHeader()" class="dropdown-header">{{match}}</li>
@@ -67,6 +63,8 @@ let isBS4 = Ng2BootstrapConfig.theme === Ng2BootstrapTheme.BS4;
6763
@Component({
6864
selector: 'typeahead-container',
6965
template: isBS4 ? bs4 : bs3,
66+
// tslint:disable-next-line
67+
host: {'[class]': '"dropdown open"' },
7068
encapsulation: ViewEncapsulation.None
7169
})
7270
export class TypeaheadContainerComponent {
@@ -82,9 +80,8 @@ export class TypeaheadContainerComponent {
8280
protected _matches:TypeaheadMatch[] = [];
8381
protected placement:string;
8482

85-
public constructor(element:ElementRef, options:TypeaheadOptions) {
83+
public constructor(element:ElementRef) {
8684
this.element = element;
87-
Object.assign(this, options);
8885
}
8986

9087
public get matches():TypeaheadMatch[] {
@@ -105,17 +102,6 @@ export class TypeaheadContainerComponent {
105102
return this.parent ? this.parent.typeaheadItemTemplate : undefined;
106103
}
107104

108-
public position(hostEl:ElementRef):void {
109-
this.top = '0px';
110-
this.left = '0px';
111-
let p = positionService
112-
.positionElements(hostEl.nativeElement,
113-
this.element.nativeElement.children[0],
114-
this.placement, false);
115-
this.top = p.top + 'px';
116-
this.left = p.left + 'px';
117-
}
118-
119105
public selectActiveMatch():void {
120106
this.selectMatch(this._active);
121107
}

src/typeahead/typeahead.directive.ts

Lines changed: 69 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
2-
ComponentRef, Directive, ElementRef, EventEmitter, HostListener, Input, OnInit, Output,
3-
ReflectiveInjector, Renderer, TemplateRef, ViewContainerRef
2+
ComponentRef, Directive, ElementRef, EventEmitter, HostListener, Input,
3+
OnInit, Output,
4+
ReflectiveInjector, Renderer, TemplateRef, ViewContainerRef, OnDestroy
45
} from '@angular/core';
56
import { FormControl, NgControl } from '@angular/forms';
67

@@ -19,6 +20,7 @@ import 'rxjs/add/operator/toArray';
1920

2021
import { ComponentsHelper } from '../utils/components-helper.service';
2122
import { TypeaheadMatch } from './typeahead-match.class';
23+
import { ComponentLoaderFactory, ComponentLoader } from '../component-loader';
2224

2325
/* tslint:disable-next-line */
2426
const KeyboardEvent = (global as any).KeyboardEvent as KeyboardEvent;
@@ -28,7 +30,7 @@ const KeyboardEvent = (global as any).KeyboardEvent as KeyboardEvent;
2830
selector: '[typeahead][ngModel],[typeahead][formControlName]'
2931
/* tslint:enable */
3032
})
31-
export class TypeaheadDirective implements OnInit {
33+
export class TypeaheadDirective implements OnInit, OnDestroy {
3234
@Output() public typeaheadLoading:EventEmitter<boolean> = new EventEmitter<boolean>(false);
3335
@Output() public typeaheadNoResults:EventEmitter<boolean> = new EventEmitter<boolean>(false);
3436
@Output() public typeaheadOnSelect:EventEmitter<TypeaheadMatch> = new EventEmitter<TypeaheadMatch>(false);
@@ -46,32 +48,39 @@ export class TypeaheadDirective implements OnInit {
4648
@Input() public typeaheadPhraseDelimiters:string = '\'"';
4749
@Input() public typeaheadItemTemplate:TemplateRef<any>;
4850

51+
/**
52+
* A selector specifying the element the typeahead should be appended to.
53+
* Currently only supports "body".
54+
*/
55+
@Input() public container: string;
56+
4957
// not yet implemented
50-
// @Input() protected typeaheadAppendToBody:boolean;
5158
// @Input() protected typeaheadEditable:boolean;
5259
// @Input() protected typeaheadFocusFirst:boolean;
5360
// @Input() protected typeaheadInputFormatter:any;
5461
// @Input() protected typeaheadSelectOnExact:boolean;
5562
// @Input() protected typeaheadSelectOnBlur:boolean;
5663
// @Input() protected typeaheadFocusOnSelect:boolean;
5764

58-
public container:TypeaheadContainerComponent;
65+
public _container:TypeaheadContainerComponent;
5966
public isTypeaheadOptionsListActive:boolean = false;
6067

6168
protected keyUpEventEmitter:EventEmitter<any> = new EventEmitter();
6269
protected _matches:TypeaheadMatch[];
6370
protected placement:string = 'bottom-left';
64-
protected popup:ComponentRef<TypeaheadContainerComponent>;
71+
// protected popup:ComponentRef<TypeaheadContainerComponent>;
6572

6673
protected ngControl:NgControl;
6774
protected viewContainerRef:ViewContainerRef;
6875
protected element:ElementRef;
6976
protected renderer:Renderer;
7077
protected componentsHelper:ComponentsHelper;
7178

79+
private _typeahead: ComponentLoader<TypeaheadContainerComponent>;
80+
7281
@HostListener('keyup', ['$event'])
7382
public onChange(e:any):void {
74-
if (this.container) {
83+
if (this._container) {
7584
// esc
7685
if (e.keyCode === 27) {
7786
this.hide();
@@ -80,19 +89,19 @@ export class TypeaheadDirective implements OnInit {
8089

8190
// up
8291
if (e.keyCode === 38) {
83-
this.container.prevActiveMatch();
92+
this._container.prevActiveMatch();
8493
return;
8594
}
8695

8796
// down
8897
if (e.keyCode === 40) {
89-
this.container.nextActiveMatch();
98+
this._container.nextActiveMatch();
9099
return;
91100
}
92101

93102
// enter
94103
if (e.keyCode === 13) {
95-
this.container.selectActiveMatch();
104+
this._container.selectActiveMatch();
96105
return;
97106
}
98107
}
@@ -120,15 +129,15 @@ export class TypeaheadDirective implements OnInit {
120129

121130
@HostListener('blur')
122131
public onBlur():void {
123-
if (this.container && !this.container.isFocused) {
132+
if (this._container && !this._container.isFocused) {
124133
this.hide();
125134
}
126135
}
127136

128137
@HostListener('keydown', ['$event'])
129138
public onKeydown(e:KeyboardEvent):void {
130139
// no container - no problems
131-
if (!this.container) {
140+
if (!this._container) {
132141
return;
133142
}
134143

@@ -146,12 +155,14 @@ export class TypeaheadDirective implements OnInit {
146155
}
147156

148157
public constructor(control:NgControl, viewContainerRef:ViewContainerRef, element:ElementRef,
149-
renderer:Renderer, componentsHelper:ComponentsHelper) {
158+
renderer:Renderer, componentsHelper:ComponentsHelper, cis: ComponentLoaderFactory) {
150159
this.element = element;
151160
this.ngControl = control;
152161
this.viewContainerRef = viewContainerRef;
153162
this.renderer = renderer;
154-
this.componentsHelper = componentsHelper;
163+
// this.componentsHelper = componentsHelper;
164+
this._typeahead = cis
165+
.createLoader<TypeaheadContainerComponent>(element, viewContainerRef, renderer);
155166
}
156167

157168
public ngOnInit():void {
@@ -187,39 +198,59 @@ export class TypeaheadDirective implements OnInit {
187198
}
188199

189200
public show():void {
190-
let options = new TypeaheadOptions({
191-
typeaheadRef: this,
192-
placement: this.placement,
193-
animation: false
194-
});
195-
196-
let binding = ReflectiveInjector.resolve([
197-
{provide: TypeaheadOptions, useValue: options}
198-
]);
199-
200-
this.popup = this.componentsHelper
201-
.appendNextToLocation(TypeaheadContainerComponent, this.viewContainerRef, binding);
202-
203-
this.popup.instance.position(this.viewContainerRef.element);
204-
this.container = this.popup.instance;
205-
this.container.parent = this;
201+
this._typeahead
202+
.attach(TypeaheadContainerComponent)
203+
// todo: add append to body, after updating positioning service
204+
// .to(this.container)
205+
// .position({attachment: 'bottom left'})
206+
.show(null, {
207+
typeaheadRef: this,
208+
placement: this.placement,
209+
animation: false
210+
});
211+
212+
// let options = new TypeaheadOptions({
213+
// typeaheadRef: this,
214+
// placement: this.placement,
215+
// animation: false
216+
// });
217+
//
218+
// let binding = ReflectiveInjector.resolve([
219+
// {provide: TypeaheadOptions, useValue: options}
220+
// ]);
221+
//
222+
// this.popup = this.componentsHelper
223+
// .appendNextToLocation(TypeaheadContainerComponent, this.viewContainerRef, binding);
224+
225+
// this.popup.instance.position(this.viewContainerRef.element);
226+
// this._container = this.popup.instance;
227+
this._container = this._typeahead.instance;
228+
this._container.parent = this;
206229
// This improves the speed as it won't have to be done for each list item
207230
let normalizedQuery = (this.typeaheadLatinize
208231
? TypeaheadUtils.latinize(this.ngControl.control.value)
209232
: this.ngControl.control.value).toString()
210233
.toLowerCase();
211-
this.container.query = this.typeaheadSingleWords
234+
this._container.query = this.typeaheadSingleWords
212235
? TypeaheadUtils.tokenize(normalizedQuery, this.typeaheadWordDelimiters, this.typeaheadPhraseDelimiters)
213236
: normalizedQuery;
214-
this.container.matches = this._matches;
237+
this._container.matches = this._matches;
215238
this.element.nativeElement.focus();
216239
}
217240

218241
public hide():void {
219-
if (this.container) {
220-
this.popup.destroy();
221-
this.container = void 0;
242+
if (this._typeahead.isShown) {
243+
this._typeahead.hide();
244+
this._container = null;
222245
}
246+
// if (this._container) {
247+
// this.popup.destroy();
248+
// this._container = void 0;
249+
// }
250+
}
251+
252+
public ngOnDestroy():any {
253+
this._typeahead.dispose();
223254
}
224255

225256
protected asyncActions():void {
@@ -305,16 +336,16 @@ export class TypeaheadDirective implements OnInit {
305336
return;
306337
}
307338

308-
if (this.container) {
339+
if (this._container) {
309340
// This improves the speed as it won't have to be done for each list item
310341
let normalizedQuery = (this.typeaheadLatinize
311342
? TypeaheadUtils.latinize(this.ngControl.control.value)
312343
: this.ngControl.control.value).toString()
313344
.toLowerCase();
314-
this.container.query = this.typeaheadSingleWords
345+
this._container.query = this.typeaheadSingleWords
315346
? TypeaheadUtils.tokenize(normalizedQuery, this.typeaheadWordDelimiters, this.typeaheadPhraseDelimiters)
316347
: normalizedQuery;
317-
this.container.matches = this._matches;
348+
this._container.matches = this._matches;
318349
} else {
319350
this.show();
320351
}

src/typeahead/typeahead.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import { FormsModule } from '@angular/forms';
55
import { TypeaheadContainerComponent } from './typeahead-container.component';
66
import { TypeaheadDirective } from './typeahead.directive';
77
import { ComponentsHelper } from '../utils/components-helper.service';
8+
import { ComponentLoaderFactory } from '../component-loader';
89

910
@NgModule({
1011
imports: [CommonModule, FormsModule],
1112
declarations: [TypeaheadContainerComponent, TypeaheadDirective],
1213
exports: [FormsModule, TypeaheadContainerComponent, TypeaheadDirective],
13-
providers: [ComponentsHelper],
14+
providers: [ComponentsHelper, ComponentLoaderFactory],
1415
entryComponents: [TypeaheadContainerComponent]
1516
})
1617
export class TypeaheadModule {

0 commit comments

Comments
 (0)