Skip to content

Commit d58f193

Browse files
authored
Merge pull request #38 from czeckd/simple-arrays
Simple string array support
2 parents 7215c1e + 5c16ef5 commit d58f193

File tree

5 files changed

+170
-53
lines changed

5 files changed

+170
-53
lines changed

README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,17 @@ Basic usage is:
3434
```
3535
The following parameters can be set on a dual-list:
3636
- **key** - The unique identifier field of each object in the `source` and
37-
`destination` arrays, default is ``_id``.
37+
`destination` arrays, default is ``_id``. (With a source of an array of strings, each string is its own id.)
3838
- **display** - The field of each object for displaying the object each the
39-
lists, default is ``_name``.
39+
lists, default is ``_name``. (With a source of an array of strings, each string is its own display.)
4040
- **height** - The height of the lists, default is ``100px``.
4141
- **format** - A format object, default is ``{ add: 'Add', remove: 'Remove', all: 'All', none: 'None', direction: 'left-to-right' }``
42-
- **filter** - A boolean whether or not to display a filter for the lists,
43-
default is ``false``.
44-
- **sort** - A boolean whether or not to keep the lists sorted, default is
45-
``false``.
42+
- **filter** - A boolean whether or not to display a filter for the lists, default is ``false``.
43+
- **sort** - A boolean whether or not to keep the lists sorted, default is ``false``.
4644
- **compare** - A compare function to be used for sorting the lists. Note if
4745
sort is not set and compare is set, then sort will be set ``true``.
48-
- **source** - The source array of objects for the list. This is the universal, master list of all possible objects.
49-
- **destination** The destination array of objects selected from the list.
46+
- **source** - The source array of objects or strings for the list. (This is the universal, master list of all possible objects.)
47+
- **destination** The destination array of objects or strings selected from the source.
5048
Note, the ``destination`` array can have prexisting elements.
5149

5250
For more usage examples, see the [`demo-app.component.ts`](https://github.com/czeckd/angular-dual-listbox/blob/master/app/demo-app.component.ts).

app/demo-app.component.ts

Lines changed: 94 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,25 @@ import { DualListComponent } from 'angular-dual-listbox';
7373
</form>
7474
</div>
7575
</div>
76+
<div class="row">
77+
<div class="col-sm-12">
78+
<label>Array source</label>
79+
<form class="form well">
80+
<div class="radio" *ngFor="let item of arrayType">
81+
<label>
82+
<input type="radio" name="sourceType" [value]="item.value" [(ngModel)]="type" (change)="swapSource()">
83+
{{item.name}} &mdash; {{item.detail}}
84+
</label>
85+
</div>
86+
</form>
87+
</div>
88+
</div>
89+
7690
<div class="row">
7791
<div class="col-sm-12">
7892
<label>General</label><br/>
7993
<form class="form-inline well">
8094
<button class="btn btn-default" (click)="doFilter()">{{filterBtn()}}</button>
81-
<button class="btn btn-default" (click)="doSwap()">Swap source</button>
8295
<button class="btn btn-primary" (click)="doReset()">Reset</button>
8396
</form>
8497
</div>
@@ -102,13 +115,21 @@ export class DemoAppComponent implements OnInit {
102115
sourceLeft = true;
103116
format:any = DualListComponent.DEFAULT_FORMAT;
104117

118+
private sourceTube:Array<string>;
105119
private sourceStations:Array<any>;
106120
private sourceChessmen:Array<any>;
107121

122+
private confirmedTube:Array<string>;
108123
private confirmedStations:Array<any>;
109124
private confirmedChessmen:Array<any>;
110125

111-
private toggle = true;
126+
arrayType = [
127+
{ name: 'Rio Grande', detail: '(object array)', value: 'station' },
128+
{ name: 'Chessmen', detail: '(object array)', value: 'chess' },
129+
{ name: 'Underground', detail: '(string array)', value: 'tube' }
130+
];
131+
132+
type = this.arrayType[0].value;
112133

113134
private stations:Array<any> = [
114135
{ key: 1, station: 'Antonito', state: 'CO' },
@@ -154,47 +175,99 @@ export class DemoAppComponent implements OnInit {
154175
{ _id: 6, name: 'King' }
155176
];
156177

178+
private tube:Array<string> = [
179+
'Harrow & Wealdstone',
180+
'Kenton',
181+
'South Kenton',
182+
'North Wembley',
183+
'Wembley Central',
184+
'Stonebridge Park',
185+
'Harlesden',
186+
'Willesden Junction',
187+
'Kensal Green',
188+
"Queen's Park",
189+
'Kilburn Park',
190+
'Maida Vale',
191+
'Warwick Avenue',
192+
'Paddington',
193+
'Edgware Road',
194+
'Marylebone',
195+
'Baker Street',
196+
"Regent's Park",
197+
'Oxford Circus',
198+
'Piccadilly Circus',
199+
'Charing Cross',
200+
'Embankment',
201+
'Waterloo',
202+
'Lambeth North',
203+
'Elephant & Castle'
204+
];
205+
157206
ngOnInit() {
158207
this.doReset();
159208
}
160209

161210
private useStations() {
162-
this.toggle = true;
163211
this.key = 'key';
164-
this.display = 'station';
212+
this.display = 'station'; //[ 'station', 'state' ];
165213
this.keepSorted = true;
166214
this.source = this.sourceStations;
167215
this.confirmed = this.confirmedStations;
168216
}
169217

170218
private useChessmen() {
171-
this.toggle = false;
172219
this.key = '_id';
173220
this.display = 'name';
174221
this.keepSorted = false;
175222
this.source = this.sourceChessmen;
176223
this.confirmed = this.confirmedChessmen;
177224
}
178225

179-
doSwap() {
180-
if (this.toggle) {
181-
this.useChessmen();
182-
} else {
226+
private useTube() {
227+
this.key = undefined;
228+
this.display = undefined;
229+
this.keepSorted = false;
230+
this.source = this.sourceTube;
231+
this.confirmed = this.confirmedTube;
232+
}
233+
234+
swapSource() {
235+
switch (this.type) {
236+
case this.arrayType[0].value:
183237
this.useStations();
238+
break;
239+
case this.arrayType[1].value:
240+
this.useChessmen();
241+
break;
242+
case this.arrayType[2].value:
243+
this.useTube();
244+
break
184245
}
185246
}
186247

187248
doReset() {
188249
this.sourceChessmen = JSON.parse(JSON.stringify(this.chessmen));
189250
this.sourceStations = JSON.parse(JSON.stringify(this.stations));
251+
this.sourceTube = JSON.parse(JSON.stringify(this.tube));
190252
this.confirmedChessmen = new Array<any>();
191253
this.confirmedStations = new Array<any>();
254+
this.confirmedTube = new Array<string>();
255+
256+
// Preconfirm some items.
257+
this.confirmedStations.push( this.stations[31] );
258+
this.confirmedTube.push( this.tube[13] );
259+
this.confirmedTube.push( this.tube[23] );
192260

193-
if (this.toggle) {
261+
switch (this.type) {
262+
case this.arrayType[0].value:
194263
this.useStations();
195-
this.confirmedStations.push( { key: 32, station: 'Eureka', state: 'CO' } );
196-
} else {
264+
break;
265+
case this.arrayType[1].value:
197266
this.useChessmen();
267+
break;
268+
case this.arrayType[2].value:
269+
this.useTube();
270+
break;
198271
}
199272
}
200273

@@ -205,17 +278,21 @@ export class DemoAppComponent implements OnInit {
205278
}
206279

207280
doCreate() {
208-
let o:any = {};
209-
o[this.key] = this.source.length + 1;
210-
o[this.display] = this.userAdd;
211-
this.source.push( o );
281+
if (typeof this.source[0] === 'object') {
282+
let o:any = {};
283+
o[this.key] = this.source.length + 1;
284+
o[this.display] = this.userAdd;
285+
this.source.push( o );
286+
} else {
287+
this.source.push(this.userAdd);
288+
}
212289
this.userAdd = '';
213290
}
214291

215292
doAdd() {
216293
for (let i = 0, len = this.source.length; i < len; i += 1) {
217294
let o = this.source[i];
218-
let found = this.confirmed.find( (e:any) => e[this.key] === o[this.key] );
295+
let found = this.confirmed.find( (e:any) => e === o );
219296
if (!found) {
220297
this.confirmed.push(o);
221298
break;

lib/dual-list.component.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
</div>
2828
</div>
2929

30-
<!-- style="margin-left:10px;" -->
3130
<div class="listbox" [ngStyle]="{ 'order' : direction() ? 2 : 1, 'margin-left' : direction() ? '10px' : 0 }">
3231
<button type="button" name="removeBtn" class="btn btn-primary btn-block"
3332
(click)="moveItem(confirmed, available)" [ngClass]="direction() ? 'point-left' : 'point-right'"

lib/dual-list.component.ts

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export class DualListComponent implements DoCheck, OnChanges {
119119
sourceChanges.forEachAddedItem((r:any) => {
120120
// Do not add duplicates even if source has duplicates.
121121
if (this.findItemIndex(this.available.list, r.item, this.key) === -1) {
122-
this.available.list.push( { _id: r.item[this.key], _name: this.makeName(r.item) });
122+
this.available.list.push( { _id: this.makeId(r.item), _name: this.makeName(r.item) });
123123
}
124124
});
125125

@@ -200,7 +200,7 @@ export class DualListComponent implements DoCheck, OnChanges {
200200
this.selectItem(list.pick, item);
201201
}
202202
list.dragStart = true;
203-
event.dataTransfer.setData('text', item[this.key]);
203+
event.dataTransfer.setData('text', item['_id']);
204204
}
205205

206206
allowDrop(event:DragEvent, list:BasicList) {
@@ -223,10 +223,7 @@ export class DualListComponent implements DoCheck, OnChanges {
223223

224224
let id = event.dataTransfer.getData('text');
225225

226-
/* tslint:disable triple-equals */
227-
// Use coercion to filter.
228-
let mv = list.list.filter( (e:any) => e[this.key] == id );
229-
/* tslint:enable triple-equals */
226+
let mv = list.list.filter( (e:any) => e._id === id );
230227
if (mv.length > 0) {
231228
for (let i = 0, len = mv.length; i < len; i += 1) {
232229
list.pick.push( mv[i] );
@@ -239,30 +236,46 @@ export class DualListComponent implements DoCheck, OnChanges {
239236
}
240237
}
241238

242-
trueUp() {
239+
private trueUp() {
243240
let changed = false;
244241

245242
// Clear removed items.
246243
let pos = this.destination.length;
247244
while ((pos -= 1) >= 0) {
248245
let mv = this.confirmed.list.filter( conf => {
249-
return conf._id === this.destination[pos][this.key];
246+
if (typeof this.destination[pos] === 'object') {
247+
return conf._id === this.destination[pos][this.key];
248+
} else {
249+
return conf._id === this.destination[pos];
250+
}
250251
});
251-
252252
if (mv.length === 0) {
253253
// Not found so remove.
254254
this.destination.splice(pos, 1);
255255
changed = true;
256256
}
257257
}
258258

259+
259260
// Push added items.
260261
for (let i = 0, len = this.confirmed.list.length; i < len; i += 1) {
261-
let mv = this.destination.filter( (d:any) => { return (d[this.key] === this.confirmed.list[i]._id); });
262+
let mv = this.destination.filter( (d:any) => {
263+
if (typeof d === 'object') {
264+
return (d[this.key] === this.confirmed.list[i]._id);
265+
} else {
266+
return (d === this.confirmed.list[i]._id);
267+
}
268+
});
262269

263270
if (mv.length === 0) {
264271
// Not found so add.
265-
mv = this.source.filter( (o:any) => { return (o[this.key] === this.confirmed.list[i]._id); });
272+
mv = this.source.filter( (o:any) => {
273+
if (typeof o === 'object') {
274+
return (o[this.key] === this.confirmed.list[i]._id);
275+
} else {
276+
return (o === this.confirmed.list[i]._id);
277+
}
278+
});
266279

267280
if (mv.length > 0) {
268281
this.destination.push(mv[0]);
@@ -279,14 +292,28 @@ export class DualListComponent implements DoCheck, OnChanges {
279292
findItemIndex(list:Array<any>, item:any, key:any = '_id') {
280293
let idx = -1;
281294

282-
// Assumption is that the arrays do not have duplicates.
283-
list.filter( (e:any) => {
295+
function matchObject(e:any) {
284296
if (e._id === item[key]) {
285297
idx = list.indexOf(e);
286298
return true;
287299
}
288300
return false;
289-
});
301+
}
302+
303+
function match(e:any) {
304+
if (e._id === item) {
305+
idx = list.indexOf(e);
306+
return true;
307+
}
308+
return false;
309+
}
310+
311+
// Assumption is that the arrays do not have duplicates.
312+
if (typeof item === 'object') {
313+
list.filter(matchObject);
314+
} else {
315+
list.filter(match);
316+
}
290317

291318
return idx;
292319
}
@@ -451,9 +478,34 @@ export class DualListComponent implements DoCheck, OnChanges {
451478
}
452479
}
453480

481+
private makeId(item:any) : string | number {
482+
if (typeof item === 'object') {
483+
return item[this.key];
484+
} else {
485+
return item;
486+
}
487+
}
488+
454489
// Allow for complex names by passing an array of strings.
455490
// Example: [display]="[ '_type.substring(0,1)', '_name' ]"
456-
makeName(item:any) : string {
491+
private makeName(item:any) : string {
492+
const display = this.display;
493+
494+
function fallback(item:any) {
495+
switch (Object.prototype.toString.call(item)) {
496+
case '[object Number]':
497+
return item;
498+
case '[object String]':
499+
return item;
500+
default:
501+
if (item !== undefined) {
502+
return item[display];
503+
} else {
504+
return 'undefined';
505+
}
506+
}
507+
}
508+
457509
let str = '';
458510

459511
if (this.display !== undefined) {
@@ -498,20 +550,11 @@ export class DualListComponent implements DoCheck, OnChanges {
498550
}
499551
return str;
500552
} else {
501-
return item[this.display];
553+
return fallback(item);
502554
}
503555
}
504556

505-
switch (Object.prototype.toString.call(item)) {
506-
case '[object Number]':
507-
return item;
508-
case '[object String]':
509-
return item;
510-
default:
511-
if (item !== undefined) {
512-
return item[this.display];
513-
}
514-
}
557+
return fallback(item);
515558
}
516559

517560
}

0 commit comments

Comments
 (0)