Rectangle 27 0

javascript How to bind one model to multiple inputs with Angular JS?


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];
          }
        }
      }
    };
});

For this directive I've just hard coded, years months and days. I'd recommend to use some javascript date functions to populate them so they aren't hard coded.

No. You only need to add selectize to the module. If the directive is in the same module of your app then you are good to go.

That looks pretty doable! Would I need to inject the selectize dependency on for that to work? Or does that stay on the parent module?

You can create a re-usable directive that has the three fields in it so it can be used for all date fields. The model for the directive is aliased to date on the isolated scope. To get each of the date parts the date is then split and then year, month and day are assigned to scope properties. Then when one of the fields is changed the date property is updated by appending them together with the - separator.

Note
Rectangle 27 0

javascript How to bind one model to multiple inputs with Angular JS?


$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;
}
$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;
}

A directive is probably best, but these examples look overly complex. Anyway if you are hoping to avoid a directive, just use $scope.$watch and re-build your date string each time one of the important variables are updated.

As a side note, this is probably what my directive logic would look like too.

Cleaner example - I prefer this because it groups all the date-related items with an object, which also makes watching for changes easier.

Something like this might be in your controller:

Sure, that sounds like a great option. At that point you might even consider creating a Date service to house all that similar logic.

To handle pre-existing values would you add another function like parseDate() to extract the parts of the date string and preset the values?

Note
Rectangle 27 0

javascript How to bind one model to multiple inputs with Angular JS?


<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>
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);
    }
  }
})

The trick is setting the parser and formatter for a model using the directive. This lets you intercept changes to the model and interact with the rest of your scope:

here's an interesting demo that uses custom directives that are a lot less intimidating than the ones you linked to. You should be able to apply them to your inputs without too much conflict with other stuff:

Note