Angular form $pristine is set to false by default - angularjs

Plunker link of my usecase.
I am using Eternicode's Bootstrap Datepicker in my app. I have created a datepicker directive and same is used in Angular form.
Datepicker directive :
angular.module('MyApp', [])
.directive('myDatePicker', function($compile) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
$(element[0]).datepicker({
autoclose: true,
format: "dd/mm/yyyy"
});
$(element[0]).datepicker().on("change", function(e) {
scope.$apply(function() {
ngModelCtrl.$setViewValue(element.val());
});
});
}
};
});
When the Angular form is initially loaded, following are its properties:
$pristine : false
$dirty : true
When i watch the form model and print them on console, i understood form model had property that was set by datepicker and this is how it looks :
On same Plunker link , if i comment date section in form following are its properties:
$pristine : true
$dirty : false
This is how form model looks now :
How do i keep my form model free from being corrupted, which is causing problems by setting $pristine to false and $dirty to true, despite of using Date directive ?
PS : This is the abstraction of the bigger use-case that is in our current application.
Thanks in advance.

I have investigated you code and observed that, you should write you following code in compile function of directive.
$(element[0]).datepicker({
autoclose: true,
format: "dd/mm/yyyy"
});
it will resolve your problem.
so your directive looks like as follow :
.directive('myDatePicker', function($compile) {
return {
restrict: 'A',
require: 'ngModel',
compile: function(scope, element, attrs) {
$(element[0]).datepicker({
autoclose: true,
format: "dd/mm/yyyy"
});
},
link: function(scope, element, attrs, ngModelCtrl) {
$(element[0]).datepicker().on("change", function(e) {
scope.$apply(function() {
ngModelCtrl.$setViewValue(element.val());
});
});
}
};
The compile function deals with transforming the template DOM. Since most directives do not do template transformation, it is not used often.

I don't completely understand your use case, but after you set your date selection, why don't you just do a programmatic
$scope.form.$setPristine();
I believe this was introduced in v1.1

Related

'attrs' property in angular directive is not working properly

I am trying to write a very simple directive which involves setting a property based off one of the attributes that it is provided. The issue I'm facing is that the value of the attrs object is not being consistently recognized within the link function.
Here is the grand total of what I'm currently trying to achieve :
angular.module('directives').directive('wikiNotes',function() {
return {
restrict: 'EA',
templateUrl: 'common/directives/wiki-notes.tpl.html',
link: function(scope, element, attrs) {
console.log(attrs.openEdit); //true
if(attrs.openEdit===true){
console.log('open edit'); //not called
}
}
};
});
The console.log(attrs.openEdit) is showing as true, but then the console.log in the if block is not getting called. Am I missing something very obvious or is this a quirk with angular directives?
Did you consider adding this attribute in your directive scope section?
I think it is more in the Angular philosophy, but this imply you create a new scope for your directive.
angular.module('directives')
.directive('wikiNotes',function() {
return {
restrict: 'EA',
scope: {
openEdit: '='
},
templateUrl: 'common/directives/wiki-notes.tpl.html',
link: function(scope) {
console.log(scope.openEdit); //true
if(scope.openEdit===true){
console.log('open edit'); //should be a boolean
}
}
};
});
Here is a JS fiddle that demonstrate it works.
https://jsfiddle.net/c8mn9wka/3/

How to use "required" attribute within Angular directive template?

I'm using an AngularJS directive to generate two radio buttons. I'm hardcoding the "required" attribute into the directive template, but it doesn't behave as expected.
When no radio buttons are checked, it correctly displays this error message, as expected.
<p ng-show="form.$invalid">Error: the form is invalid.</p>
But it doesn't display this error message.
<p ng-show="form.gender.$invalid">Error: the gender input is invalid.</p>
Any idea why?
Please see the Plunker for details:
http://plnkr.co/edit/i5kVeX8WdrUbM83lT6O6?p=preview
You need to tell your directive to validate input fields initially. You can do this by defining $formatters callback checking validity of the fields:
app.directive('dRadio', function() {
return {
require: '^form',
restrict: 'E',
scope: { model: '=ngModel' },
template: '<input required type="radio" id="{{value}}" name="{{name}}" value="{{value}}" ng-model="model"><label for="{{value}}">{{label}}</label>',
link: function(scope, element, attrs, ngModelController) {
scope.name = attrs.name;
scope.value = attrs.value;
scope.label = attrs.label;
ngModelController[attrs.name].$formatters.unshift(function(value) {
ngModelController[attrs.name].$setValidity('required', !!scope.model);
return value;
});
}
};
});
Note, that you also need to add require: '^form' rule.
Demo: http://plnkr.co/edit/rac1RNNOWHs1wTnktJHE?p=preview
You can accomplish by doing this way
Plnkr
I changed the following line
<p ng-show="!test">Error: the gender input is invalid.</p>

How to initialize datepickers in base controller?

I'm trying to avoid having to initialize datepickers in every controller of my application. I would like to have a central place where I initialize my datepickers.
I tried initializing datepickers in my base controller, but it doesn't work - I'm guessing the DOM isn't rendered at that point.
Where should this piece of code be put?
I also tried (in base ctrl)
angular.element(document).ready(function() {
$('.date-picker').datepicker({
autoclose: true,
format: "yyyy-mm-dd",
setDate: new Date(),
});
});
but to no avail.
You can attach a datePicker directive to your datepicker elements:
app.directive("datePickerInit", [function() {
return {
restrict: "A",
link: function(scope, elem, attrs) {
$(elem).datepicker({
autoclose: true,
format: "yyyy-mm-dd",
setDate: new Date(),
});
}
}
}]);
Then just attach:
<input type="date" date-picker-init />

Access ng-model inside jQuery control

I have created an AngularJS directive using the bootstrap-datepicker which is based on jQuery and I want some stuff based on the ng-model which I'm sending to the directive. The problem is that on the datepicker init click events I don't have access to anything angular related. There is no scope, no ngModel nothing. All of these are undefined. Now, I might be missing something obvious, but I don't know what this is. Here is the code for the directive:
.directive('jqCalendar', function () {
return {
restrict: 'EA',
require: 'ngModel',
template: '<div></div>',
replace: true,
scope:{
ngModel: '='
},
link: function (scope, element, attrs, ngModelCtrl) {
var ngModel = scope.ngModel;
element.datepicker({
multidate: true,
beforeShowDay: function (date) {
var today = new Date();
var angularModel = ngModel; //ngModel is always undefined in here, why?!
if (date < today) return false;
},
format: 'dd MM yyyy',
weekStart: 1
})
.on('changeDate', function (e) {
//make server call for selected/deselected date
alert('asd');
});
}
}
})
Outside the element.datepicker({...}) I can see the value of my model, but once inside the said control, I cannot access anything. Any ideas?

AngularJS - Formatting ngModel on custom datepicker directive

I'm using JQuery bootstrap datepicker (eternicode.github.io/bootstrap-datepicker/) in an angular application.
I've wrote my own directive to wrap this datepicker and do some date formatting. Datepicker works fine and display an appropriate date format.
Currently, I want to show a formatted date value and put a timestamp formatted value in ngModel.
My code is okay when I'm not trying to use template in my directive :
cosyApp.directive("datepicker", ['moment',
function(moment) {
function link($scope, element, attrs, ctrl) {
// Init JQuery datepicker
element.datepicker();
ctrl.$parsers.push(function(valueFromInput) {
// Format displayed value in timestamp format
return moment(valueFromInput).format('X');
});
}
return {
restrict: 'A',
require: 'ngModel',
link: link
};
}
]);
But when I use template attribute, ngModel and displayed value are the same :
return {
restrict: 'A',
replace: true,
require: 'ngModel',
template: "<div class='input-group date'> {{ngModel}}" +
"<input class='form-control' ng-model='ngModel'>" +
"<span class='input-group-addon'><i class='glyphicon glyphicon-calendar'></i></span>" +
"</div>",
link: link
};
I made a few changes to your plunker. I didn't try to figure out why the calendar is showing up right away, I instead just bound the datepicker to the input field.
Instead of trying to use ngModel on the input ng-mode directly as you were doing, I created an intermediate model object, then adding a function to watch for changes to the input and pass those changes to the ngModelController directly.
Side note, I believe if you plan on updating your model value outside the UI and want it to update the view, you will have to add a $watch to update the intermediate model object.
http://plnkr.co/edit/5213zUvnqyv0ARqc11aU?p=preview
cosyApp.directive("datepickerx",
function($window) {
function link($scope, element, attrs, ctrl) {
$scope.model = ctrl.$viewValue;
// Init JQuery datepicker
element.find('input').datepicker({
autoclose: true,
clearBtn: true,
});
$scope.changed = function() {
ctrl.$setViewValue($scope.model);
}
ctrl.$parsers.push(function(valueFromInput) {
// Format displayed value in timestamp format and store it to ngModel
return $window.moment(valueFromInput).format('X');
});
}
/* ********** This part of code doesn't works ********** */
return {
restrict: 'A',
replace: true,
require: 'ngModel',
scope: {
ngModel: '='
},
template: '<div class="input-group date">' +
'<input class="form-control" ng-model="model" ng-change="changed()"/>' +
'<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>' +
'</div>',
link: link
};
}
);

Resources