Skip to content

Commit

Permalink
fix: handle selected value when items are not loaded yet (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
anjmao committed Oct 4, 2017
1 parent 7383749 commit 5bcdda5
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 102 deletions.
6 changes: 5 additions & 1 deletion src/demo/app/examples/autocomplete.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ export class SelectAutocompleteComponent {
}

loadGithubUsers(term: string): Observable<any[]> {
return this.http.get<any>(`https://api.github.com/search/users?q=${term}`).map(rsp => rsp.items);
if (term) {
return this.http.get<any>(`https://api.github.com/search/users?q=${term}`).map(rsp => rsp.items);
} else {
return Observable.of([]);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/demo/app/examples/multi.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class SelectMultiComponent {
this.companies2.push({ id: i, name: c });
});

this.selectedCompaniesDisabled = [{ id: 0, name: 'Miškas' }, { id: 1, name: 'Žalias' }]
this.selectedCompaniesDisabled = [{ id: 0, name: 'Miškas' }, { id: 1, name: 'Žalias' }];
}

clearModel() {
Expand Down
19 changes: 15 additions & 4 deletions src/demo/app/examples/reactive-forms.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
</ng-template>
</ng-select>
<small class="form-text text-muted">Albums data from backend using HttpClient.</small>
<br>
<button class="btn btn-secondary btn-sm" (click)="selectFirstAlbum()">Select first album</button>
<button class="btn btn-secondary btn-sm" (click)="selectAlbumsRange(0, 10)">Set 0-10 albums</button>
<button class="btn btn-secondary btn-sm" (click)="selectAlbumsRange(10, 20)">Set 10-20 albums</button>
</div>
<hr>
Expand Down Expand Up @@ -128,6 +132,7 @@ export class ReactiveFormsComponent {
];

albums = [];
allAlbums = [];
photos = [];

constructor(private fb: FormBuilder, private http: HttpClient, private modalService: NgbModal) {
Expand Down Expand Up @@ -170,6 +175,11 @@ export class ReactiveFormsComponent {
this.heroForm.get('album').patchValue(this.albums[0].id);
}

selectAlbumsRange(from, to) {
this.albums = this.allAlbums.slice(from, to);
this.selectFirstAlbum();
}

openModel(content) {
this.modalService.open(content);
}
Expand All @@ -179,15 +189,16 @@ export class ReactiveFormsComponent {
}

private loadAlbums() {
this.http.get<any[]>('https://jsonplaceholder.typicode.com/albums').subscribe(rsp => {
this.albums = rsp;
this.http.get<any[]>('https://jsonplaceholder.typicode.com/albums').subscribe(albums => {
this.allAlbums = albums;
this.albums = [...this.allAlbums];
this.selectFirstAlbum();
});
}

private loadPhotos() {
this.http.get<any[]>('https://jsonplaceholder.typicode.com/photos').subscribe(rsp => {
this.photos = rsp;
this.http.get<any[]>('https://jsonplaceholder.typicode.com/photos').subscribe(photos => {
this.photos = photos;
this.selectFirstPhoto();
});
}
Expand Down
27 changes: 20 additions & 7 deletions src/lib/src/items-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,7 @@ export class ItemsList {

private _markedIndex = -1;
private _selected: NgOption[] = [];
private _multiple: boolean;

constructor(items: NgOption[], multiple: boolean) {
this.items = this.mapItems(items);
this.filteredItems = [...this.items];
this._multiple = multiple;
}
private _multiple = false;

get value(): NgOption | NgOption[] {
if (this._multiple) {
Expand All @@ -27,6 +21,16 @@ export class ItemsList {
return this.filteredItems[this._markedIndex];
}

setItems(items: NgOption[]) {
this.items = this.mapItems(items);
this.filteredItems = [...this.items];
}

setMultiple(multiple: boolean) {
this._multiple = multiple;
this._selected = [];
}

select(item: NgOption) {
if (!this._multiple) {
this.clearSelected();
Expand All @@ -35,6 +39,15 @@ export class ItemsList {
item.selected = true;
}

findItem(value, bindLabel: string, bindValue: string): NgOption {
if (bindValue) {
return this.items.find(x => x[bindValue] === value);
}
const index = this.items.indexOf(value);
return index > -1 ? this.items[index] :
this.items.find(x => x[bindLabel] === value[bindLabel])
}

unselect(item: NgOption) {
this._selected = this._selected.filter(x => x !== item);
item.selected = false;
Expand Down
13 changes: 0 additions & 13 deletions src/lib/src/ng-search-focus.directive.ts

This file was deleted.

39 changes: 32 additions & 7 deletions src/lib/src/ng-select.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Subject } from 'rxjs/Subject';
describe('NgSelectComponent', function () {

describe('Model change detection', () => {
it('update ngModel on value change', fakeAsync(() => {
it('should update ngModel on value change', fakeAsync(() => {
const fixture = createTestingModule(
NgSelectModelChangesTestCmp,
`<ng-select [items]="cities"
Expand All @@ -38,7 +38,7 @@ describe('NgSelectComponent', function () {
discardPeriodicTasks();
}));

it('update internal model on ngModel change', fakeAsync(() => {
it('should update internal model on ngModel change', fakeAsync(() => {
const fixture = createTestingModule(
NgSelectModelChangesTestCmp,
`<ng-select [items]="cities"
Expand All @@ -61,7 +61,7 @@ describe('NgSelectComponent', function () {
discardPeriodicTasks();
}));

it('update internal model after it was toggled with *ngIf', fakeAsync(() => {
it('should update internal model after it was toggled with *ngIf', fakeAsync(() => {
const fixture = createTestingModule(
NgSelectModelChangesTestCmp,
`<ng-select *ngIf="visible"
Expand All @@ -88,6 +88,24 @@ describe('NgSelectComponent', function () {
expect(fixture.componentInstance.select.value).toEqual(null);
}));

it('should set items correctly after ngModel set first', fakeAsync(() => {
const fixture = createTestingModule(
NgSelectModelChangesTestCmp,
`<ng-select [items]="cities"
bindLabel="name"
[clearable]="true"
[(ngModel)]="selectedCity">
</ng-select>`);

const cities = [{id: 7, name: 'Pailgis'}];
fixture.componentInstance.selectedCity = cities[0];
tickAndDetectChanges(fixture);
fixture.componentInstance.cities = cities;
tickAndDetectChanges(fixture);

expect(fixture.componentInstance.select.value).toEqual(jasmine.objectContaining(cities[0]));
}));

it('should clear previous value when setting new model', fakeAsync(() => {
const fixture = createTestingModule(
NgSelectModelChangesTestCmp,
Expand Down Expand Up @@ -262,6 +280,7 @@ describe('NgSelectComponent', function () {
NgSelectBasicTestCmp,
`<ng-select [items]="cities"
bindLabel="name"
[multiple]="multiple"
[(ngModel)]="selectedCity">
</ng-select>`);
});
Expand Down Expand Up @@ -382,7 +401,7 @@ describe('NgSelectComponent', function () {
}));

it('should remove last selected value when multiple', fakeAsync(() => {
fixture.componentInstance.select.multiple = true;
fixture.componentInstance.multiple = true;
fixture.componentInstance.cities = [...fixture.componentInstance.cities];
tickAndDetectChanges(fixture);
selectOption(fixture, KeyCode.ArrowDown, 1);
Expand Down Expand Up @@ -549,9 +568,10 @@ describe('NgSelectComponent', function () {
[(ngModel)]="selectedCity">
</ng-select>`);

fixture.detectChanges();
tick(200);
fixture.componentInstance.select.onFilter({ target: { value: 'vilnius' } });
tick(200);

const result = [jasmine.objectContaining({ id: 1, name: 'Vilnius' })];
expect(fixture.componentInstance.select.itemsList.filteredItems).toEqual(result);
}));
Expand All @@ -564,10 +584,11 @@ describe('NgSelectComponent', function () {
[(ngModel)]="selectedCity">
</ng-select>`);

const result = jasmine.objectContaining(fixture.componentInstance.cities[2]);
fixture.detectChanges();
tick(200);
fixture.componentInstance.select.onFilter({ target: { value: 'pab' } });
tick(200);

const result = jasmine.objectContaining(fixture.componentInstance.cities[2]);
expect(fixture.componentInstance.select.itemsList.markedItem).toEqual(result)
triggerKeyDownEvent(getNgSelectElement(fixture), KeyCode.Enter);
expect(fixture.componentInstance.select.value).toEqual(result);
Expand All @@ -588,6 +609,9 @@ describe('NgSelectComponent', function () {
fixture.componentInstance.customFilter.subscribe(term => {
expect(term).toBe('vilnius');
});

tick(200);

fixture.componentInstance.select.onFilter({ target: { value: 'vilnius' } });
tickAndDetectChanges(fixture);
}));
Expand Down Expand Up @@ -819,6 +843,7 @@ function createTestingModule<T>(cmp: Type<T>, template: string): ComponentFixtur
class NgSelectBasicTestCmp {
@ViewChild(NgSelectComponent) select: NgSelectComponent;
selectedCity: { id: number; name: string };
multiple = false;
cities = [
{ id: 1, name: 'Vilnius' },
{ id: 2, name: 'Kaunas' },
Expand Down

0 comments on commit 5bcdda5

Please sign in to comment.