Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to add custom entries in Multiple mode #193

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
padding-top: 30px;
}
</style>
<link rel="stylesheet" href="styles.3ff695c00d717f2d2a11.css"></head>
<link rel="stylesheet" href="styles.280e991c5992f8630bc9.css"></head>
<body>
<app-demo>Loading...</app-demo>
<script src="assets/js/prettify.min.js"></script>
<!-- script files will be inserted by html-webpack-plugin -->
<script src="runtime.359d5ee4682f20e936e9.js" defer></script><script src="polyfills-es5.64eb36538e1280be9ce3.js" nomodule defer></script><script src="polyfills.05337bc8c8a2286011e7.js" defer></script><script src="main.4609ecc8db320e4f7344.js" defer></script></body>
<script src="runtime.359d5ee4682f20e936e9.js" defer></script><script src="polyfills-es5.64eb36538e1280be9ce3.js" nomodule defer></script><script src="polyfills.05337bc8c8a2286011e7.js" defer></script><script src="main.01480025f3faedbbf39e.js" defer></script></body>
</html>
1 change: 1 addition & 0 deletions docs/main.01480025f3faedbbf39e.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion docs/main.4609ecc8db320e4f7344.js

This file was deleted.

2 changes: 2 additions & 0 deletions src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ChildrenDemoComponent } from './demo/select/children-demo';
import { NoAutoCompleteDemoComponent } from './demo/select/no-autocomplete-demo';
import { TabsModule } from 'ngx-bootstrap/tabs';
import { ButtonsModule } from 'ngx-bootstrap/buttons';
import { CustomEntriesDemoComponent } from './demo/select/custom-entries-demo';

describe('AppComponent', () => {
beforeEach(waitForAsync(() => {
Expand All @@ -35,6 +36,7 @@ describe('AppComponent', () => {
NoAutoCompleteDemoComponent,
RichDemoComponent,
SingleDemoComponent,
CustomEntriesDemoComponent,
],
}).compileComponents();
}));
Expand Down
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TabsModule } from 'ngx-bootstrap/tabs';
import { ButtonsModule } from 'ngx-bootstrap/buttons';
import { CustomEntriesDemoComponent } from './demo/select/custom-entries-demo';


@NgModule({
Expand All @@ -26,6 +27,7 @@ import { ButtonsModule } from 'ngx-bootstrap/buttons';
NoAutoCompleteDemoComponent,
RichDemoComponent,
SingleDemoComponent,
CustomEntriesDemoComponent,
],
imports: [
BrowserModule,
Expand Down
10 changes: 10 additions & 0 deletions src/app/demo/select-section.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ const tabDesc = {
ts: require('!!raw-loader!./select/no-autocomplete-demo.ts'),
html: require('!!raw-loader!./select/no-autocomplete-demo.html'),
},
customEntries: {
heading: 'Custom entries',
ts: require('!!raw-loader!./select/custom-entries-demo.ts'),
html: require('!!raw-loader!./select/custom-entries-demo.html'),
},
};

@Component({
Expand Down Expand Up @@ -66,6 +71,11 @@ const tabDesc = {
<no-autocomplete-demo></no-autocomplete-demo>
</sample-section>
</tab>
<tab heading="Custom entries">
<sample-section [desc]="tabDesc.customEntries">
<custom-entries-demo></custom-entries-demo>
</sample-section>
</tab>
</tabset>

<h2>Documentation</h2>
Expand Down
24 changes: 24 additions & 0 deletions src/app/demo/select/custom-entries-demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<h3>Custom entries</h3>

<div class="example-block">
<div class="example-block__item">
<ngx-select [multiple]="true"
[customEntries]="true"
[items]="items"
[formControl]="names"
[disabled]="ngxDisabled"
[autoClearSearch]="true"
placeholder="Nothing selected"
(selectionChanges)="doSelectOptions($event)">
</ngx-select>
<p></p>
<div class="alert alert-secondary">
<pre>{{names.value | json}}</pre>
</div>
<div>
<button class="btn btn-primary" (click)="ngxDisabled = !ngxDisabled">
{{ngxDisabled ? 'Enable' : 'Disable'}}
</button>
</div>
</div>
</div>
18 changes: 18 additions & 0 deletions src/app/demo/select/custom-entries-demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Component } from '@angular/core';
import { INgxSelectOption } from 'app/lib/ngx-select/ngx-select.interfaces';
import { FormControl } from '@angular/forms';

@Component({
selector: 'custom-entries-demo',
templateUrl: './custom-entries-demo.html',
})
export class CustomEntriesDemoComponent {
public items: string[] = ['Ana', 'Aleyna', 'Barbara', 'Charlotte', 'Diana', 'Elise', 'Fiona', 'Gina', 'Helene', 'Irene', 'Jessica',
'Katarina', 'Lea', 'Liara', 'Maria', 'Mara', 'Melanie', 'Natalie'];

public ngxDisabled = false;
public names = new FormControl();

public doSelectOptions = (options: INgxSelectOption[]) => console.log('MultipleDemoComponent.doSelectOptions', options);

}
1 change: 1 addition & 0 deletions src/app/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Any item can be `disabled` for prevent selection. For disable an item add the pr
| optGroupLabelField | string | `'label'` | Provide an opportunity to change the name a `label` property of objects with an `options` property in the `items` |
| optGroupOptionsField | string | `'options'` | Provide an opportunity to change the name of an `options` property of objects in the `items` |
| [multiple] | boolean | `false` | Mode of this component. If set `true` user can select more than one option |
| [customEntries] | boolean | `false` | Submode for Multiple. If set `true` allows the user to create custom entries with Enter-Press |
| [allowClear] | boolean | `false` | Set to `true` to allow the selection to be cleared. This option only applies to single-value inputs |
| [placeholder] | string | `''` | Set to `true` Placeholder text to display when the element has no focus and selected items |
| [noAutoComplete] | boolean | `false` | Set to `true` to hide the search input. This option only applies to single-value inputs |
Expand Down
40 changes: 31 additions & 9 deletions src/app/lib/ngx-select/ngx-select.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export class NgxSelectComponent implements INgxSelectOptions, ControlValueAccess
@Input() public keepSelectMenuOpened = false;
@Input() public autocomplete = 'off';
@Input() public dropDownMenuOtherClasses = '';
@Input() public customEntries = false;
public keyCodeToRemoveSelected = 'Delete';
public keyCodeToOptionsOpen = ['Enter', 'NumpadEnter'];
public keyCodeToOptionsClose = 'Escape';
Expand All @@ -104,6 +105,7 @@ export class NgxSelectComponent implements INgxSelectOptions, ControlValueAccess
@Output() public close = new EventEmitter<void>();
@Output() public select = new EventEmitter<any>();
@Output() public remove = new EventEmitter<any>();
@Output() public filtered = new EventEmitter<TSelectOption[]>();
@Output() public navigated = new EventEmitter<INgxOptionNavigated>();
@Output() public selectionChanges = new EventEmitter<INgxSelectOption[]>();

Expand Down Expand Up @@ -212,12 +214,21 @@ export class NgxSelectComponent implements INgxSelectOptions, ControlValueAccess
debounceTime(0) // For a case when optionsFlat, actualValue came at the same time
).subscribe(([optionsFlat, actualValue]: [NgxSelectOption[], any[]]) => {
const optionsSelected = [];

actualValue.forEach((value: any) => {
const selectedOption = optionsFlat.find((option: NgxSelectOption) => option.value === value);
if (selectedOption) {
optionsSelected.push(selectedOption);
}
if (this.customEntries && this.multiple) {
if (value) {
let selectedOption = optionsFlat.find((option: NgxSelectOption) => option.value === value);
if (!selectedOption) {
selectedOption = new NgxSelectOption(value, value, false, value);
}
optionsSelected.push(selectedOption);
}
} else {
const selectedOption = optionsFlat.find((option: NgxSelectOption) => option.value === value);
if (selectedOption) {
optionsSelected.push(selectedOption);
}
}
});

if (this.keepSelectedItems) {
Expand Down Expand Up @@ -247,6 +258,7 @@ export class NgxSelectComponent implements INgxSelectOptions, ControlValueAccess
}
return option;
});
this.filtered.emit(this.optionsFiltered);
this.cacheOptionsFilteredFlat = null;
this.navigateOption(ENavigation.firstIfOptionActiveInvisible);
this.cd.markForCheck();
Expand Down Expand Up @@ -406,14 +418,23 @@ export class NgxSelectComponent implements INgxSelectOptions, ControlValueAccess
this.keyCodeToNavigateLast
);
const keysForClosedState = [].concat(this.keyCodeToOptionsOpen, this.keyCodeToRemoveSelected);

if (this.optionsOpened && keysForOpenedState.indexOf(event.code) !== -1) {
event.preventDefault();
event.stopPropagation();
switch (event.code) {
case ([].concat(this.keyCodeToOptionsSelect).indexOf(event.code) + 1) && event.code:
this.optionSelect(this.optionActive);
this.navigateOption(ENavigation.next);
if (this.customEntries && this.multiple && !this.optionActive) {
// tslint:disable-next-line
const selection = event.srcElement['value'];
const newEntry: NgxSelectOption = new NgxSelectOption(selection, selection, false, selection);
this.subjOptionsSelected.next((this.multiple ? this.subjOptionsSelected.value : []).concat([newEntry]));
// tslint:disable-next-line
event.srcElement['value'] = '';
this.optionsClose();
} else {
this.optionSelect(this.optionActive);
this.navigateOption(ENavigation.next);
}
break;
case this.keyCodeToNavigateFirst:
this.navigateOption(ENavigation.first);
Expand Down Expand Up @@ -540,7 +561,8 @@ export class NgxSelectComponent implements INgxSelectOptions, ControlValueAccess
if (this.searchCallback) {
return this.searchCallback(search, option);
}
return (!search || regExp.test(option.text)) && (!this.multiple || selectedOptions.indexOf(option) === -1);
return (!search || regExp.test(option.text)) &&
(!this.multiple || !selectedOptions.find(opt => opt.value === option.value));
};

return options.filter((option: TSelectOption) => {
Expand Down