avatarGithub

ng-repeat与ui-sortable一起使用的问题与解决方案

2015-08-12

demo

问题

通过拖拽把0拖至2的位置,再把2拖至3的位置。理应是[1, 0, 3, 2, 4]的列表却变成了[1, 0, 4, 3, 2]。控制台中输出的是当前的lists,但实际的UI与我们的model没有对应起来。

原因

初始化


<ul sortable="" class="ui-sortable">
<!-- ngRepeat: l in lists -->
<li ng-repeat="l in lists" class="ng-scope">
<span class="ng-binding">0</span>
</li>
<!-- end ngRepeat: l in lists -->
<li ng-repeat="l in lists" class="ng-scope">
<span class="ng-binding">1</span>
</li>
<!-- end ngRepeat: l in lists -->
<li ng-repeat="l in lists" class="ng-scope">
<span class="ng-binding">2</span>
</li>
<!-- end ngRepeat: l in lists -->
<li ng-repeat="l in lists" class="ng-scope">
<span class="ng-binding">3</span>
</li>
<!-- end ngRepeat: l in lists -->
<li ng-repeat="l in lists" class="ng-scope">
<span class="ng-binding">4</span>
</li>
<!-- end ngRepeat: l in lists -->
</ul>

第一次拖拽


<ul sortable="" class="ui-sortable">
<!-- ngRepeat: l in lists -->
<li ng-repeat="l in lists" class="ng-scope">
<span class="ng-binding">1</span>
</li>
<!-- end ngRepeat: l in lists -->
<li ng-repeat="l in lists" class="ng-scope">
<span class="ng-binding">2</span>
</li>
<li ng-repeat="l in lists" class="ng-scope">
<span class="ng-binding">0</span>
</li>
<!-- end ngRepeat: l in lists -->
<!-- end ngRepeat: l in lists -->
<li ng-repeat="l in lists" class="ng-scope">
<span class="ng-binding">3</span>
</li>
<!-- end ngRepeat: l in lists -->
<li ng-repeat="l in lists" class="ng-scope">
<span class="ng-binding">4</span>
</li>
<!-- end ngRepeat: l in lists -->
</ul>

ng-repeat生成每一个element都会带有一个注释,这个注释相当于在页面上的分隔符,便于指令更好处理lists变化后相应的ui变化。而在移动后我们可以看到注释乱了,导致指令的处理发生了错乱。

解决方案

  1. 保存移动前的元素节点($('ul').contents()),在sortable的stop事件中取消掉sortable,修改lists,通过ng-repeat来实现排序。即只是通过sortable来获取移动的index,然后修改数组,通过angular来实现排序。ui-sortable即是这样实现的。

  2. ng-repeat= "l in lists track by Math.random()" 这里简单解释一点ng-repeat的内部实现,ng-repeat在初始化的时候会遍历lists并保存数组每一个元素对应的idscopeelement,同时会保存一个map用来判断数组中的元素是否是新元素,如果提供了track by,该map的key值即为track by对应的值,如果没有提供,key值由angular生成。key值对应的value在初始化的过程中设为true。 在lists发生变化的时候,ng-repeat指令会在遍历lists的过程中判断每一个元素是新元素还是旧元素,如果是旧元素,那么直接利用保存好的scopeelement即可,如果是新元素,就重新生成。 而在拖拽的过程中,会导致内部保存的元素与页面实际元素不符,导致错乱。 原因找到,就好解决了。只要每次都生成新的元素就可以了。而ng-repeat指令是通过map来判断是否是新值的,只要每一次track by的值是不同的,问题就能够解决了。