2017-01-30 2 views
0

У меня есть выбор с ngOptions на основе массива. Этот массив может измениться.Сохранять выбранную опцию, когда новый массив параметров не содержит значения

Если новое значение массива не содержит выбранное значение параметра, значение параметра устанавливается в undefined с помощью selectController. Есть ли способ предотвратить это ?

Plunker: https://plnkr.co/edit/kao3h5ivHXlP1Wrdx1Ib?p=preview

Сценарий:

  • выберите синий/красный или зеленый цвет
  • нажми на Уменьшенный только есть черные и белые варианты
  • Смотрите, что модель значение остается пустым

Wanted поведение: что стоимость модели остается на синий/красный или зеленый

<!doctype html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8"> 
    <script src="//code.angularjs.org/snapshot/angular.min.js"></script> 
</head> 
<body ng-app="selectExample"> 
    <script> 
angular.module('selectExample', []) 
    .controller('ExampleController', ['$scope', function($scope) { 
    $scope.colorsFull = [ 
     {id:"bk", name:'black'}, 
     {id:"w", name:'white'}, 
     {id:"r", name:'red'}, 
     {id:"be", name:'blue'}, 
     {id:"y", name:'yellow'} 
    ]; 
    $scope.colors = $scope.colorsFull; 
    $scope.selectedColor =$scope.colorsFull[0]; 
    $scope.colorsReduced = [ 
     {id:"bk", name:'black2'}, 
     {id:"w", name:'white2'}]; 
    }]); 
</script> 
<div ng-controller="ExampleController"> 
    <button ng-click="colors=colorsReduced">Reduced</button> 
    <button ng-click="colors=colorsFull">Full</button> 
    <br/> 
    Colors : {{colors}} 
    <hr/> 
    <select ng-model="selectedColor" ng-options="color.name for color in colors track by color.id"> 
    </select> 
    selectedColor:{{selectedColor}} 

</div> 
</body> 
</html> 
+0

Ну, я ставлю, что объект установлен на неопределенный, поэтому ему нечего отслеживать. Когда вы нажмете уменьшенные цвета, я бы вызвал функцию, а затем установил цвета в пустой цвет какого-то типа, я бы не обработал код html, я бы обработал его в контроллере ... можете ли вы иметь цвет, blank " – Maccurt

+0

Держу пари, что он пытается найти новый цвет по идентификатору, и этот идентификатор удален, поэтому он находит неопределенный ... – Maccurt

ответ

1

Вы можете достичь этого путем отслеживания того, что цвет выбран в полном цвета выпадающего меню, и вставить его в восстановленное массив цветов. Во-первых, добавить ng-change директиву, чтобы мы могли следить за выбранный цвет:

<select ng-model="selectedColor" ng-options="color.name for color in colors track by color.id" ng-change="setColor(selectedColor)"> 

И в контроллере:

$scope.setColor = function(color) { 
    if(color !== null) { 
     // Keep track of the color that is selected 
     $scope.previousColor = color; 
    } 
    else { 
     // Changed arrays, keep selected color in model 
     $scope.selectedColor = $scope.previousColor; 
    } 
} 

Теперь ng-model установлен в правильный цвет, когда изменяются массивы, но он будет отображаться пустым в выпадающем списке уменьшенных цветов, потому что опция не существует. Поэтому нам нужно вставить эту опцию в массив. Однако переключение между выпадающими меню приведет к тому, что массив уменьшенных цветов будет продолжать добавлять дополнительные параметры, и мы хотим только помнить опцию, которую мы выбрали из полного массива цветов. Итак, нам нужно создать начальный набор цветов, чтобы вернуться обратно при переключении.

// Keep a copy of the original set of reduced colors 
$scope.colorsReducedInitial = [ 
    {id:"bk", name:'black2'}, 
    {id:"w", name:'white2'}]; 

Наконец, нам нужно вставить выбранную опцию в массив с уменьшенными цветами. Изменение нг нажмите на Сниженная кнопку, чтобы использовать функцию:

<button ng-click="setColorsReduced()">Reduced</button> 

Теперь мы можем вставить опцию, после сброса уменьшенного цвета массива в исходное состояние:

$scope.setColorsReduced = function() { 
    // Revert back to the initial set of reduced colors 
    $scope.colors = angular.copy($scope.colorsReducedInitial); 

    if($scope.previousColor !== undefined) { 
     var found = false; 
     angular.forEach($scope.colorsReducedInitial, function(value, key) { 
      if(value.id == $scope.previousColor.id) { 
       found = true; 
      } 
     }); 

     // If the id is found, no need to push the previousColor 
     if(!found) { 
      $scope.colors.push($scope.previousColor); 
     } 
    } 
} 

Обратите внимание, что мы перемещаются по массиву уменьшенных цветов, чтобы гарантировать, что мы не дублируем любые цвета, такие как черный или белый.

Теперь уменьшенные цвета ng-model имеют выбранный цвет предыдущего выпадающего списка.

Updated Plunkr Demo

+0

Это именно то, что мне нужно. За исключением того, что ng-change не вызывается в init, и поэтому previousColor не задан, и он ошибочно, если вы нажмете уменьшить раньше. Я напишу еще один ответ с возможным дополнением. Не стесняйтесь включать его в свой ответ. Если вы сделаете, я выберу ваш ответ. –

+0

Вы правы, предыдущий цвет не был если вы нажмете кнопку «Уменьшить», я добавил чек, чтобы убедиться, что beforeColor определен до запуска цикла. – Jukebox

0

Использование ниже коды в скрипте

$scope.makeSelected=function(){ 
    $scope.selectedColor =$scope.colorsReduced[0]; 
    } 

И просто добавить вызов этой функции в уменьшенной кнопке линии, как показано ниже

<button ng-click="colors=colorsReduced;makeSelected()">Reduced</button> 

Это будет делать то, что вы хотите достичь.

+0

Извините, но я не вижу, как это делает то, что я хочу. Он заменяет значение цветамиReduced [0] Пожалуйста, lo ok при ответе @ Jukebox –

0

Используя ответ Jukebox, я закончил писать директиву, используя formCtrl. $ Formatters, чтобы получить начальное значение. Он также предлагает возможность хранить PreviousValue в объеме или в локальной переменной:

Использование: <select .... select-keep> или <select .... select-keep="previousColor">

Директива:

.directive('selectKeep', function($parse) { 
    return { 
     require: 'ngModel', 
     link: function (scope, element, attrs, modelCtrl) { 
     var previousValueGetter; 
     var previousValueSetter; 
     if (attrs.selectKeep) { //use a scope attribute to store the previousValue 
       previousValueGetter = $parse(attrs.selectKeep); 
       previousValueSetter = previousValueGetter.assign; 
     } 
     else { //use a local variable to store the previousValue 
       var previousValue; 
       previousValueGetter = function(s) { return previousValue;}; 
       previousValueSetter = function(s, v) { previousValue = v;}; 
     } 

     //store the initial value 
     modelCtrl.$formatters.push(function(v) { 
       previousValueSetter(scope, v); 
      return v; 
     }); 

     //get notified of model changes (copied from Jukebox's answer) 
     modelCtrl.$viewChangeListeners.push(function() { 
      if (modelCtrl.$modelValue !== null) { 
      previousValueSetter(scope, modelCtrl.$modelValue); 
      } else { 
      modelCtrl.$setViewValue(previousValueGetter(scope)); 
      } 
     }); 
     } 
    }; 

Plunker

Edit: это имеет недостаток, форма становится грязной, даже если значение не изменяется. Я должен был добавить эти строки в else viewChangeListener, но это выглядит не очень хорошо. Любые идеи?:

... 
} else { 
    modelCtrl.$setViewValue(previousValueGetter(scope)); 
    //set pristine since this change is not a real change 
    modelCtrl.$setPristine(true); 
    //check if any other modelCtrl is dirty. If not, we will have to put the form as pristine too 
    var oneDirty =_.findKey(modelCtrl.$$parentForm, function(otherModelCtrl) { 
    return otherModelCtrl && otherModelCtrl.hasOwnProperty('$modelValue') && otherModelCtrl !== modelCtrl && otherModelCtrl.$dirty; 
    }); 
    if (!oneDirty) { 
    modelCtrl.$$parentForm.$setPristine(true); 
    } 
} 
Смежные вопросы