AngularJS: ng-модель переключения int в строку

В настоящее время я работаю над приложением на Angular. Пока что все идет хорошо. Я действительно, действительно новичок в angular и удивлен, что это заняло так много времени для первого настоящего блокпоста.

Ситуация:

У меня есть массив объектов, каждый из которых имеет порядок.

category.items = [{id: 1, order: 1, type: {}, ...}, {id: 54, order: 2, type: {}, ...}, {id: 3, order: 3, type: {}, ...}]

Пользователь должен иметь возможность переставить эти элементы. Новый порядок должен быть установлен в свойство объекта 'порядок'.

В HTML эти объекты отображаются так:

<div class="category">
    <div class="item" ng-repeat="(itemIndex, item) in category.items track by $index">
        <div class="header">
        </div>
    </div>
</div>

В header-div у меня есть поле ввода, введите select.

<select ng-model="item.order"  ng-change="changeItemOrder((itemIndex + 1), item.order, itemIndex)">
  <option ng-repeat="item in category.items" ng-value="($index + 1)">{{$index + 1}}</option>
</select>

Код для changeItemOrder:

$scope.changeItemOrder = function(old_order, new_order, item_index) {
    new_order = parseInt(new_order);
    if (old_order != new_order) {
        var upper = Math.max(old_order, new_order);
        var lower = Math.min(old_order, new_order);

        angular.forEach($scope.category.items, function(item, key) {
            if (item_index != key) {
                if (new_order < old_order) {
                    if (item_index >= new_order && (key + 1) >= lower && (key + 1) <= upper) {
                        item.order = (parseInt(item.order) + 1);
                    }
                } else if (new_order > old_order) {
                    if (item_index <= old_order && (key + 1) <= upper && (key + 1) >= lower) {
                        item.order = (parseInt(item.order) - 1);
                    }
                }
            } else {
                item.order = parseInt(new_order);
            }
        });

        $scope.reorderItems();
    }
};

(ReorderItems просто вызывает угловую сортировку с механизмом сортировки по умолчанию, сравнивающим заказы и возвращающим -1, 1 или 0.)

Вот где я обнаружил/заметил/определил одну из самых серьезных ошибок в одном из возможных решений этой проблемы. Здесь я заметил, что мой INT как-то конвертируется в строку, так как при рендеринге в выпадающий список добавляется опция со значением 'string: 2'.

Я пробовал ng-options всеми возможными способами, но даже они приводили к проблемам. Я сделал ng-options, используя item.order как item.order в... и так далее, что просто заставляло порядок переключаться, пока все элементы не имели одинаковый порядок. Попытка использования различных методов группировки или trackbys привела к различным ошибкам, например, внезапное введение NaN en NULL в раскрывающемся списке или полное удаление свойства порядка в целом из объекта-объекта.

До сих пор наименее подверженным ошибкам решением было использование ng-repeat в моих опциях. Это приводит только к несоответствию типа item.order.

Теперь, после долгих поисков, потратив часы на стекопоток (особенно перед тем, как написать этот вопрос с помощью этой изящной мелочи в поиске вопросов), я пришел к вам.

  1. Как я могу остановить/обойти поведение, когда мой item.order переключен с INT на STRING?
  2. Если это невозможно, как заставить $ index быть строкой, чтобы модель (строка) соответствовала значению (строке)
  3. Если это невозможно, как я могу написать свои ng-параметры, чтобы получить желаемое поведение? (Я серьезно пробовал много, от отслеживания до разных, как и для заявлений, все привело к различным ошибкам)

    При начальной загрузке все селекторы показывают правильное выбранное значение, поэтому все item.order изначально INT (я получаю их из нашего API), только после взаимодействия все, кроме объекта, который вызвал переупорядочение, испортились.

Ответ 1

В конце концов я смог решить это, выполнив:

<div class='header'>
    <select ng-model="item.order" ng-change="changeItemOrder((itemIndex + 1), item.order, itemIndex)">
        <option ng-repeat="thing in category.items" ng-selected="(item.order === ($index + 1))" value="{{$index + 1}}">{{$index + 1}}</option>
    </select>
</div>

Это, и только это решение (до сих пор) имеет то, что я хочу/нуждаюсь. Мне нужна модель item.order, чтобы отслеживать текущую позицию и показывать правильное значение при начальной загрузке. Вы не можете установить модель на questionIndex, потому что это беспокоит другие HTML-элементы, я не могу установить его в $index, потому что это также было странным.

В то время как другие решения, предлагаемые здесь, "работают", они не приводят к спецификации, которая мне дана. Поэтому я предполагаю, что это характерно.

Ответ 2

Он изменяется на строку, потому что HTML не имеет понятия о том, что такое целое число. В конечном счете выбранные переменные считываются из DOM и передаются на angular, и поэтому он меняет свой тип.

Вы можете заставить модель всегда содержать целое число с директивой

directive('forceInt', function() {
  return {
    require: 'ngModel',
    link: function(scope, element, attrs, controller) {     
      controller.$parsers.push(function(value) {
        if (typeof value === 'string') {
          value = parseInt(value, 10);  
          controller.$setViewValue(value);
          controller.$render();
        }
        return value;
      });
    }
  };
});

(plunk)

Но я вижу, что кто-то уже указал, что могут быть лучшие способы отслеживать заказ. FWIW, эта директива, по крайней мере, будет следить за тем, чтобы модель никогда не была строкой.

Ответ 3

.directive('numberToString', function () {
    return {
        require: 'ngModel',
        link: function (scope, element, attrs, ngModel) {
            ngModel.$parsers.push(function (value) {
                return '' + value;
            });
            ngModel.$formatters.push(function (value) {
                return value+'';
            });
        }
    }
});