AngularJS: невозможно отформатировать нулевое или неопределенное значение с помощью ngModelController $formatters

У меня есть директива AngularJS, которая содержит текстовый ввод, который имеет собственную модель, которая получает значение из области действия контроллера через директиву ng-model.

Взгляните на это перо (и код ниже): http://codepen.io/ericwshea/pen/KwXRyr

Проблема в том, что иногда эта модель оказывается нулевым или неопределенным значением, и в этом случае я хотел бы использовать ngModelController для ввода текста, чтобы отформатировать отображение нулевого значения в текстовом вводе, чтобы оно было чем-то вроде «NULL».

Это работает, если значение представляет собой некоторую произвольную строку, которую я сопоставляю в средстве форматирования, но не в том случае, если значение равно null (я также тестировал с неопределенными, теми же результатами).

Любое понимание/обходной путь по этому поводу, или это просто недостаток $formatters?

HTML:

<div ng-app="app" class="container">
  <div ng-controller="ctrl" class="col-md-12">
    <form>
      <input-directive ng-model="model"></input-directive>
      <input-directive ng-model="model2"></input-directive>
      <div ng-if="model">Model 1: {{model}}</div>
      <div ng-if="model2">Model 2: {{model2}}</div>
    </form>
  </div>
</div>

ЯВАСКРИПТ:

angular.module('app', [])
  .controller('ctrl', function($scope) {
    $scope.model = null;
    $scope.model2 = 'make this null';
  })

  .directive('inputDirective', function() {
    var template = 
        '<div>'+
          '<div class="input-group">'+
            '<input type="text" class="form-control" ng-model="localModel">'+
            '<span class="input-group-btn">'+
              '<button ng-click="save()" class="btn btn-default" type="button">Save</button>'+
            '</span>'+
          '</div>'+
        '</div>';

    function link (scope, elem, attr) {
      var inputModelCtrl = elem.find('input').controller('ngModel');

      function formatter(val) {
        if (val === 'make this null') {
          return scope.nullValue;
        }
        if (val === null) {
          return scope.nullValue;
        }
        return val;
      }

      scope.nullValue = 'NULL';
      scope.localModel = scope.ngModel;
      scope.save = function() {
        scope.ngModel = scope.localModel;
      }
      inputModelCtrl.$formatters.push(formatter);
    }

    return {
      restrict: 'E', 
      replace: true,
      require: 'ngModel',
      template: template,
      link: link,
      scope: {
        ngModel: '='
      }
    }
  })
;

person Eric    schedule 03.02.2015    source источник


Ответы (1)


Мне удалось найти обходной путь для этого. Я инициализирую значение при создании директивы, помещая часы в ngModel, ловя нулевое значение и делая его «NULL», который на самом деле будут форматировать angular $formatters. Я форматирую значение представления localModel в , и оно работает.

Чтобы убедиться, что значение модели правильное, когда я отправляю «NULL» обратно из директивы в область действия контроллера, я использую $parser в ngModelController, чтобы отслеживать «NULL» и преобразовывать его в ноль.

Чтобы завершить круг, $watch внутри директивы ngModel делает так, что если значение установлено равным null вне директивы, localModel повторно инициализируется до «NULL» и форматируется для правильного отображения.

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

Вы можете увидеть изменения здесь: http://codepen.io/ericwshea/pen/KwXRyr.

angular.module('app', [])
  .controller('ctrl', function($scope) {
    $scope.model = null;
    $scope.getModel = function() {
      return $scope.model === null ? 'null' : $scope.model;
    }
    $scope.setToNull = function() {
      $scope.model = null;
    }
  })

  .directive('inputDirective', function() {
    var template = 
        '<div>'+
          '<div class="input-group">'+
            '<input type="text" class="form-control" ng-model="localModel">'+
            '<span class="input-group-btn">'+
              '<button ng-click="save()" class="btn btn-default" type="button">Save</button>'+
            '</span>'+
          '</div>'+
        '</div>';

    function link (scope, elem, attr, ngModelCtrl) {
      var inputModelCtrl = elem.find('input').controller('ngModel');
      var nullDisplay = '<NULL>';

      function ngModelParser(val) {
        if (val === 'NULL') return null;
        return val;
      }

      function localModelFormatter(val) {
        if (val === 'NULL') return nullDisplay;
        return val;
      }

      scope.save = function() {
        ngModelCtrl.$setViewValue(scope.localModel);
        console.log(ngModelCtrl.$modelValue);
      }

      scope.$watch('ngModel', function(val) {
        if (val === null) val = 'NULL';
        scope.localModel = val;
      })

      inputModelCtrl.$formatters.push(localModelFormatter);
      ngModelCtrl.$parsers.push(ngModelParser);
    }

    return {
      restrict: 'E', 
      replace: true,
      require: 'ngModel',
      template: template,
      link: link,
      scope: {
        ngModel: '='
      }
    }
  })
;

Я надеюсь, что это поможет всем, кто сталкивается с подобной проблемой, хотя этот сценарий довольно специфичен (настолько специфичен, что мне пришлось отвечать на него самому!)

person Eric    schedule 19.02.2015