Reputation: 1297
I am building some kind of date-picker, which is actually 2 date pickers. one for start date and the other for end date.Every datepicker element generate a template of 2 input tags(). I want to pass data from input's value attribute to the controller. I have tried to define fields in the inner scope which are 2-way data binding(dateOne and dateTwo) but apparently no effect and no real data is pass between the 2 fields. My other approach is using ng-model, but I have little exprience with this feature and I don't know the rules for that. Here is my code
angular.module('directives', [])
.directive('datepicker', ['$timeout',function ($timeout) {
// Runs during compile
return {
scope: {
id: '@',
"class": '@',
dateOne: '=',
dateTwo: '='
},
restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
template: '<div id="{{id}}" class="{{class}}">'+
'<div class="date-wrapper">'+
'<label for="datepicker-start">From:</label>'+
'<div class="fieldWrapper">'+
'<input id="datepicker-start" type="date" placeholder="Select date" value={{dateOne}} />'+
'<a class="calendar"></a>'+
'</div>'+
'</div>'+
'<div class="date-wrapper">' +
'<label for="datepicker-end">To:</label>' +
'<div class="fieldWrapper">' +
'<input id="datepicker-end" type="date" placeholder="Select date" value={{dateTwo}}/>' +
'<a class="calendar"></a>' +
'</div>' +
'</div>'+
'</div>'
,
replace: true,
link: function($scope, iElm, iAttrs, controller) {
console.log('directive link function');
console.log('directive iAttrs', iAttrs);
$(".date-wrapper").each(function (index) {
console.log('directive index', index);
$input = $(this).find('input');
$btn = $(this).find('.calendar');
console.log('input', $input[0]);
console.log('btn', $btn[0]);
$input.attr('type', 'text');
var pickerStart = new Pikaday({
field: $input[0],
trigger: $btn[0],
container: $(this)[0],
format: 'DD/MM/YYYY',
firstDay: 1
});
$btn.show();
});
}
};
}]);
------------------------Updated Code -----------------------------------
angular.module('directives', [])
.directive('datepicker', ['$timeout',function ($timeout) {
// Runs during compile
return {
scope: {
id: '@',
"class": '@',
dateOne: '=',
dateTwo: '='
},
restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
template: '<div id="{{id}}" class="{{class}}">'+
'<div class="date-wrapper">'+
'<label for="datepicker-start">From:</label>'+
'<div class="fieldWrapper">'+
'<input id="datepicker-start" type="date" placeholder="Select date" ng-model=dateOne />' +
'<a class="calendar"></a>'+
'</div>'+
'</div>'+
'<div class="date-wrapper">' +
'<label for="datepicker-end">To:</label>' +
'<div class="fieldWrapper">' +
'<input id="datepicker-end" type="date" placeholder="Select date" ng-model=dateTwo />' +
'<a class="calendar"></a>' +
'</div>' +
'</div>'+
'</div>'
,
replace: true,
link: function($scope, iElm, iAttrs, controller) {
console.log('directive iAttrs', iAttrs);
$(".date-wrapper").each(function (index) {
console.log('directive index', index);
$input = $(this).find('input');
$btn = $(this).find('.calendar');
console.log('input', $input[0]);
console.log('btn', $btn[0]);
$input.attr('type', 'text');
var pickerStart = new Pikaday({
field: $input[0],
trigger: $btn[0],
container: $(this)[0],
format: 'DD/MM/YYYY',
firstDay: 1
});
$btn.show();
});
$scope.$watch(iAttrs.dateOne, function (newValue, oldValue) {
console.log('newValue', newValue);
console.log('oldValue', oldValue);
}, true);
}
};
Upvotes: 3
Views: 1241
Reputation: 9839
Actually you are almost there, I've done something very similar to what you have described and here was my approach to solve it (I used the UI-Bootstrap Date picker in my case).
The way you would send data from your directive to your controller is by using callbacks, rather than simple watches. If you would have used =
you would have to set watches in your controller (and directive) to watch for value changes, it's bad practice overall and extra code.
So basically what you need to do is
In you directive definition object bind a callback method/function using the &
sign like so
scope: {
onSelect: "&" // onSelect is our callback function in the ctrl
}
You then supply the callback attribute a function bound to the controller's $scope, but you pass it a function reference (not a function call as you would in something like ng-changed). like so
<my-directive on-selected="onSelected"></my-directive>
Then you define what onSelected
should do, lets say I want to print the selected date
// inside controller
$scope.onSelected = function(time) {
console.log("Time selected: ", time);
}
Note that we pass the time argument from the directive to the controller like so, scope.onSelect()
is actually a curried function, meaning it will return a function once called (that is, if you provided it with a function, you could test it using angular.isFunction
), so you should call the curried function and provide it your argument, scope.onSelect()(time)
.
scope.selectDate = function(time) {
if (angular.isFunction(scope.onSelect())) {
// we use isFunction to test if the callback function actually
// points to a valid function object
scope.onSelect()(time); // we pass out new selected date time
}
}
Here is a plunk that shows what I mean.
Upvotes: 2
Reputation: 5663
Replace value in the template with ng-model=dateOne and the same with dateTwo.
I suggest to use a dedicated controller for this directive and not doing the logic inside the link function.
app.directive('someDirective', function () { return { restrict: 'A', controller: 'SomeController', controllerAs: 'ctrl', template: '{{ctrl.foo}}' }; });
Read more here http://blog.thoughtram.io/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html
Upvotes: 0