Подтвердить что ты не робот

AngularJS, как заставить ввод повторно отображаться при размытии

У меня есть специальный код проверки, который включает в себя $formatter. (Я храню валюту в фунтах за правильность, но показываю в pounds.pence.)

Если пользователь вводит "10" во вход (который является допустимым значением), вход остается отображающим "10" после перехода в следующее поле.

Я хотел бы, чтобы он отображал 10,00 для согласованности.

Если модель изменила значение до 1000, тогда форматировщик отобразил бы поле "10.00".

Я хотел бы, чтобы форматировщик работал на field.blur() (пока вход действителен).

Моя проблема заключается в том, что если я изменю значение модели с 10 на 10, то, понятно, нет изменений, и поэтому поле не будет повторно отображаться.

код:

var CURRENCY_REGEXP = /^\-?\d+(\.?\d?\d?)?$/;
app.directive('currency', function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      ctrl.$parsers.unshift(function(viewValue) {
        if (CURRENCY_REGEXP.test(viewValue)) {
          // it is valid
          ctrl.$setValidity('currency', true);
          console.log("valid");
          return viewValue * 100;
        } else if (viewValue === '') {
          return 0;
        } else {
          // it is invalid, return undefined (no model update)
          ctrl.$setValidity('currency', false);
          console.log("invalid");
          return undefined;
        }
      });
      ctrl.$formatters.push(function(modelValue) {
         if (modelValue === 0) { // we're using integer pence, so this is safe
             return '';
         }
         return (modelValue / 100).toFixed(2); 
      });
    }
  };
});

P.S. Это не имеет ничего общего с Angular встроенной "валютой".


Обновление: я добавил директиву renderOnBlur, как и в ответе Энди. Он вызывается, но вызов метода рендеринга не перерисовывает ввод. то есть "10" остается как "10", а не изменяется на "10.00" по желанию.

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

На странице, которую Энди упоминает http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController, говорится, что вам нужно реализовать $render самостоятельно. Это кажется странным, поскольку входы уже отображаются правильно при изменении значения модели.

app.directive('renderOnBlur', function() {
    return {
        require: 'ngModel',
        restrict: 'A',
        link: function(scope, elm, attrs, ctrl) {
            elm.bind('blur', function() {
                console.log('rendering ctrl', ctrl);
                ctrl.$render();
            });
        }
    };  
});

P.S. Я понятия не имею, что делает restrict: 'A', - это истинное грузо-культовое программирование в худшем случае. Кажется, что require: 'ngModel', заполняет параметр ctrl.


Вдохновленный ответом от @Dan Doyen, я переписал его как:

app.directive('renderOnBlur', function() {
    return {
        require: 'ngModel',
        restrict: 'A',
        link: function(scope, elm, attrs, ctrl) {
            elm.bind('blur', function() {
                var viewValue = ctrl.$modelValue;
                for (var i in ctrl.$formatters) {
                    viewValue = ctrl.$formatters[i](viewValue);
                }
                ctrl.$viewValue = viewValue;
                ctrl.$render();
            });
        }
    };  
});

Это может быть общим для любого $formatter, а не повторять код форматирования, как в ответе Дэна.

4b9b3361

Ответ 1

Однако ваш контроллер $modelValue обновляется должным образом, но поскольку событие размытия происходит за пределами angular, похоже, ваш $viewValue не является. Как насчет этого?

 elm.bind('blur', function() {
       ctrl.$viewValue = (ctrl.$modelValue / 100).toFixed(2);
       ctrl.$render();
 });

Ответ 2

Альтернативная реализация - это запуск angular formatters. angular 1.5 реализует часы $modelValue для изменений, а затем запускает $formatters. Чтобы сделать это вручную, можно сделать это

function triggerFormattersAndRender(ngModel, scope) {
  /* Triggers angulars formatters, which watches for $modelValue changes */
  var originalModelValue = ngModel.$modelValue;
  if (originalModelValue === null) return;

  ngModel.$modelValue = null;
  scope.$digest();
  ngModel.$modelValue = originalModelValue;
  scope.$digest();
}

А затем в директиве

function link(scope, elem, attrs, ngModel) {

    elem.bind('blur', function() {
        triggerFormattersAndRender(ngModel, scope);
    });

    // when we get focus, display full precision
    elem.bind('focus', function() {
      if (ngModel.$modelValue) {
        ngModel.$setViewValue(ngModel.$modelValue.toString());
        ngModel.$render();
      }
    })

}

Ответ 3

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

Кроме того, как сказал Dark Falcon: Formatters следует повторить назад.

Наконец, не перебирайте массивы с помощью in-in, по крайней мере, не без проверки hasOwnProperty() (для меня код разбился, потому что он обрабатывал Array.find() как форматировщик).

// Reformat text on blur
elements.bind('blur', function() {
    if(!ngModel.$valid) {
        return;
    }
    var viewValue = ngModel.$modelValue;
    var formatters = ngModel.$formatters;
    for (var i = formatters.length - 1; i >= 0; --i) {
        viewValue = formatters[i](viewValue);
    }
    ngModel.$viewValue = viewValue;
    ngModel.$render();
});

Ответ 5

10.00 === 10 true

a=10.00

console.log(a) 10

.00 ничего не значит на javascript, из-за этого ваши 10.00 становятся 10

Я предлагаю сделать значение a String, чтобы вы могли создать желаемый формат