angular / material

Material design for AngularJS
https://material.angularjs.org/
MIT License
16.55k stars 3.39k forks source link

feat(datepicker): add support for applying an input mask #9976

Open thallesnoce opened 7 years ago

thallesnoce commented 7 years ago

Actual Behavior:

CodePen (or steps to reproduce the issue): *

Angular Versions: *

Additional Information:

scriptsure commented 7 years ago

+1

jalchr commented 7 years ago

Please add native support for masking input dates.

wilker7ribeiro commented 7 years ago

Here the hack I did with a decorator to make this work Working plnkr: https://plnkr.co/edit/Kbw4WaH1AVBTrlIjFvmJ in my app.config phase

$provide.decorator('mdDatepickerDirective', function ($delegate) {
      var directive = $delegate[0];

      var template = directive.template;
      var link = directive.link;

      // extending the link function
      directive.compile = function () {
        return function (scope, element, attrs, ctrl) {

          link.apply(this, arguments);
          if (attrs.ngMask) {
            var inputNgModelControl = scope.ctrl.ngInputElement.controller('ngModel')

            // for the first initialization
            inputNgModelControl.$formatters.push(function (value) {
              var viewValue = scope.ctrl.ngInputElement.controller('mdDatepicker').ngModelCtrl.$viewValue;
              if (!viewValue) {
                return undefined;
              }
              return scope.ctrl.$mdDateLocale.formatDate(viewValue);
            })

            // for transfer validation
            scope.$watch(function () {
              return scope.ctrl.ngInputElement.controller('ngModel').$error.mask;
            }, function (newValue, oldValue) {
              ctrl[0].$setValidity('mask', !newValue)
            })
          }
        };
      };
      // replacing template when needed
      directive.template = function (tElement, tAttrs) {
        var originalTemplate = template.apply(this, arguments);
        // adding mask and ng-model to mdDatepicker's input
        if (tAttrs.ngMask) {
          var element = angular.element(originalTemplate);
          element.find('input').attr('mask', tAttrs.ngMask);
          element.find('input').attr('ng-model', "ctrl.dateInput");

          // new tempalte with mask and ng-model attributes
          var newTemplate =  ''
          for (var i = 0; i < element.length; i++) {
            newTemplate += element[i].outerHTML
          }
          return newTemplate ;
        }
        return originalTemplate;
      };
      return $delegate;
    });

html:

<form name='form'>
      <md-input-container>
        <label>Data</label>
        <md-datepicker name="date" ng-mask="1?9/3?9/9999" ng-model="date"></md-datepicker>
        <div ng-messages="form.date.$error">
          <div ng-message="required"> Required Field </div>
          <div ng-message="mask"> Invalid Date </div>
        </div>
      </md-input-container>
    </form>

Things I notice:

Sorry for bad English

jalchr commented 7 years ago

@wilker7ribeiro I hope this becomes part of the datepicker. Did you send a pull request ?

codymikol commented 6 years ago

+1 this would be very useful and is something we are currently working around.

Splaktar commented 6 years ago

To clarify, this issue is asking for https://github.com/candreoliveira/ngMask support in md-datepicker?

Have you tried https://github.com/angular-ui/ui-mask? I notice that there is an AngularJS Material related issue there that hasn't been triaged in 2 years, but it just seems to be related to placeholders, so perhaps some of the other features work? I haven't tested it.

codymikol commented 6 years ago

I guess it's not necessarily asking for angular material to support ngMask, a better explanation would be that in order for either ngMask or ui-mask to function, you need to be able to add those directives to an input tag. md-datepicker doesn't expose an input tag so you can't really apply any sort of mask directives.

If it were optional to expose the input portion of the directive in between the tag, it would be possible to add these masks fairly easily.

Something like this would work for ng-mask.

<md-datepicker>
  <input  mask='39/19/9999' >
</md-datepicker>  

And this would work for ui-mask.

<md-datepicker>
  <input ui-mask="39/19/9999">
</md-datepicker>

Theoretically, by making the input portion of the datepicker optional, you could still be backwards compatible with the current datepicker implementations.

Currently my team is running a custom version of md-datepicker with hardcoded masks to work around this.

Splaktar commented 6 years ago

Exposing the input is supported in Angular Material, but that seems like quite a risky change to bring into AngularJS Material at this point. I do agree that the approach is better, but there is quite a risk of breaking existing apps.

If someone from the community was willing to work on this and investigate/propose/develop/verify a solution that did not cause breaking changes, then we would be willing to review it and try to get it merged.

The datepicker is a complex component, so this is no easy task. There are also quite a number of higher priority items that the team needs to focus on at this time.