Отключение orderBy в AngularJS при редактировании списка
После применения orderBy
в моем списке полей ввода, если я редактирую какое-либо поле, он начинает сортировку немедленно, и я теряю фокус. Я попытался выкопать код angular и обнаружил, что они применили что-то вроде $watch на атрибутах orderBy, поэтому всякий раз, когда значение меняет его, он сортирует список. Есть ли способ отключить orderBy при редактировании? Я не хочу позволять orderBy сортировать данные при редактировании текста. Любая помощь будет оценена
Вот мой плункер
Примечание. Я хочу использовать orderBy и не нуждаюсь ни в каких альтернативах, таких как сортировка списка из самого контроллера. Я просто хочу, чтобы orderBy сортировал список один раз на загрузке страницы, а затем оставался довольно.
Ответы
Ответ 1
Я использовал orderBy
в списке, который можно было бы переупорядочить с помощью angular -sortable и столкнулся с аналогичной проблемой.
Мое решение состояло в том, чтобы выполнить заказ вручную, вызвав фильтр orderBy
внутри контроллера, когда страница была инициализирована, а затем вызвала его впоследствии, когда это необходимо, например, в функцию обратного вызова после повторного упорядочения списка.
Ответ 2
Вы можете переопределить директиву, чтобы изменить момент обновления до момента переупорядочения. Вы также можете просто не использовать ng-model
и полагаться на настраиваемую директиву.
Этот поток обсуждает переопределение директивы ввода для изменения обновления модели, которое должно быть вызвано событием tge blur
. Взгляните на fiddle.
Несмотря на то, что вы можете переопределить директиву, вы не должны этого делать и лучшее решение, как описано и приведено в качестве примера @Liviu T. в ниже) будет создавать настраиваемую директиву, которая удаляет привязку привязки к событию и добавляет размытие. Вот директивный код, и вот Livuu plunker:
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 не являются пространствами имен, вам нужно будет удалить любое ранее добавленное событие.
Ответ 3
После @CaioToOn работает, но он разбивается на Angular 1.2.4 (возможно, на всю ветку 1.2.X). Чтобы заставить его работать, вам также нужно объявить приоритет пользовательской директивы равным 1 (директива ввода равна 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 и fooobar.com/questions/350586/... для соответствующего исправления приоритета.
Ответ 4
Другой подход может заключаться в том, чтобы не начинать фокусироваться. Если ваша основная проблема заключается в том, что вы теряете фокус, то вместо отключения 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"/>
Я знаю, что это напрямую не отвечает на ваш вопрос, но может помочь решить основную проблему.
Ответ 5
Нет простого и простого способа выполнить то, что вы хотите, но есть некоторые варианты. Вам нужно будет работать без ng-модели и вместо этого использовать событие blur:
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>
Plunker.
Но имейте в виду, что это нарушит двустороннюю привязку между моделью и представлением.
Ответ 6
Вы можете создать собственный фильтр и вызвать его только при необходимости. Пример, когда вы нажимаете "Grid header" для сортировки или после динамического добавления/удаления значений в массив или просто нажмите кнопку (Refresh Grid)
Вам нужна зависимость Inject Angular фильтр и фильтр сортировки
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>
Ответ 7
Вы можете заморозить текущее упорядочение во время редактирования. Скажем, ваш 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" ) и устанавливаете его в текущую позицию. Я нуль-pad это так, что позиция 0002 сопоставляется до 0011. Может быть, это не обязательно, не знаю.
Обычно вы хотите запомнить текущий порядок, здесь в переменных области "savedOrder" и "savedReverse".
И, наконец, вы скажете angular сортировать по этому новому свойству "pos", и voilà порядок таблицы заморожен, потому что это свойство просто не изменяется при редактировании.
Когда вы закончите редактирование, вы должны сделать обратное. Вы восстанавливаете старый порядок из переменных области "savedOrder" и "savedReverse":
$scope.endEdit = function() {
$scope.orderBy = savedOrderBy;
$scope.reverse = reverse;
};
Если порядок массива $scope.items имеет значение для вас, вам также придется отсортировать его снова до его первоначального заказа.
Ответ 8
Попробуйте использовать переменную области видимости для изменения порядка. В этом случае, когда вы собираетесь заказать, вы вызываете функцию в своем контроллере, которая меняет значение переменной порядка в поле, которое вы хотите заказать, и когда вы редактируете, вы reset его.
Пример:
<li ng-repeat="item in filtered = (items | filter:search) | orderBy:order:rever" >
Итак, здесь мы имеем переменную "порядок", чтобы сообщить 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 = "";
Сбрасывает порядок.
В результате заказы будут эффективными только при нажатии кнопки, а затем он не будет снова заказываться, если вы не нажмете кнопку еще раз.
Это можно изменить, чтобы он заказывал снова, когда, например, вы закончили редактирование элемента, как вы хотели.