2

What I want to achieve here is I would like to mark input field (child node of md-datepicker) should be marked readonly so that user can't enter date value manually (typing) and forced to select date from md-datepicker.

I tried implementing this by decorating md-datepicker directive, but no luck.

is there any other easy and correct way to mark input field as readonly and force user to select date only from calender ?

I'm using Angularjs.

===========================================================================

What I tried is decorating md-datepicker directive and achieve behavior that I want like this

(function () {
'use strict';

angular.module('APPLICATION_NAME').config(['$provide', function($provide) {

    $provide.decorator('mdDatepickerDirective', [
        '$delegate',

        /**
         * @function mdDatepickerDirective
         * @description decorates mdDatepickerDirective to extend functionality:
         *              - Mark input field as read-only so which will prevent user from typing date manually
         *                and should select from date-picker calender only.
         * @param {angular.Directive} $delegate
         * @returns {angular.Directive} $delegate
         */
            function mdDatePickerDecorator($delegate) {
            var directive = $delegate[0];
            var compile = directive.compile;

            directive.compile = function (tElement) {
                var link = compile.apply(this, arguments);
                tElement.find("input").prop("readOnly", "true");
            };

            return $delegate;

        }
    ]);
}])})();

But I'm getting some errors like :

  • TypeError: Cannot read property '$setTouched' of null
  • TypeError: Cannot read property '$setViewValue' of null

What's wrong with element after directive decorator runs ? Pls help.

Enigma
  • 749
  • 1
  • 13
  • 35

3 Answers3

3

DO scss file

md-datepicker{ input{ pointer-events:none; } }

user1786647
  • 594
  • 4
  • 6
1

On focus of input box explicitly calling the datepicker which will open calendar so that user cannot edit the date.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- Angular Material Dependencies -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/0.11.2/angular-material.min.js"></script>

<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/0.11.2/angular-material.min.css">

<div ng-app="StarterApp" ng-controller="AppController" ng-init="initDatepicker();">
    <md-content flex class="padding-top-0 padding-bottom-0" layout="row">
        <md-datepicker id="datePicker" ng-model="user.submissionDate1" md-placeholder="Start date" flex ng-click="ctrl.openCalendarPane($event)"></md-datepicker>
    </md-content>
</div>
<script>
  var app = angular.module('StarterApp', ['ngMaterial']);

app.controller('AppController', function($scope) {


 document.querySelectorAll("#datePicker input")[0].setAttribute("readonly","readonly");
    $scope.initDatepicker = function(){
        angular.element(".md-datepicker-button").each(function(){
            var el = this;
            var ip = angular.element(el).parent().find("input").bind('click', function(e){
                angular.element(el).click();
            });
            angular.element(this).css('visibility', 'hidden');
        });
    };
});
</script>

Demo link Example

ankesh jain
  • 171
  • 4
  • Good solution but not complete - You handled click event but if you come to this input by using keyboard like tab from previous element - then it allows manual date entry. Pls have a look once. – Enigma Sep 11 '17 at 11:16
  • Please find updated plunker link: handled tab using keyboard https://plnkr.co/edit/6u7DQjiWz409OYbcHfHN?p=preview – ankesh jain Sep 11 '17 at 12:27
  • Problem with this is I need to call 'initDatepicker' in every controller or place where datepicker is used. so that's lot of duplication of code. Why can't we fix it at one place and get desired output for all date-picker in application. That's why I written directive decorator but getting some errors. Can you have a look at it ? – Enigma Sep 11 '17 at 12:38
  • I have created directive removing the init function,add directive attribute and id to the element wherever u need. Please find the link : https://plnkr.co/edit/6u7DQjiWz409OYbcHfHN?p=preview – ankesh jain Sep 11 '17 at 13:08
  • Looking up elements via selectors is not supported by jqLite! Error observed because angular.element is used in your answer. Can you please update with proper jqLite selectors. – Enigma Sep 11 '17 at 13:39
1

I've created Directive rather than applying patches or instead of directive decoration

Hope this will save somebody time.

here it is :

(function () {
'use strict';

angular.module('myApplication').directive('mcReadOnly', mcReadOnly);

/**
 * @function mcReadOnly
 * @description creates a directive which will override the behavior of md-datepicker and
 *              will not allow user to enter date manually.
 * @param
 * @returns {Directive} directive - for DOM manipulation of md-datepicker directive.
 */
function mcReadOnly() {
    var directive = {
        restrict: 'A',
        link: link
    };

    return directive;

    /**
     * @function link
     * @description link function of this directive will do following things :
     *              1. Will mark 'input' field as readonly so it will not edited/changed by user manually - can only
     *              be changed/edited by changing value from calender pane.
     *              2. When clicked upon 'input' text field of md-datepicker, It will open calender pane (like user clicked on
     *              Date picker calender icon)
     * @param {scope} - scope of directive
     * @param {element} - DOM element where directive is applied
     * @returns
     */
    function link(scope, element) {
        element.find("input")[0].setAttribute("readonly","true");
        element.find("input").bind('click', function(){
            element.find("button")[0].click();
        });
    }
}
})();
Enigma
  • 749
  • 1
  • 13
  • 35