Reputation: 147
I am a complete beginner in js and Angular.js. I need some help binding returned result from a date picker in an Angular custom directive to a ng-model in an HTML input field. Here goes -
My HTML form uses a ng-controller to retrieve data from the backend DB to populate the elements, among them a date field. This date field can be updated using an attached date picker. Given Jquery date picker won't work with angular.js in FireFox and Safari browsers, I wrote a custom ng directive implementing a date picker. However, I have a problem getting the result returned from the custom directive to replace the original date field in the HTML.
In my HTML, the date input field has ng-model="myCtrl.deadline"
, whereas my custom directive has "scope.deadline = date;"
- without the "myCtrl"
prefix. If I set ng-model="deadline"
then the date field won't be populated by initial data pulled from the backend. If I set ng-model={{deadline}}
I get the angular.min.js error in the browser when I select a new date from the date picker.
The date picker in the custom directive works fine, I can pick a new date into the HTML date field, but the new date is not bound to the HTML field. As such, there is no way to diff. the new value with previous value to detect a change and trigger an update of the new date value to the backend.
Here is how my code looks like:
Code
<html ng-app="myApp" ng-controller="MyCtrl as myCtrl">
<head>
<script src="js/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-messages.js"></script>
<title>Edit your data</title>
</head>
<body>
<div class="container">
...
<div class="row">
<div class="col-sm-4 col-md-4"><strong>Deadline:</strong></div>
<div class="col-sm-8 col-md-8">
<input type="text"
class="form-control"
id="theDate"
ng-model="myCtrl.deadline"
ng-blur="myCtrl.update('deadline')"
ng-change="myCtrl.update('deadline')"
required jq-date-picker><span class="glyphicon glyphicon-calendar" aria-hidden="true"></span></div>
</div>
<div>
...
</body>
<script type="text/javascript">
var app = angular.module('myApp', ['ngMessages']);
app.controller(
'MyCtrl',
['$http','$location','$timeout', function($http, $location, $timeout) {
var self = this;
self.Id="";
...
self.deadline = "";
self.prevDeadline = "";
// function begin
//handles updates on the page
self.update = function(varname){
var changed = false;
// the 2 statements show self.deadline not get updated
// with the new date value from the custom directive
// can't diff. with prevDeadline to detect a change
console.log("prev.DeadLine: " + self.prevDeadline);
console.log("new.deadLine: " + self.deadline);
...
}
//retrieve data initially
self.getData = function(Id){
$http.get('/rest/v3/getData/',{params:{betId: betId}}).then(
function(response){
var data = response.data;
....
self.deadline = data.deadline;
self.prevDeadline = self.deadline;
...
);
}
} ]);
app.directive('jqDatePicker', function (){
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
element.datepicker({
dateFormat: 'D, d MM yy',
onSelect: function (date) {
scope.deadline = date;
scope.$apply();
}
});
}
};
});
</script>
</html>
Sorry about the long post. I am a novice and find using angular UI Bootstrap approach to solve this problem is a bit too advance for my comprehension currently. Thanks for your help.
Follow up question : If I use angular datepicker approach like in this demo code https://angular-ui.github.io/ui-date/, the directive code looks like this
angular.module('MyApp', ['ui.date'])
.controller('MyCtrl', function($scope) {
$scope.aDate = '2015-10-31';
$scope.dateOptions = {
dateFormat: 'dd.mm.yy',
}
})
My existing controller code looks like this
var app = angular.module('myApp', ['ngMessages']);
app.controller(
'MyCtrl',
['$http','$location','$timeout', function($http, $location, $timeout) {
var self = this;
self.Id="";
...
self.deadline = "";
self.prevDeadline = "";
...
} ]);
How do I merge the two code snippets together ? Will it be like this ?
var app = angular.module('myApp', ['ui.date', 'ngMessages']);
app.controller(
'MyCtrl',
['$http','$location','$timeout', '$scope', function($http, $location, $timeout, $scope) {
var self = this;
self.Id="";
...
self.deadline = "";
self.prevDeadline = "";
...
$scope.aDate = '2015-10-31';
$scope.dateOptions = {
dateFormat: 'dd.mm.yy',
...
} ]);
I have found Angular.org documentation and tutorial not easy to understand for me as a beginner. So far I have relied on finding code samples on the web to code for what I need. I'd like to learn angular custom directive properly so I can code for different requirements instead of being stuck when I can't find code samples to copy and paste. Thanks very much for your help.
Upvotes: 1
Views: 1181
Reputation: 5571
You only need:
ngModelCtrl.$setViewValue(date);
at:
app.directive('jqDatePicker', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
$(element).datepicker({
dateFormat: 'D, d MM yy',
onSelect: function(date) {
ngModelCtrl.$setViewValue(date); // <--
}
});
}
};
});
ngModel.NgModelController:
NgModelController provides API for the ngModel directive. The controller contains services for data-binding, validation, CSS updates, and value formatting and parsing. It purposefully does not contain any logic which deals with DOM rendering or listening to DOM events. Such DOM related logic should be provided by other directives which make use of NgModelController for data-binding to control elements.
$setViewValue(value, trigger):
Update the view value.
This method should be called when a control wants to change the view value; typically, this is done from within a DOM event handler. For example, the input directive calls it when the value of the input changes and select calls it when an option is selected.
Something like this:
(function() {
var app = angular.module('myApp', ['ngMessages']);
app.controller('MyCtrl', ['$http', '$location', '$timeout', function($http, $location, $timeout) {
var self = this;
self.Id = "";
self.deadline = "";
self.prevDeadline = "";
//retrieve data initially
self.getData = function(Id) {
$http.get('/rest/v3/getData/', {
params: {
betId: betId
}
}).then(function(response) {
var data = response.data;
self.deadline = data.deadline;
self.prevDeadline = self.deadline;
}, function(response) {
console.log(response);
});
};
}]);
app.directive('jqDatePicker', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
$(element).datepicker({
dateFormat: 'D, d MM yy',
onSelect: function(date) {
ngModelCtrl.$setViewValue(date);
}
});
}
};
});
})();
<link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-messages.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div data-ng-app="myApp">
<div data-ng-controller="MyCtrl as myCtrl">
<div class="container">
<div class="row">
<div class="col-sm-4 col-md-4"><strong>Deadline:</strong></div>
<div class="col-sm-8 col-md-8">
<input type="text" class="form-control" id="theDate" ng-model="myCtrl.deadline" required jq-date-picker><span class="glyphicon glyphicon-calendar" aria-hidden="true"></span></div>
</div>
<div>
Selected Date: {{myCtrl.deadline}}
</div>
</div>
</div>
</div>
Using https://angular-ui.github.io/ui-date/assets/date.js:
ui-date="dateOptions"
in your textBox date field.'ui.date'
dependency in your module.'$scope'
in your controller.Something like this:
(function() {
var app = angular.module('myApp', ['ngMessages', 'ui.date']);
app.controller('MyCtrl', ['$scope', '$http', '$location', '$timeout', function($scope, $http, $location, $timeout) {
var self = this;
self.Id = "";
self.deadline = "06.10.2015";
self.prevDeadline = "";
$scope.dateOptions = {
dateFormat: 'dd.mm.yy',
};
//retrieve data initially
self.getData = function(Id) {
$http.get('/rest/v3/getData/', {
params: {
betId: betId
}
}).then(function(response) {
var data = response.data;
self.deadline = data.deadline;
self.prevDeadline = self.deadline;
}, function(response) {
console.log(response);
});
};
}]);
})();
<link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-messages.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="https://angular-ui.github.io/ui-date/assets/date.js"></script>
<div data-ng-app="myApp">
<div data-ng-controller="MyCtrl as myCtrl">
<div class="container">
<div class="row">
<div class="col-sm-4 col-md-4"><strong>Deadline:</strong></div>
<div class="col-sm-8 col-md-8">
<input type="text" class="form-control" id="theDate" ng-model="myCtrl.deadline" required ui-date="dateOptions"><span class="glyphicon glyphicon-calendar" aria-hidden="true"></span></div>
</div>
<div>
Selected Date: {{myCtrl.deadline}}
</div>
</div>
</div>
</div>
Hope this helps.
Upvotes: 0