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 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 в раскрывающемся списке или полное удаление свойства order в целом из item-объекта.

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

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

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

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

3 ответа

Решение

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

<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, потому что это также делало странные вещи.

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

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

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

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;
      });
    }
  };
});

( планк)

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

.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+'';
            });
        }
    }
});
Другие вопросы по тегам