0

I've defined a directive like so:

angular.module('MyModule', [])
    .directive('datePicker', function($filter) {
        return {
            require: 'ngModel',
            link: function(scope, elem, attrs, ctrl) {
                ctrl.$formatters.unshift(function(modelValue) {
                    console.log('formatting',modelValue,scope,elem,attrs,ctrl);
                    return $filter('date')(modelValue, 'MM/dd/yyyy');
                });
                ctrl.$parsers.unshift(function(viewValue) {
                    console.log('parsing',viewValue);
                    var date = new Date(viewValue);
                    return isNaN(date) ? '' : date;
                });
            }
        }
    });

The parser seems to fire every time I type a key in my textbox though -- what exactly is the default event, is it keyup, or input? And how do I change it to only fire onchange? It really isn't necessary to fire anymore often than that.

Furthermore, I'm actually manipulating the content of this input using jQuery UI's datepicker. When clicking on the calendar it doesn't seem to trigger the appropriate event that causes the model to be updated/parser to be triggered. I think I can force an event to be fired but I need to know which one.


Trying to use scope.$apply() but that doesn't seem to help any:

.directive('datepicker', function($filter) {
    return {
        require: 'ngModel',
        link: function(scope, elem, attrs, ctrl) {
            $(elem).datepicker({
                onSelect: function(dateText, inst) {
                    console.log(dateText, inst);
                    scope.$apply();
                }
            });
            ctrl.$formatters.unshift(function(modelValue) {
                console.log('formatting',modelValue);
                return $filter('date')(modelValue, attrs.datePicker || 'MM/dd/yyyy');
            });
            ctrl.$parsers.unshift(function(viewValue) {
                console.log('parsing',viewValue);
                return new Date(viewValue);
            });
        }
    }
})

I don't think the solution given here works for me because (a) I want to use the datepicker attribute value for choosing a date format or other options, but more importantly, (b) it seems to be passing back a string to the model when I want an actual date object... so some form of parsing has to be done and applied to the ng-model.

Community
  • 1
  • 1
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • 1
    See http://stackoverflow.com/questions/11873627/angularjs-ng-model-binding-not-updating-with-dynamic-values for some ideas about jQuery UI's datepicker. You probably need to call $apply() for Angular to update the model. – Mark Rajcok Jan 24 '13 at 04:59
  • 1
    Have you checked the source code of datePicker of http://angular-ui.github.com/ ? – SunnyShah Jan 24 '13 at 05:52
  • @MarkRajcok: I'm calling `scope.$apply()` inside datepicker's `onSelect` but it doesn't seem to have any effect. – mpen Jan 24 '13 at 07:38
  • @SunnyShah: Nope! Didn't know about that -- I'm already using twitter bootstrap and jQuery, so this looks exactly like what I want! Very slick. I still want to learn what I'm doing wrong though :-) – mpen Jan 24 '13 at 07:39
  • @Mark, I can make a complete directive for you from scratch. But then it will be duplicate effort. – SunnyShah Jan 24 '13 at 07:41
  • @SunnyShah: I guess I can check the source for myself then! https://github.com/angular-ui/angular-ui/blob/v0.3.2/modules/directives/date/date.js Looks a lot more complex than what I had going on. – mpen Jan 24 '13 at 07:48
  • @Mark, You need to first go through the steep curve of learning AngularJS. After that everything will start making sense. :) – SunnyShah Jan 24 '13 at 07:58
  • I was hoping angular was going to be a simple framework :-P I'm waiting for Meteor to hit 1.0... it seems to do all this plus live updating across clients. – mpen Jan 25 '13 at 02:42
  • @Mark, I think, I have created production quality code for you. Check it. Let me know If you find issues. – SunnyShah Jan 25 '13 at 10:18

1 Answers1

2

Here I created a mo-change-proxy directive, It works with ng-model and it updates proxy variable only on change.

In this demo I have even included improved directive for date-input. Have a look.
Demo: http://plnkr.co/edit/DBs4jX9alyCZXt3LaLnF?p=preview

angModule.directive('moChangeProxy', function ($parse) {
    return {
        require:'^ngModel',
        restrict:'A',
        link:function (scope, elm, attrs, ctrl) {
            var proxyExp = attrs.moChangeProxy;
            var modelExp = attrs.ngModel;
            scope.$watch(proxyExp, function (nVal) {
                if (nVal != ctrl.$modelValue)
                    $parse(modelExp).assign(scope, nVal);
            });
            elm.bind('blur', function () {
                var proxyVal = scope.$eval(proxyExp);
                if(ctrl.$modelValue != proxyVal) {
                    scope.$apply(function(){
                        $parse(proxyExp).assign(scope, ctrl.$modelValue);
                    });
                }
            });
        }
    };
});
SunnyShah
  • 28,934
  • 30
  • 90
  • 137
  • 1
    Is there a reason you used bind('blur',... instead of bind('change', ... ? – Mark Rajcok Jan 25 '13 at 19:57
  • What's the `mo` stand for? Is that a convention? Anyway, this is pretty cool..just need to figure out how to integrate jQuery datepicker with it. Not quite trivial :-) http://plnkr.co/edit/4okndhEgoLSfROqqHgRP?p=preview I think I have to do some more reading to understand how this all works. – mpen Jan 26 '13 at 02:05
  • @Mark, mo is my namespace. My company name. ;) – SunnyShah Jan 26 '13 at 05:28
  • @Mark, It is good to see that date-Picker is now poping out. Let me know once you finish it. – SunnyShah Jan 26 '13 at 05:31
  • @MarkRajcok, I wanted to make a generic onChange proxy. Which work with any element. – SunnyShah Jan 26 '13 at 05:32