Отключение orderBy в AngularJS при редактировании списка
После применения orderBy
в моем списке полей ввода, если я редактирую любое поле, он сразу начинает сортировку, и я теряю фокус. Я попытался покопаться в угловом коде и обнаружил, что они применяют что-то вроде $watch к атрибутам orderBy, поэтому при каждом изменении значения сортирует список. Есть ли способ отключить orderBy при редактировании? Я не хочу, чтобы orderBy сортировал данные при редактировании текста. Любая помощь будет оценена
Примечание: я хочу использовать orderBy, и мне не нужны никакие альтернативы, такие как сортировка списка из любого контроллера. Я просто хочу, чтобы порядок сортировал список один раз при загрузке страницы, а затем оставался достаточно.
8 ответов
Я использовал orderBy
в списке, который можно переупорядочить с помощью angular-sortable и столкнулся с аналогичной проблемой.
Моим решением было выполнить заказ вручную, позвонив по телефону orderBy
фильтруйте внутри контроллера, когда страница была инициализирована, и затем вызывайте ее впоследствии, когда это необходимо, например, в функции обратного вызова после переупорядочения списка.
Вы можете переопределить директиву, чтобы изменить момент обновления на тот момент, когда вы хотите изменить порядок. Вы также можете просто не использовать ng-model
и полагаться на пользовательскую директиву.
В этой теме обсуждается переопределение директивы input для изменения обновления модели, запускаемого tge blur
событие. Посмотрите на скрипку.
Хотя вы могли бы переопределить директиву, вы не должны этого делать, и лучшим решением, как объяснено и проиллюстрировано Liviu T.. в комментариях ниже, будет создание пользовательской директивы, которая удаляет привязку keyup события и добавляет размытую, Вот код директивы, а вот плунжер Ливиу:
app.directive('modelChangeBlur', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attr, ngModelCtrl) {
if (attr.type === 'radio' || attr.type === 'checkbox') return;
elm.unbind('input').unbind('keydown').unbind('change');
elm.bind('blur', function() {
scope.$apply(function() {
ngModelCtrl.$setViewValue(elm.val());
});
});
}
};
});
<input type="text" ng-model="variable" model-change-blur/>
К сожалению, поскольку события Angular не являются пространствами имен, вам придется удалить любое ранее добавленное событие.
Другой подход может состоять в том, чтобы не потерять фокус для начала. Если ваша основная проблема заключается в том, что вы теряете фокус, то вместо отключения orderBy добавьте эту директиву к вашему входу:
app.directive("keepFocus", ['$timeout', function ($timeout) {
/*
Intended use:
<input keep-focus ng-model='someModel.value'></input>
*/
return {
restrict: 'A',
require: 'ngModel',
link: function ($scope, $element, attrs, ngModel) {
ngModel.$parsers.unshift(function (value) {
$timeout(function () {
$element[0].focus();
});
return value;
});
}
};
}])
Тогда просто:
<input keep-focus ng-model="item.name"/>
Я знаю, что это не дает прямого ответа на ваш вопрос, но может помочь решить основную проблему.
После @CaioToOn работает, но он ломает в Angular 1.2.4 (возможно, всю ветку 1.2.X). Чтобы заставить его работать, вам также нужно объявить приоритет пользовательской директивы равным 1 (директива input - 0). Директива становится:
app.directive('modelChangeBlur', function() {
return {
restrict: 'A',
priority: 1,
require: 'ngModel',
link: function(scope, elm, attr, ngModelCtrl) {
if (attr.type === 'radio' || attr.type === 'checkbox') return;
elm.unbind('input').unbind('keydown').unbind('change');
elm.bind('blur', function() {
scope.$apply(function() {
ngModelCtrl.$setViewValue(elm.val());
});
});
}
};
});
См. http://plnkr.co/edit/rOIH7W5oRrijv46f4d9R?p=preview и /questions/35189684/angularjs-120-rc2-vs-120-privyazka-elementov/35189691#35189691 для соответствующего исправления приоритета.
Попробуйте использовать переменную области видимости, чтобы изменить порядок. В этом случае, когда вы собираетесь делать заказ, вы вызываете функцию в вашем контроллере, которая изменяет значение переменной порядка на поле, по которому вы хотите заказать, а когда вы редактируете, вы сбрасываете его.
Пример:
<li ng-repeat="item in filtered = (items | filter:search) | orderBy:order:rever" >
Итак, здесь у нас есть переменная "order", чтобы указать ng-repeat, по какому полю должен быть упорядочен список. Кнопка вызывает функцию в контроллере, которая изменяет значение "порядка".
<button type="button" id="orderName" click="changeOrder('item.name')" >Name order </button>
<button type="button" id="orderDate" click="changeOrder('item.date')" >Date order </button>`
А потом, в функции changeOrder
$scope.order = param;
Где "param" - это поле, по которому вы хотите упорядочить. Если вы ничего не сделаете, у вас возникнет та же проблема, что и у вас, так что после того, как вы присвоите правильное значение переменной порядка, вы идете
$scope.order = "";
Что сбрасывает порядок. В результате упорядочение будет эффективным только при нажатии кнопки, а затем оно не будет повторяться, если вы не нажмете кнопку еще раз. Это можно изменить, чтобы он снова заказывал, когда, например, вы закончили редактирование элемента, как вы хотели.
Вы можете заморозить текущий порядок во время редактирования. Скажем, ваш HTML выглядит так:
<tbody ng-repeat="item in items | orderBy:orderBy:reverse">
<tr ng-click="startEdit()">
<td>{{item.name}}</td>
</tr>
</tbody>
В вашем контроллере вы пишете:
var savedOrderBy, savedReverse;
$scope.startEdit() = function() {
$scope.items = $filter('orderBy')($scope.items, $scope.orderby, $scope.reverse);
for (var i = 0; i < $scope.items.length; ++i) {
if (i < 9999) {
$scope.items[i]['pos'] = ("000" + i).slice(-4);
}
}
savedOrderBy = $scope.orderBy;
savedReverse = $scope.reverse;
$scope.orderBy = 'pos';
$scope.reverse = false;
};
Прежде чем пользователь начнет редактирование, вы сначала сортируете текущие элементы в том же порядке, в котором они в настоящее время отображаются на странице. Вы делаете это, вызывая orderBy $filter() с текущими параметрами сортировки.
Затем вы перебираете свои - теперь отсортированные - элементы, и добавляете произвольное свойство (здесь "pos") и устанавливаете его в текущую позицию. Я обнуляю его так, чтобы позиция 0002 смещалась до 0011. Может быть, это и не нужно, не знаю.
Обычно вы хотите запомнить текущее упорядочение, здесь, в переменных области действия "SavedOrder" и "SavedReverse".
И, наконец, вы указываете angular отсортировать по этому новому свойству "pos" и вуаля, порядок таблиц заморожен, потому что это свойство просто не изменяется при редактировании.
Когда вы закончите редактирование, вы должны сделать наоборот. Вы восстанавливаете старое упорядочение по переменным области действия "SavedOrder" и "SavedReverse":
$scope.endEdit = function() {
$scope.orderBy = savedOrderBy;
$scope.reverse = reverse;
};
Если для вас важен порядок массива $ scope.items, вам также придется снова отсортировать его в исходном порядке.
Нет простого или простого способа выполнить то, что вы хотите, но есть несколько вариантов. Вам придется работать без ng-модели, а вместо этого использовать событие размытия:
JS:
var app = angular.module('myapp', []);
app.controller('MainCtrl', function($scope) {
$scope.items = [
{ name: 'foo', id: 1, eligible: true },
{ name: 'bar', id: 2, eligible: false },
{ name: 'test', id: 3, eligible: true }
];
$scope.onBlur = function($event, item){
// Here you find the target node in your 'items' array and update it with new value
_($scope.items).findWhere({id: item.id}).name = $event.currentTarget.value;
};
});
app.directive('ngBlur', function($parse) {
return function ( scope, element, attr ) {
var fn = $parse(attr.ngBlur);
element.bind( 'blur', function ( event, arg ) {
scope.$apply( function(){
fn(scope, {
$event : event,
arg: arg
});
});
});
};
});
HTML:
<div ng-controller="MainCtrl">
<div ng-repeat="item in items | orderBy:'name'" >
<input value="{{item.name}}" ng-blur="onBlur($event, item)"/>
</div>
</div>
Но имейте в виду, что это нарушит двустороннюю связь между моделью и представлением.
Вы можете создать собственный фильтр и вызывать его только при необходимости. Пример, когда вы нажимаете "Заголовок сетки" для сортировки или после динамического добавления / удаления значений в массив, или просто нажмите кнопку (Обновить сетку)
Вам нужно зависимость Inject Angular filter и sort filter
angular
.module('MyModule')
.controller('MyController', ['filterFilter', '$filter', MyContFunc])
function ExpenseSubmitter(funcAngularFilter, funcAngularFilterOrderBy) {
oCont = this;
oCont.ArrayOfData = [{
name: 'RackBar',
age: 24
}, {
name: 'BamaO',
age: 48
}];
oCont.sortOnColumn = 'age';
oCont.orderBy = false;
var SearchObj = {
name: 'Bama'
};
oCont.RefreshGrid = function() {
oCont.ArrayOfData = funcAngularFilter(oCont.ArrayOfData, SearchObj);
oCont.ArrayOfData = funcAngularFilterOrderBy('orderBy')(oCont.ArrayOfData, oCont.sortOnColumn, oCont.orderBy);
}
}
и вызвать в HTML что-то вроде:
<table>
<thead>
<tr>
<th ng-click="oCont.sortOnColumn = 'age'; oCont.RefreshGrid()">Age</th>
<th ng-click="oCont.sortOnColumn = 'name'; oCont.RefreshGrid()">Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="val in oCont.ArrayOfData">
<td>{{val.age}}</td>
<td>{{val.name}}</td>
</tr>
</tbody>
</table>