【问题标题】:How to bind one model to multiple inputs with Angular JS如何使用 Angular JS 将一个模型绑定到多个输入
【发布时间】:2015-01-25 03:34:43
【问题描述】:

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

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

显然 ng-model 不能直接使用,因为我试图将日期字符串的一个 part 绑定到每个输入。我很确定这样做的方法是再见创建三个“临时”范围变量/模型

$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 个作用域属性,然后将它们组合起来。当您从 api 获取日期时,您需要拆分字段并设置每个范围属性。您可以创建一个可重用的指令来执行此操作,以便它可以在多个地方使用
  • 我认为包含三个字段并从/转换为单个日期字段的可重用指令应该是要走的路。但是为什么不使用 ui.bootstrap.datepicker 之类的日期选择器呢?
  • @WayneEllery 创建指令并仍然使用 angular-selectize 会很麻烦吗?
  • @ps0604 正如我在问题中所说,我同意指令可能“更好”,但我只是不知道该怎么做。而且我怀疑与角度选择集成会很棘手。如果它不像我想的那么难做,我愿意接受。我将再次讨论日期选择器。
  • 这并不棘手,您只需在指令中使用 angular-selectize 而不是使用普通选择。如果没有指令,它会简单得多,但建议使用指令,以便可以重复使用。

标签: javascript angularjs angularjs-directive


【解决方案1】:

指令可能是最好的,但这些示例看起来过于复杂。无论如何,如果您希望避免使用指令,只需使用 $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

【讨论】:

  • 要处理预先存在的值,您是否会添加另一个函数,如parseDate() 来提取日期字符串的部分并预设值?
  • 当然,这听起来是个不错的选择。那时你甚至可以考虑创建一个日期服务来容纳所有类似的逻辑。
【解决方案2】:

这里有一个有趣的演示,它使用了比您链接的那些更不吓人的自定义指令。您应该能够将它们应用于您的输入,而不会与其他东西有太多冲突:

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>

【讨论】:

    【解决方案3】:

    您可以创建一个包含三个字段的可重用指令,以便它可用于所有日期字段。该指令的模型在隔离范围上别名为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

    【讨论】:

    • 这看起来很可行!我需要注入 selectize 依赖项才能工作吗?还是保留在父模块上?
    • 没有。您只需要将 selectize 添加到模块中。如果指令在您的应用程序的同一模块中,那么您就可以开始了。
    猜你喜欢
    • 2012-12-03
    • 1970-01-01
    • 2019-06-10
    • 1970-01-01
    • 2021-09-20
    • 2017-03-21
    • 2018-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多