ng-sortable
ng-sortable copied to clipboard
table sort issue
Issue
Hi I'm having a weird issue when sorting a full width table, when dragging a row onto a new row it breaks (not a great word but finding it hard to describe the issue) the table layout. I have a screen cast of the issue which illustrates the issue video here
Have you seen this issue before?
environment
AngularJS v1.4.8 Chrome 47.0.2526.111 ng-sortable 1.3.2
template
<table class="full-table">
<thead>
<tr class="table__header">
<th class="table__first-row">Title</th>
<th>Views</th>
<th>Updated</th>
<th>Pull request</th>
<th class="table__padding--right">
<span>Sort chapters</span>
<toggle toggle-model="chapCtrl.state.toggle" change="chapCtrl.toggleSort()"></toggle>
</th>
</tr>
</thead>
<tbody as-sortable="chapCtrl.dragControlListeners" ng-model="chapCtrl.chapters">
<tr class="table__row" ng-repeat="chapter in chapCtrl.chapters" as-sortable-item>
<td class="table__first-row">
{{chapter.title}}
</td>
<td>
{{chapter.views}}
</td>
<td>
{{chapter.created | date:'dd-MM-yyyy'}}
</td>
<td>
<span ng-if="!chapter.pullrequest.set">none</span>
<a ng-if="chapter.pullrequest.set" href="#">waiting to merge</a>
</td>
<td class="table__padding--right table__last-row" >
<span ng-if="!chapCtrl.state.toggle">delete</span>
<span ng-if="chapCtrl.state.toggle" as-sortable-item-handle>sort</span>
</td>
</tr>
</tbody>
</table>
controller
(function() {
'use strict';
angular.module('docd')
.controller('ChaptersController', ChaptersController);
function ChaptersController(chapters) {
var vm = this;
vm.state = {
toggle: true
};
vm.chapters = chapters;
vm.toggleSort = function() {
console.log('working');
};
vm.toggleListeners = {
orderChanged: function() {
console.log('working');
}
};
}
ChaptersController.$injext = ['chapters'];
})();
@httpete-ire while dragging the drag element (sortable-item) gets the css of the 'sortable', that could be the reason, please check.
@a5hik sortable does not have any css applied to it.
I have tracked the issue down to as-sortable-placeholder being inserted into the first td, this causes the whole table to increase the width.
I created a new video which highlights this better. The td has a red background applied and __as-sortable-placeholder has a green background.
I will try recreate the issue in a jsfiddle so you can see the issue
I have recreated the issue using the table demo
This is the only css applied to the demo
#table-container {
width: 100%;
}
td, th {
text-align: left;
}
td {
background: grey;
}
tr {
background: red;
}
.as-sortable-placeholder {
background: green;
}
Any thoughts on the issue?
@a5hik any thoughts on what might be happening?
I'd be happy to try and fix the issue if you could point me in the right direction :+1:
@httpete-ire I will look in to this in a day or two.
I am having a similar issue. I have tracked it down to the as-sortable-placeholder class changing the CSS display attribute to "block". In ng-sortable.css: .as-sortable-item,.as-sortable-placeholder{display:block;}
This is causing my table rows to be horizontally compressed. It looks like you recognized the issue, as you have a TODO item in the table demo header. If I disable the display: block attribute the rows format correctly but I see the drag-n-drop problem described by the original poster, @httpete-ire.
@jefff888 good find. I got it to work in my use case with the code below.
.as-sortable-item,
.as-sortable-placeholder {
display: table-row !important;
}
@a5hik one suggestion for this would be to pass an optional settings object on the as-sortable-item directive. If the table setting is set to true, display becomes table-row. This shouldnt break compatibility with other DOM elements. What do you think? I could try to implement this and make a PR
<tr class="table__row" ng-repeat="chapter in chapCtrl.chapters" as-sortable-item="{ table: true}">
@a5hik I think you idea to pass a table setting to as-sortable-item is a good one. It looks like it will provide some needed isolation to the developer rather than having to experiment with CSS setting
I am also having the same problem. Looking through the code I stumbled upon the insertBefore and insertAfter functions which seem to be the culprits here:
/**
* Inserts the placeHolder in to the targetScope.
*
* @param targetElement the target element
* @param targetScope the target scope
*/
function insertBefore(targetElement, targetScope) {
// Ensure the placeholder is visible in the target (unless it's a table row)
if (placeHolder.css('display') !== 'table-row') {
placeHolder.css('display', 'block');
}
if (!targetScope.sortableScope.options.clone) {
targetElement[0].parentNode.insertBefore(placeHolder[0], targetElement[0]);
dragItemInfo.moveTo(targetScope.sortableScope, targetScope.index());
}
}
/**
* Inserts the placeHolder next to the targetScope.
*
* @param targetElement the target element
* @param targetScope the target scope
*/
function insertAfter(targetElement, targetScope) {
// Ensure the placeholder is visible in the target (unless it's a table row)
if (placeHolder.css('display') !== 'table-row') {
placeHolder.css('display', 'block');
}
if (!targetScope.sortableScope.options.clone) {
targetElement.after(placeHolder);
dragItemInfo.moveTo(targetScope.sortableScope, targetScope.index() + 1);
}
}
So there is some check for "display: table-row", however, at least in Chromium v48, these checks do nothing (even if placeHolder contains a
I don't have the time to delve more into the code right now, I actually do not understand what exactly the placeHolder variable contains (since it doesn't seem to be a DOM model). Anyhow, if these checks would work (maybe it's just Chrome) I believe we wouldn't have this problem.
The css:
tr.as-sortable-item { display: table-row; }
tr.as-sortable-placeholder { display: table-row !important; }
works nicely as a temporary fix though, thanks @httpete-ire.
I hope my posting sheds if only a bit more light on the issue. Thanks all!
I am also having the same problem too!
I ran into a similar issue, but not while using a table. I found that when an item is being dragged, .as-sortable-placeholder is set to display: inline-block. This caused the width of the container to increase.
My quick solution looked like this.
as-sortable-placeholder { display: inline-block !important; }
Unfortunately, that eliminates a user's ability to see exactly where an item is being dropped. It would be great if a solution was found for this did not cause the container's width to expand but allowed a user to see where they were placing an item.
@a5hik more than a year passed since you said "I will look in to this in a day or two." Any news on this?
I've run into this issue as well. The way I see it, there are two issues:
- The placeholder is not displayed as table-row, which stretches the first column of the table
- The element with the row actually being dragged is put into the DOM with a tbody tag around it. This means you get a tbody inside of the original tbody instead of a tr inside of the original tbody, which leads to the data being all crammed together as the column widths of the row being dragged are not linked to the column widths of the original table. (Note that the placeholder is just a tr inside of the original tbody, which is what we need.)
I fixed the first issue like this (based on a fix I found above):
tr.as-sortable-item,
tr.as-sortable-placeholder{
display: table-row !important;
}
For the second issue, I decided to hide the the element with the row being dragged and instead replace the placeholder with that element. I also add an additional class to the placeholder to make it stand out from the rest of the rows.
tbody.as-sortable-drag,
tbody.as-sortable-dragging {
visibility: hidden;
}
$scope.sortableOptions = {
placeholder: sortableOptionsPlaceholderGenerator,
additionalPlaceholderClass: 'active'
};
function sortableOptionsPlaceholderGenerator(itemData) {
return angular.copy(itemData.element[0]);
}
Dragging looks a bit less fluid this way, but it solves all of the issues I had without having to change the ng-sortable source code.