Привязка данных для преобразования мм в футы и дюймы или наоборот с использованием Angularjs и ng-модели

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

Я уже создал решение для этого. http://plnkr.co/edit/thgx8vjsjxwfx6cLVVn1?p=preview Но он каждый раз использует ng-change для преобразования значений.

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

var app = angular.module('plunker', []);

    app.controller('MainCtrl', function($scope) {
 // $scope.lengthtodb = 0;

  $scope.mmToFtIn =function(){

    toInch = $scope.lengthmm * 0.0393701;
    toFt = toInch/12;
    OutputFT = Math.floor(toFt);
    OutputInch = (toFt - Math.floor(toFt))*12;
    $scope.lengthft = OutputFT;
    $scope.lengthin = OutputInch;


    $scope.Result1 = OutputFT + "ft" + OutputInch + "Inches";


  };

   $scope.FtInTomm =function(){

    tomm = (($scope.lengthft *12)+  $scope.lengthin)*25.4;
    $scope.lengthmm = tomm;

     $scope.Result2 = tomm;


  };

});

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

3 ответа

Форматирование вывода на представление лучше всего выполнять с помощью фильтров.

JS:

app.filter('inchFilter', function() {
  return function(input) {
    return Math.floor(input * 0.0393701);
  };
});

HTML:

<input name="mm" type="text" value="{{lengthmm | inchFilter}}">

Редактировать:

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

app.directive('enhancedInput', function($parse){
  return {
    restrict: 'A',
    require: 'ngModel',
    link : function(scope, element, attr, ngModelCtrl){
      ngModelCtrl.$parsers.unshift(scope.$eval(attr.fromMm));
      ngModelCtrl.$formatters.unshift(scope.$eval(attr.toMm));
    }
  };
});

Это достигается сначала "требованием" ngModelController, а затем с помощью его $ синтаксических анализаторов и $formatters для перехвата связи между моделью и представлением.

plunker

Вот улучшение, которое я внес в ответ Кристофа Хегемана.

Директива использует только один атрибут, который указывает на объект области inchesShowFeet (вы можете называть это как хотите).

<input type="text" ng-model="data.beamLength" 
       ng-enhanced-input="inchesShowFeet" 
       ng-model-options="{ debounce: 500 }" />

Сама директива, как он упомянул, использует парсеры атрибута ng-model

app.directive('ngEnhancedInput', function($parse){
  return {
    restrict: 'A',
    require: 'ngModel',
    link : function(scope, element, attr, ngModelCtrl){
      ngModelCtrl.$parsers.unshift(scope.$eval(attr.ngEnhancedInput).store);
      ngModelCtrl.$formatters.unshift(scope.$eval(attr.ngEnhancedInput).show);
    }
  };
});

Объект, обычно устанавливаемый в контроллере, имеет функцию show и store. Название смешное, но это лучшее, что я могу придумать. Один возвращает значение для отображения в текстовом поле, другой возвращает значение для сохранения в модели.

$scope.inchesShowFeet = {
    show: function(val){ return val / 12; },
    store: function(val){ return val * 12; }
};

Итак, скажи, что у меня есть 25 дюймы хранятся в моей модели. Функция show get вызывается с val из 25. Он делит его на 12 и возвращает его, который отображается в текстовом поле. Когда вы вводите 4, функция магазина get вызывается с val из 4. Он умножает его на 12 и возвращает 48, что сохраняется в модели.

Одним из вариантов является создание $watch на миллиметровой переменной, а затем обновите модель в дюймах. Это, вероятно, лучший вариант, чем ng-change, потому что $watch будет срабатывать каждый раз, когда переменная изменяется в области видимости, а не только при изменении ввода в текстовое поле.

Я создал пример для вас. Хотя вы заявляете, что хотите использовать только одну переменную, я считаю, что использование второй переменной - действительно лучший подход. В качестве альтернативы вы можете использовать объект или массив для поддержки одной переменной при сохранении двух значений.

Для этого примера я определил переменные следующим образом

    $scope.inches = 0;
    $scope.mm = 0;

Затем мы просто создаем часы для обеих переменных. Повторим, эта функция вызывается каждый раз, когда происходит изменение mm переменная, которая позволяет гарантировать, что отношения между mm а также inches поддерживается

    $scope.$watch('mm', function(newVal, oldVal) {
      $scope.inches = newVal / 25.4;
    });

Вы также можете создать собственный фильтр следующим образом... это может быть даже лучшим решением, если вам нужно только отобразить mm в отформатированном виде (в отличие от использования его для расчетов или что-то еще).

angular.module('myFilters', []).filter('mmToInches', function() {
  return (function (input) {
    return (input / 25.4);
  });
});

Тогда позже сделай...

Inches Filter : {{ mm | mmToInches }}

Вы можете добавить аргументы к фильтрам, чтобы один фильтр мог выполнять оба преобразования.

angular.module('myFilters', []).filter('convert', function() {
  return (function (input, type) {
    if (type == 'mm2inch')
    {
      return (input / 25.4);
    } else if (type == 'inch2mm') {
      return (input * 25.4);
    }
  });
});

Inches Filter : {{ mm | convert:'mm2inch' }}
MM Filter : {{ inches | convert:'inch2mm' }}
Другие вопросы по тегам