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>
Approach: To share data between the controllers in AngularJS we have two main cases: Share data between parent and child: Here, the sharing of data can be done simply by using controller inheritance as the scope of a child controller inherits from the scope of the parent controller.
The scope is the binding part between the HTML (view) and the JavaScript (controller). The scope is an object with the available properties and methods. The scope is available for both the view and the controller.
Shared scope: directive and controllers share the scope and the data. We cannot pass data explicitly to the directive. 2. Inherited scope: directive inherits the scope of the controller.
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With