问题
I am trying to build a custom element to manage simple lists, renaming the items and changing their order. Unfortunately I'm noticing some weird behavior that's actually really hard to pin down.
- Typing into the inputs does not appear to be recognized as changes for Aurelia to update the item
- When typing/changing one item after page load and then changing its position in array via those methods, the index of the item seems lost (turns into -1). If the item isn't changed via input field, the index in the array is recognized correctly and sorting works.
Are there any known issues with arrays, binding and maybe even child elements? What are the battle tested approached to get the desired behavior? Thanks a lot!
Parent Element
...
<list items.bind="list"></list>
...
List Element
<template>
<div class="input-group" repeat.for="item of items">
<input value.bind="item" class="input" type="text" placeholder="Item" autofocus>
<a click.delegate="deleteItem(item)">X</a>
<a click.delegate="moveItemUp(item)">^</a>
<a click.delegate="moveItemDown(item)">v</a>
</div>
<a click.delegate="addItem()">Add Item</a>
List JS
export class List {
@bindable({defaultBindingMode: bindingMode.twoWay}) items;
constructor() {}
addItem() {
this.items.push('new')
}
deleteItem(item) {
let i = this.items.indexOf(item)
this.items.splice(i, 1)
}
moveItemUp(item) {
let i = this.items.indexOf(item)
if (i === 0) return
let temp = item
this.items.splice(i, 1)
this.items.splice(i - 1, 0, temp)
}
moveItemDown(item) {
let i = this.items.indexOf(item)
if (i === this.items.length) return
let temp = item
this.items.splice(i, 1)
this.items.splice(i, 0, temp)
}
}
回答1:
repeat.for
has several contextual variables you can leverage of. [Documentation]
Gist demo: https://gist.run/?id=1c8f78d8a774cc859c9ee2b1ee2c97f3
- Current item's correct position can be determined by using
$index
contextual variable instead ofitems.indexOf(item)
. - Databound values of inputs will be preserved by passing
item
toitems.slice(newIndex, item)
.
If you need to observe array changes, CollectionObserver could be a great fit for that. More details here: Observing Objects and Arrays in Aurelia.
list.js
import { bindable, bindingMode } from 'aurelia-framework';
export class List {
@bindable({defaultBindingMode: bindingMode.twoWay}) items;
constructor() {}
addItem() {
this.items.push(`New Item ${this.items.length + 1}`);
}
deleteItem(i) {
this.items.splice(i, 1);
}
moveItemUp(i, item) {
if (i === 0)
return;
this.moveItem(i, i - 1, item);
}
moveItemDown(i, item) {
if (i === this.items.length - 1)
return;
this.moveItem(i, i + 1, item);
}
moveItem(oldIndex, newIndex, item) {
this.items.splice(oldIndex, 1);
this.items.splice(newIndex, 0, item);
}
}
list.html
<template>
<div class="input-group" repeat.for="item of items">
<input value.bind="item" class="input" type="text" placeholder="Item" autofocus> |
<a click.delegate="deleteItem($index)"><i class="fa fa-close"></i></a> |
<a click.delegate="moveItemUp($index, item)"><i class="fa fa-arrow-up"></i></a> |
<a click.delegate="moveItemDown($index, item)"><i class="fa fa-arrow-down"></i></a>
</div>
<a click.delegate="addItem()">Add Item</a>
</template>
回答2:
I believe this has to do with the immutability of strings. That is, strings can't be modified, so when you modify a value in the textbox, the array element is actually replaced instead of modified. That's why you're losing the binding.
Here's a gist that demonstrates it working correctly when binding to a list of objects.
https://gist.run/?id=22d186d866ac08bd4a198131cc5b4913
来源:https://stackoverflow.com/questions/39970544/change-array-order-in-aurelia-weird-behaviour