如何使用Angular JS将一个模型绑定到多个输入

时间:2015-01-25 03:34:43

标签: javascript angularjs angularjs-directive

我有一个表单输入,用于MySQL日期字段。例如:2015-01-31

我想允许用户使用3种不同的表单输入来输入它。一年一个,一个月一个,一天一个。

显然,ng-model不能立即开始工作,因为我试图将日期字符串的一个部分绑定到每个输入。我很确定如何做到这一点就是创造三个"临时"范围变量/模型

$scope.year;
$scope.month;
$scope.day;

...然后以某种方式将它们组合/绑定到实际值。

//If only it were this easy!
$scope.date = $scope.year + "-" + $scope.month + "-" + $scope.day;

上面的行当然不会起作用,因为这些值不是双向约束的。如果表单仅用于保存新数据,我可以通过组合提交的输入来逃避。但我需要它来处理/显示现有数据。如果我能找到一种方法来纠结Angular的绑定魔法来做我想做的事情,那么它会变得非常丑陋。

我发现this question我认为它试图做同样的事情,但他们用自定义指令解决它,这是我希望避免的。我知道这可能是一种更易于维护/可移植/模块化的方式,但我是Angular的新手并且有点害怕。此外,输入使用了可爱的angular-selectize指令,这为该方法增加了额外的复杂性。

3 个答案:

答案 0 :(得分:11)

指令可能是最好的,但这些示例看起来过于复杂。无论如何,如果您希望避免使用指令,只需使用$scope.$watch并在每次更新其中一个重要变量时重新构建日期字符串。

这样的事情可能在你的控制器中:

$scope.year = '';
$scope.month = '';
$scope.day = '';

// this might be able to be refactored
$scope.$watch('year', buildDate);
$scope.$watch('month', buildDate);
$scope.$watch('day', buildDate);

function buildDate() {
  $scope.date = $scope.year + "-" + $scope.month + "-" + $scope.day;
}

作为旁注,这可能是我的指令逻辑也是如此。

编辑:代码清理和小提琴

更清洁的例子 - 我更喜欢这个,因为它将所有与日期相关的项目与一个对象组合在一起,这也使得更容易观察变化。

$scope.date = {
    year: '',
    month: '',
    day: ''
};

// use watch collection to watch properties on the main 'date' object
$scope.$watchCollection('date', buildDate);

function buildDate(date) {
  $scope.dateString = date.year + "-" + date.month + "-" + date.day;
}

Fiddle

答案 1 :(得分:5)

这是一个有趣的演示,它使用的自定义指令比你链接的指令要吓人得多。您应该能够将它们应用到您的输入中,而不会与其他内容发生太多冲突:

http://plnkr.co/edit/3a8xnCfJFJrBlDRTUBG7?p=preview

诀窍是使用该指令为模型设置解析器和格式化程序。这使您可以拦截对模型的更改并与范围的其余部分进行交互:

app.controller('MainCtrl', function($scope) {
  $scope.name = 'World';
  $scope.date = new Date();
});

app.directive('datePartInput', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, elem, attrs, ngModel) {
      var part = attrs.part;
      var modelToUser, userToModel
      console.log('part:', part);
      if (part == 'year') {
        modelToUser = function(date) {
          return date.getFullYear();
        }
        userToModel = function(year) {
          ngModel.$modelValue.setYear(year);
          return ngModel.$modelValue
        }
      }
      else if (part == 'month') {
        modelToUser = function(date) {
          return date.getMonth();
        }
        userToModel = function(month) {
          ngModel.$modelValue.setMonth(month);
          return ngModel.$modelValue;
        }
      }
      else if (part == 'day') {
        modelToUser = function(date) {
          return date.getUTCDate();
        };
        userToModel = function(day) {
          ngModel.$modelValue.setUTCDate(day);
          return ngModel.$modelValue;
        };
      }
      ngModel.$formatters.push(modelToUser);
      ngModel.$parsers.push(userToModel);
    }
  }
})

模板:

<body ng-controller="MainCtrl">
  <p>Hello {{name}}!</p>
  {{date |  date}}
  <input date-part-input part="year" ng-model="date">
  <input date-part-input part="month" ng-model="date">
  <input date-part-input part="day" ng-model="date">
</body>

答案 2 :(得分:2)

您可以创建一个可重复使用的指令,其中包含三个字段,因此可以用于所有日期字段。该指令的模型在隔离范围上别名为date。为了获取每个日期部分,然后拆分日期,然后将yearmonthday分配给范围属性。然后,当其中一个字段更改时,通过将它们与-分隔符一起附加来更新日期属性。

对于这个指令,我只是硬编码,几个月和几天。我建议使用一些javascript日期函数来填充它们,这样它们就不会被硬编码。

angular
.module('app')
.directive('dateSelect', function (){
    return {
        restrict: 'E',
        replace: true,
        scope: {
          date:'=ngModel'
        },
        template: '<div class="dateSelect"><div class="dateField"><selectize placeholder="Select a year..." config="yearConfig" ng-model="year" ng-change="dateChanged()"></selectize></div>' +
        '<div class="dateField"><selectize placeholder="Select a month..." config="monthConfig" ng-model="month" ng-change="dateChanged()"></selectize></div>' + 
        '<div class="dateField"><selectize placeholder="Select a day..." config="dayConfig" ng-model="day" ng-change="dateChanged()"></selectize></div></div>',
        controller: function ($scope) {
          $scope.yearConfig = {
          options: [{value: 2013, text: '2013'}, {value: 2014, text:'2014'}, {value: 2015, text:'2015'}],
          create: true,
          sortField: 'value',
          maxItems: 1,
        };
        $scope.monthConfig = {
          options: [{value: '01', text: '1'}, {value: '02', text: '2'}, {value: '03', text:'3'}, 
          {value: '04', text: '4'}, {value: '05', text:'5'}, {value: '06', text:'6'}, {value: '07', text: '7'}, {value: '08', text:'8'}, {value: '09', text:'9'},
          {value: '10', text: '10'}, {value: '11', text:'11'}, {value: '12', text:'12'}],
          create: true,
          sortField: 'value',
          maxItems: 1,
        };

        $scope.dayConfig = {
          options: [{value: '01', text: '1'}, {value: '02', text: '2'}, {value: '03', text:'3'}, 
          {value: '04', text: '4'}, {value: '05', text:'5'}, {value: '06', text:'6'}, {value: '07', text: '7'}, {value: '08', text:'8'}, {value: '09', text:'9'},
          {value: '10', text: '10'}, {value: '11', text:'11'}, {value: '12', text:'12'}],
          create: true,
          sortField: 'value',
          maxItems: 1,
        };

        $scope.dateChanged = function () {
          if (!angular.isUndefined($scope.year) && !angular.isUndefined($scope.month) && !angular.isUndefined($scope.day)) {
            $scope.date = $scope.year + "-" + $scope.month + "-" + $scope.day;
          }
        }

        if (!angular.isUndefined($scope.date)) {
          var dateParts = $scope.date.split("-");

          if (dateParts.length === 3) {
            $scope.year = dateParts[0];
            $scope.month = dateParts[1];
            $scope.day = dateParts[2];
          }
        }
      }
    };
});

Plunkr