Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I've created a directive to wrap a jQuery plugin, and I pass a config object for the plugin from the controller to the directive. (works)

In the config object is a callback that I want to call on an event. (works)

In the callback, I want to modify a property on the controller's $scope, which does not work. Angular does not recognize that the property has changed for some reason, which leads me to believe that the $scope in the callback is different than the controller's $scope. My problem is I just don't why.

Can anybody point me in the right direction?


Click here for Fiddle


app.js

var app = angular.module('app', [])
    .directive('datepicker', function () {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                // Uncommenting the line below causes
                // the "date changed!" text to appear,
                // as I expect it would.
                // scope.dateChanged = true;

                var dateInput = angular.element('.datepicker')

                dateInput.datepicker(scope.datepickerOpts);

                // The datepicker fires a changeDate event
                // when a date is chosen. I want to execute the
                // callback defined in a controller.
                // ---
                // PROBLEM: 
                // Angular does not recognize that $scope.dateChanged
                // is changed in the callback. The view does not update.
                dateInput.bind('changeDate', scope.onDateChange);
            }
        };
    });

var myModule = angular.module('myModule', ['app'])
    .controller('MyCtrl', ['$scope', function ($scope) {
        $scope.dateChanged = false;

        $scope.datepickerOpts = {
            autoclose: true,
            format: 'mm-dd-yyyy'
        };

        $scope.onDateChange = function () {
            alert('onDateChange called!');

            // ------------------
            // PROBLEM AREA:
            // This doesnt cause the "date changed!" text to show.
            // ------------------
            $scope.dateChanged = true;

            setTimeout(function () {
                $scope.dateChanged = false;
            }, 5000);
        };
    }]);

html

<div ng-controller="MyCtrl">
    <p ng-show="dateChanged">date changed!</p>
    <input type="text" value="02-16-2012" class="datepicker" datepicker="">
</div>
share|improve this question

2 Answers 2

up vote 8 down vote accepted

There are a number of scope issues at work in your demo. First , within the dateChange callback, even though the function itself is declared inside the controller, the context of this within the callback is the bootstrap element since it is within a bootstrap handler.

Whenever you change angular scope values from within third party code , angular needs to know about it by using $apply. Generally best to keep all third party scopes inside the directive.

A more angular apprroach is to use ng-model on the input. Then use $.watch for changes to the model. This helps keep all the code inside the controller within angular context. Is rare in any angular application not to use ng-model on any form controls

 <input type="text"  class="datepicker" datepicker="" ng-model="myDate">

Within directive:

dateInput.bind('changeDate',function(){
      scope.$apply(function(){
         scope[attrs.ngModel] = element.val()
      });
});

Then in Controller:

$scope.$watch('myDate',function(oldVal,newVal){ 
       if(oldVal !=newVal){
           /* since this code is in angular context will work for the hide/show now*/
            $scope.dateChanged=true;
             $timeout(function(){
                   $scope.dateChanged=false;
              },5000);
        }        
});

Demo: http://jsfiddle.net/qxjck/10/

EDIT One more item that should change is remove var dateInput = angular.element('.datepicker') if you want to use this directive on more than one element in page. It is redundant being used in directive where element is one of the arguments in the link callback already, and is instance specific. Replace dateInput with element

share|improve this answer
    
one more thought...there are several angular/bootstrap ui directive projects that are super easy to use on github –  charlietfl Mar 25 '13 at 2:23
    
Thanks for setting me straight. I got it working immediately with $apply (I could hug you for that), but I want to do it right so I'm trying to implement the approach you showed. Something I didn't show but should have in my demo is I'm actually using an <a> tag to trigger the date picker. The chosen date is held in element.data('datepicker').getDate(). I'm new to AngularJS, so please excuse me if I'm wrong, but ng-model is for form elements. What would be a suitable replacement? –  simshaun Mar 25 '13 at 3:32
    
not as familiar with bootstrap datepicker as i am with jQuery Ui one. However instead of using ng-model on an input, can hard code the variable to update in controller scope, or use any custom attribute you want to store the variable name( if using this in multiple places in page for example). Have more problems, create updated version with <a> tag implementation. Source of value as data on elemnt easy to modify in the scope.$apply call in directive. Keep same $watch methodlogy. –  charlietfl Mar 25 '13 at 3:38
    
Finally got it to a point I'm happy with. Thanks! –  simshaun Mar 25 '13 at 5:16
    
cool... is a bit frustrating at first figuring out nuances of angular, but well worth it in long run –  charlietfl Mar 25 '13 at 5:32

The changeDate event bound to the input seems to be set up to fire outside of the Angular framework. To show the paragraph, call $scope.$apply() after setting dateChanged to true. To hide the paragraph after the delay, you can use $apply() again inside the function passed to setTimeout, but you're likely to keep out of further trouble using Angular's $timeout() instead.

Fiddle

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.