Angular UI datepicker validated as false when empty - javascript

I'm using the JqueryUI datepicker(1.0.0) directive with Angular 1.4.9 to display a date of birth. This field is not required and should not be validated unless it's filled.
Once the page loads, the field is validated as true(As expected). And once the user selects a date, it's again valid. But if we were to manually erase the field, the field becomes invalid.
<input ui-date="dateOptions" name="dateOfBirth" ng-model="dob"/>
ng-model can be set to the same value before and after, but the value remains invalid.
I've created a JSFiddle that replicates the problem here.
https://jsfiddle.net/nipuna777/ctsmuv80/

As said #nipuna777, it looks like a bug in the datepicker.
We can fix it with a directive.
Live example on jsfiddle.
var myApp = angular.module('myApp', ['ui.date']);
myApp.directive('uiDateFix', function($timeout) {
return {
restrict: "A",
require: 'ngModel',
priority: 100,
scope: {
ngRequired: "="
},
link: function(scope, elem, attr, ngModel) {
var originalValidate = null;
$timeout(function() {
if (!originalValidate)
originalValidate = ngModel.$validators.uiDateValidator;
ngModel.$validators.uiDateValidator = function uiDateValidator2(modelValue, viewValue) {
//Define correct validations
if (viewValue || scope.ngRequired)
return originalValidate(modelValue, viewValue);
else
return true;
}
}, 500);
}
}
});
//myApp.factory('myService', function() {});
myApp.controller('MyCtrl', function($scope) {
$scope.name = 'Superhero';
$scope.value = 'Superhero';
$scope.dateOptions = {
changeYear: true,
changeMonth: true,
yearRange: '1900:-0'
};
$scope.isReq = true;
$scope.dob = ""
})
<script src="https://code.jquery.com/jquery-2.2.1.js" charset="utf-8"></script>
<script src="https://code.jquery.com/ui/1.11.3/jquery-ui.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.9/angular.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-date/1.0.0/date.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.11.3/themes/smoothness/jquery-ui.css">
<body ng-app="myApp">
<div ng-controller="MyCtrl">
<h1> Datepicker</h1>
<form name="person">
<label for="dateOfBirth">Date of Birth: </label>
<input ui-date="dateOptions" name="dateOfBirth" ng-model="dob" ui-date-fix ng-required="isReq" />
<label for="isReq">Required </label>
<input type="checkbox" name="isReq" ng-model="isReq" />
<p>
dob value: {{dob}}
</p>
<p>
dob valid: {{person.dateOfBirth.$valid}}
</p>
<p>
dob errors: {{person.dateOfBirth.$error}}
</p>
<hr>
<h2>Recreating the problem</h2>
<ol>
<li>With the field empty dob is valid</li>
<li>When you select a value from datepicker again the dob field is valid</li>
<li>When dob is manually removed, the field becomes invalid</li>
</ol>
</form>
</div>
</body>

It is an issue in the validator of the plugin, it is fixed in the master branch/dist/date.js.
So you can use that file instead of the one in the cdn to fix it

Related

how to bind with model with the value from another model in html?

As you all know input='date' will not return the formatted value we want. So I put the pipeline below like | date: "dd/MM/yyyy" then I can get what I want. But how can I bind that value with a model?
<input ng-disabled="defaultSaveButtons[$index]" ng-init="InitializeDateForToday($index,field)" type="date" placeholder="dd/mm/yyyy" class="form-control text-right inputFill2" ng-model="deliveryDate" />
<span ng-model="field.value">{{deliveryDate | date: "dd/MM/yyyy"}}</span>
<br />
<span>{{field.value}}</span>
Use the date filter inside your controller like below:
angular.module("myApp", [])
.controller("myCtrl", function($scope, $filter) {
$scope.field = { value : "" };
$scope.setField = function() {
var date = $filter('date')($scope.deliveryDate, "dd/MM/yyyy");
$scope.field.value = date;
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<input type="date" placeholder="dd/mm/yyyy" ng-model="deliveryDate" ng-change="setField()"/>
<br/><br/>
<span>{{field.value}}</span><br/>
<span>{{deliveryDate}}</span>
</div>

Submit form when async validator returns promise in AngularJS

I have created a async validator 'username-validator' to check username availability and to avoid number of remote calls I am updating ng-model on 'blur' event rather than on 'key press'. So validator is triggered only on 'blur' event.
<input type="text" ng-model="signup.username" name="username"
required
username-validator
ng-model-options="{ updateOn: 'blur' }">
But when form gets submitted it checks if form is valid and only then it gets submitted.
$scope.submit = function(form) {
if (form.$valid) {
alert('submitting', $scope.signup.username, $scope.signup.password);
}
};
But when I am clicking on Submit button and if form is in $pending state then form doesn't get submitted in one click, because at the time of click it is in $pending state and $valid is undefined.
I want to write my $scope.submit function to handle submission once $pending state is resolved.
How do i do it?
Don't want to disable the Submit button for $pending state.
To demonstrate the problem I am adding running snippet with it.
angular.module('app', ['ngMessages'])
.controller('MainCtrl', function($scope) {
$scope.signup = {};
$scope.submit = function(form) {
if (form.$valid) {
alert('submitting ' + $scope.signup.username + ' ' + $scope.signup.password);
}
};
})
.directive('usernameValidator', function($q, $timeout) {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$asyncValidators.username = function(modelValue, viewValue) {
if (!viewValue) {
return $q.when(true);
}
var deferred = $q.defer();
$timeout(function() {
// Faking actual server-side validity check with $http.
// Let's pretend our service is so popular all short username are already taken
if (viewValue && viewValue.length < 5) {
deferred.reject();
}
deferred.resolve();
}, 2000);
return deferred.promise;
};
}
};
});
.validation-error {
color: red;
}
.validation-pending {
color: orange;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular-messages.min.js"></script>
<script src="script.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body ng-app="app">
<div ng-controller="MainCtrl">
<form name="myForm" ng-submit="submit(myForm)" novalidate>
<div>
<label>
Username:
<input type="text" ng-model="signup.username" name="username" required username-validator ng-model-options="{ updateOn: 'blur' }">
</label>
<div ng-if="myForm.username.$dirty">
<div ng-messages="myForm.username.$error" class="validation-error">
<div ng-message="required">Username required</div>
<div ng-message="username">Username already in use</div>
</div>
<div ng-messages="myForm.username.$pending" class="validation-pending">
<div ng-message="username">Checking username availability...</div>
</div>
</div>
</div>
<div>
<label>
Password:
<input type="password" ng-model="signup.password" name="password" required>
</label>
<div ng-messages="myForm.password.$error" ng-if="myForm.password.$dirty" class="validation-error">
<div ng-message="required">Password required</div>
</div>
</div>
<button type="submit">Submit</button>
</form>
</div>
</body>
</html>
You could attach the promise to the scope when checking for the username
scope.usernameCheck = deferred.promise;
return deferred.promise;
Then in your submit function wait for the promise to be resolved
$scope.submit = function(form) {
if ($scope.usernameCheck) {
$scope.usernameCheck.then(function() {
if (form.$valid) {
alert('submitting ' + $scope.signup.username + ' ' + $scope.signup.password);
}
});
}
};

angularJs Form validation with dynamic error message

I want validate a form where for each input may have many error messages as per input. I want to write those error message from javaScript.
HTML
<input name="username" ng-model="user.username" type="text" placeholder="Username" required>
<div class="field-message" ng-messages="frm.username.$error" ng-if='frm.username.$dirty' ng-cloak>
<div ng-message="required">Username is required</div>
</div>
JS
angular.module('DemoApp', ['ngMessages']);
angular.bootstrap(document.getElementById('demoApp'), ['DemoApp']);
In the above code ng-message directive have error message that will be shown if input is not valid. If there are so many case to validate and each case have different error message then multiple ng-message have to add in html for particular input control.
I want to only input tag in html and error message to be come from javascript.
How can I achieve this?
See this question: How to add custom validation to an AngularJS form?
To summarize:
You can use ui-validate
OR
You can just create custom validation, like so (copied from the answer in the original question):
app.directive('blacklist', function (){
return {
require: 'ngModel',
link: function(scope, elem, attr, ngModel) {
var blacklist = attr.blacklist.split(',');
//For DOM -> model validation
ngModel.$parsers.unshift(function(value) {
var valid = blacklist.indexOf(value) === -1;
ngModel.$setValidity('blacklist', valid);
return valid ? value : undefined;
});
//For model -> DOM validation
ngModel.$formatters.unshift(function(value) {
ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
return value;
});
}
};
});
And here's some example usage:
<form name="myForm" ng-submit="doSomething()">
<input type="text" name="fruitName" ng-model="data.fruitName" blacklist="coconuts,bananas,pears" required/>
<span ng-show="myForm.fruitName.$error.blacklist">
The phrase "{{data.fruitName}}" is blacklisted</span>
<span ng-show="myForm.fruitName.$error.required">required</span>
<button type="submit" ng-disabled="myForm.$invalid">Submit</button>
</form>
EDIT: According to OP's request, print only the relevant error:
Plunkr: http://plnkr.co/edit/KN0Oukb8uACvZjtGPUg4?p=preview
var app = angular.module('angularjs-starter', []);
app.controller('MainCtrl', function($scope) {
$scope.doSomething = function() {
alert('Submitted!');
}
$scope.getErrors = function() {
if ($scope.myForm.fruitName.$error.required) {
return 'This field is required';
} else if ($scope.myForm.fruitName.$error.blacklist) {
return 'The phrase ' + $scope.data.fruitName + ' is blacklisted';
}
}
});
app.directive('blacklist', function() {
return {
require: 'ngModel',
link: function(scope, elem, attr, ngModel) {
var blacklist = attr.blacklist.split(',');
ngModel.$parsers.unshift(function(value) {
ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
return value;
});
}
};
});
.invalid {
color: red;
}
.ng-invalid {
border-color: red;
}
<!DOCTYPE html>
<html ng-app="angularjs-starter">
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<form name="myForm" ng-submit="doSomething()">
<div>Please <u>don't</u> type the words: coconuts, bananas or pears.</div>
<input type="text" name="fruitName" ng-model="data.fruitName" blacklist="coconuts,bananas,pears" required/>
<span class="invalid" ng-show="myForm.fruitName.$error">{{getErrors()}}</span>
<br/>
<button type="submit" ng-disabled="myForm.$invalid">Submit</button>
<button type="button" ng-click="getErrors()">Get errors</button>
</form>
</body>
</html>

angularJS hide bootstrap datepicker on icon click

in my app i try to use bootstrap datepicker
but i have one trouble: how to hide it on icon and some other field (if i will need) when it's is displayed?
my view:
<div class="input-group date custom">
<input type="text" id="date" data-ng-model="article.Date" name="date" class="form-control" required/><span class="input-group-addon"><i class="date-ico"></i></span>
</div>
and directive (it's declaration is on modal window):
.directive('dateClick', function ($window) {
return {
restrict: 'EA',
link: function (scope, element, attrs) {
var dateIcon = element.find('.date-icon');
dateIcon.bind('click', function () {
var datepckr = angular.element(document).find('.datepicker');
if (typeof datepckr[0] !== 'undefined'){
console.log('hide');
var datepckrMenu = angular.element(document).find('.datepicker.dropdown-menu');
datepckrMenu.datepicker('hide');
}
});
}
}
});
how could i hide my datepicker in the best way?
also i see that it didn't hide if i click in textAngular's editor...
you can try it me be it's help...
Live Demo
github Link
<div>
<div data-ng-controller="AppCtrl">
<input id="datepicker" type="text" data-ng-datepicker data-ng-options="datepickerOptions" data-ng-model="date">
<input id="datepickerMirror" type="text" data-ng-model="date">
</div>
</div>
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/2.0.4/css/bootstrap.min.css">
<link rel="stylesheet" href="https://rawgit.com/cletourneau/angular-bootstrap-datepicker/master/dist/angular-bootstrap-datepicker.css">
<script src="http://code.jquery.com/jquery-2.0.2.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/2.0.4/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="https://rawgithub.com/cletourneau/angular-bootstrap-datepicker/master/dist/angular-bootstrap-datepicker.js" charset="utf-8"></script>
Js Script
app = angular.module 'myapp', ['ng-bootstrap-datepicker']
AppCtrl = ($scope)->
$scope.datepickerOptions =
format: 'yyyy-mm-dd'
language: 'fr'
autoclose: true
weekStart: 0
$scope.date = '2000-03-12'
app.controller 'AppCtrl', AppCtrl
angular.bootstrap document, ['myapp']

AngularJS watch not being triggered

I'm having a problem with AngularJs. I have created a directive that $watch the model and it takes some action based on the model's current status. However, although during debugging I can see that the $watch is set, it is only being triggered after the model get valid at least once and I don't know why that is happening. Debugging it doesn't even gets into the $watch function when something is typed.
The code is below:
Directive:
(function() {
'use strict';
var app = angular.module('app');
app.directive('tooltipValidation', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
var tooltip = $(element).qtip({
content: {
text: element.next('div')
},
show: false,
hide: true
}).qtip('api');
scope.$watch(attrs.ngModel, function() {
if (ngModel.$invalid && ngModel.$dirty) {
tooltip.show();
} else {
tooltip.hide();
}
});
}
};
});
})()
HTML:
<div class="form-address clearfix" ng-show="isNewShippingAddress" ng-form="newShippingAddressForm">
<h3>Include new shipping address:</h3>
<div class="novo-endereco clearfix" id="newAddress">
<div class="required address apelido">
<label for="newShippingAddressAlias">Alias</label>
<input id="newShippingAddressAlias" name="newShippingAddressAlias" type="text" tooltip-validation ng-model="newShippingAddress.Alias" required ng-maxlength="32" />
<div data-ng-show="newShippingAddressForm.newShippingAddressAlias.$dirty && newShippingAddressForm.newShippingAddressAlias.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressAlias.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressAlias.$error.maxlength">max 32 char</p>
</div>
</div>
<div class="required endereco">
<label for="newShippingAddressStreet">Street</label>
<input id="newShippingAddressStreet" name="newShippingAddressStreet" type="text" tooltip-validation ng-model="newShippingAddress.Street" required ng-maxlength="256" />
<div data-ng-show="newShippingAddressForm.newShippingAddressStreet.$dirty && newShippingAddressForm.newShippingAddressStreet.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressStreet.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressStreet.$error.maxlength">max 256 char</p>
</div>
</div>
<div class="required cep">
<label for="newShippingAddressZipCode">ZipCode</label>
<input id="newShippingAddressZipCode" name="newShippingAddressZipCode" type="text" tooltip-validation ng-model="newShippingAddress.ZipCode" required ng-pattern="/^[0-9]{8}$/" />
<div data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$dirty && newShippingAddressForm.newShippingAddressZipCode.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$error.pattern">8 digits</p>
</div>
</div>
<input type="submit" class="button grey" value="Save new address" data-ng-click="saveShippingAddress()" ng-disabled="newShippingAddressForm.$invalid" />
</div>
</div>
Regards,
dimello
Try:
scope.$watch(function(){
return ngModel.$viewValue; //Watch for view value (the value in your input)
}, function() {
if (ngModel.$invalid && ngModel.$dirty) {
tooltip.show();
} else {
tooltip.hide();
}
});
DEMO
Explanation:
When you type an invalid value into the input with ng-model, the underlying model is not updated, causing your scope.$watch(attrs.ngModel not being fired because you're watching for changes in the model. If you need to fire the function every time the input changes no matter it's valid or not, try the above solution.

Categories

Resources