I'm trying to use input[date] in my form, but angularjs doesn't validate any date:
<form name="myForm">
{{birthday}} <br/>
<input type="date" ng-model="birthday" id="birthday" name="birthday"></input>
</form>
If I try to put "alex" inside the input, it accepts!
JSFiddle: http://jsfiddle.net/3GKeM/2/ (please try with firefox)
You're trying to use a new feature in AngularJS 1.3.x from AngularJS 1.0 which doesn't support type="date".
If you want that support on FireFox. You'll have to use AngularJS. 1.3
The documentation you're referring to is for 1.3
https://docs.angularjs.org/api/ng/input/input%5Bdate%5D
This behaves as expected in Firefox. CanIUse.com reports that this HTML5 feature is not yet supported in Firefox 32 or earlier.
So you'll need to implement a javascript solution if you want all browsers to behave similarly. I'd suggest jQuery UI Datepicker. It's used by a lot of people, has an alright look to it (which you can customize actually) and there are lots of examples on how to use it.
Other options:
Pickdate
Bootstrap Datepicker
And the Angular UI Bootstrap option
Update
You stated that you already have your own datepicker (in comments below), and you are just looking for validation: you may want to consider looking at HTML5 Patterns - Date. They have a variety of Regex solutions for validation. They have several different date formats already in place for you.
And then you can use Angular ng-patter to pass in the regex:
example:
<input type="text" ng-pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])"></input>
//accepts: YYYY-MM-DD
I would either use ng-validate and a regex to check the format of the input value or because i do believe ng-validate has been deprecated depending on what version you're using, I would use ng-change on the input where you pass the model for the form to a function where you then check the format of the value and set the model to be invalid or valid. Here's a working example I've pulled from my app where i check to see if it is in fact a phone number and that it begins with 1.
checkNumber: function(number,form) {
var num = parseInt(number.replace(/[^0-9\.]+/g, '')),
l = num.toString().length,
first = num.toString()[0];
console.log(num);
if(number && first != '1' || number && l < 11) {
form.$setValidity('phoneNumber',false);
form.phoneNumber.$setValidity('format',false);
} else {
form.$setValidity('phoneNumber',true);
form.phoneNumber.$setValidity('format',true);
}
}
There are browser inconsistencies as to what constitutes a valid date. A valid date string in firefox or ie, is invalid in chrome and vice-versa. I solved a similar issue by creating a custom validation directive that works quite nicely. you can find it here:
http://jsfiddle.net/fortesl/2uv6xmjL/6/
Also shown below:
app.directive('dateFieldValidator', [function () {
var validateDate = function (date, format) {
if (!date.length) {
return true;
}
return moment(date, format.toUpperCase(), true).isValid();
};
return {
restrict: 'A',
require: 'ngModel',
scope: {
dateFormat: '#'
},
link: function (scope, elem, attrs, ngModelCtrl) {
//For DOM -> model validation
ngModelCtrl.$parsers.unshift(function (value) {
var valid = validateDate(value, scope.dateFormat);
ngModelCtrl.$setValidity('validDate', valid);
return valid ? value : undefined;
});
//For Model Update --> DOM
ngModelCtrl.$formatters.unshift(function (value) {
var valid = validateDate(value, scope.dateFormat);
ngModelCtrl.$setValidity('validDate', valid);
return value;
});
}
};
}]);
Related
I wanted to use an input[date] inside a form to enable users to use the modern browsers' support, without using a javascript datepicker.
For the ones who're using not supporting browsers, I'v set ui-mask. Both work together.
I use the french format (dd/mm/YYYY).
<input type="date" ng-model="myDate" ui-mask="99/99/9999">
$scope.myDate = new Date("2016-02-12"); // works
$scope.myDate = "12/02/2016"; // Doesn't...
In an empty form, no problem, Angular accept it.
But with a filled form, Angular expects a date format I don't want : YYYY-mm-dd (javascript date object).
I can't understand why Angular only provides support that format, and not others like french format. Because I want the input[date] to show the french format, not the standard one !
Many modern browsers provides interesting support for dates, I can't understand why Angular waste that.
Or maybe I missed something ?
To achieve your goal need to use momentJs and ngModel $parsers and $formatters. Below some code and JSFiddle example:
In your controller you prepare some string value in french date format:
$scope.myDate= '12/02/2016';
In view you set this value to ngModel attribute of input[type=date] element:
<input class="form-control" type="date" ng-model="myDate">
You need to create new directive, it can be assigned to input element:
.directive('input', function(dateFilter) {
return {
restrict: 'E',
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
if (
'undefined' !== typeof attrs.type && 'date' === attrs.type && ngModel
) {
ngModel.$formatters.push(function(modelValue) {
return moment(modelValue, "DD/MM/YYYY").toDate();
});
ngModel.$parsers.push(function(viewValue) {
return moment(viewValue).format("DD/MM/YYYY");
});
}
}
}
})
New function in $formatters array modifies your myDate to javascript date object (with moment parser it's pretty simple). New function in $parsers array modifies value of input element back to french formatted date string.
This solution provides two way binding between your myDate and input[type=date] element. :)
Look at JSFiddle exaple
Trying to implement a textarea component with emoticons support while writing.
I want to be able to backup the original text (ascii chars only) while presenting the filtered/generated html outcome (with an angular emoticons filter) on a div.
My initial solution is to
<textarea ng-model="text" ng-change="..." ng-focus="..."></textarea>
<div ng-bind-html="text | myEmoticonsFilter"></div>
but I'm having trouble getting to the part of using a hidden textarea. Also, with this I wouldn't be able to mouse-select text and delete or copy/paste safely.
I also thought of using a <div contenteditable="true"> but ng-focus and ng-change wouldn't be handled.
Does anyone have any sugestion on how to continue this?
Edit 1: here is a jsfiddle with an attempt on what I'm doing. Up until now, able to replace the first occurrence, but the behavior remains erratic since that. I'm using a contenteditable directive for 2-way data binding and to filter the emoticon pattern.
Edit 2: regarding my statement saying that ng-focus and ng-change wouldn't be handled, that is not true - ng-focus works natively on <div contenteditable="true"> and ng-change will work as long as a directive is declared using the ngModel and setting the appropriate $modelValue and $viewValue (an example is provided in the jsfiddle in Edit 1).
The only way to do this in a consistently cross-browser manner is to use a WYSIWYG field that converts emoji to images.
There's a jQuery plugin jquery-emojiarea that does what you need, so you'd just need to create a directive that wraps this plugin and you're off to the races. Since it inputs into a hidden textarea with emoji syntax :smile: angular should have no difficulty binding.
Here's a working directive I threw together. http://jsfiddle.net/dboskovic/g8x8xs2t/
var app = angular.module('app', []);
app.controller('BaseController', function ($scope) {
$scope.text = 'This is pretty awesome. :smile: :laughing:';
});
app.directive('emojiInput', function ($timeout) {
return {
restrict: 'A',
require: 'ngModel',
link: function ($scope, $el, $attr, ngModel) {
$.emojiarea.path = 'https://s3-us-west-1.amazonaws.com/dboskovic/jquery-emojiarea-master/packs/basic';
$.emojiarea.icons = {
':smile:': 'smile.png',
':angry:': 'angry.png',
':flushed:': 'flushed.png',
':neckbeard:': 'neckbeard.png',
':laughing:': 'laughing.png'
};
var options = $scope.$eval($attr.emojiInput);
var $wysiwyg = $($el[0]).emojiarea(options);
$wysiwyg.on('change', function () {
ngModel.$setViewValue($wysiwyg.val());
$scope.$apply();
});
ngModel.$formatters.push(function (data) {
// emojiarea doesn't have a proper destroy :( so we have to remove and rebuild
$wysiwyg.siblings('.emoji-wysiwyg-editor, .emoji-button').remove();
$timeout(function () {
$wysiwyg.emojiarea(options);
}, 0);
return data;
});
}
};
});
And usage:
<textarea ng-model="text" emoji-input="{buttonLabel:'Insert Emoji',wysiwyg:true}"></textarea>
If you want the editable field to convert text like :( as you type you'll need to fork that jquery plugin and modify it slightly to parse input text on change as well as on init. (like, a couple lines of code)
I am using Pikaday date picker plugin in my angular JS app.
Is it possible to have different format for view and different format while submitting?
For ex: 01 Jan 2014 (Display)
01/01/2014 (while submitting the form)
I know i can apply a filter while posting the content. However, since the date picker is been used in many places i have to do it at all the places separately. It will be great if the plugin updates my ng-model with a certain format but displays it differently.
You can get this by using angularjs directive (formatters and parsers).
For example using moment.js:
app.directive('fdate', function () {
return {
require: 'ngModel',
link: function(elem, $scope, attrs, ngModel){
ngModel.$formatters.push(function(val)
{
return moment(val).format("DD/MM/YYYY");
});
ngModel.$parsers.push(function(val){
return moment(val, "DD/MM/YYYY").valueOf();
});
}
}
});
and in your html:
<input type="text" ng-model="obj.yourdate" fdate>
i found this fiddle as an example:
http://jsfiddle.net/arunpjohny/wNBAn/
I posted the same question in different form, but no one answered. I am not getting a clear picture of what the Formatters and Parsers do in angular js.
By the definition, both the Formatters and Parsers look similar to me. Maybe I am wrong, as I am new to this angularjs.
Formatters Definition
Array of functions to execute, as a pipeline, whenever the model value changes.
Each function is called, in turn, passing the value through to the next.
Used to format / convert values for display in the control and validation.
Parsers Definition
Array of functions to execute, as a pipeline, whenever the control reads value from the DOM.
Each function is called, in turn, passing the value through to the next.
Used to sanitize / convert the value as well as validation.
For validation, the parsers should update the validity state using $setValidity(), and return undefined for invalid values.
Please help me to understand both features with a simple example. A simple illustration of both will be appreciated.
This topic was covered really well in a related question: How to do two-way filtering in AngularJS?
To summarize:
Formatters change how model values will appear in the view.
Parsers change how view values will be saved in the model.
Here is a simple example, building on an example in the NgModelController api documentation:
//format text going to user (model to view)
ngModel.$formatters.push(function(value) {
return value.toUpperCase();
});
//format text from the user (view to model)
ngModel.$parsers.push(function(value) {
return value.toLowerCase();
});
You can see it in action: http://plnkr.co/UQ5q5FxyBzIeEjRYYVGX?plnkr=legacy
<input type="button" value="set to 'misko'" ng-click="data.name='misko'"/>
<input type="button" value="set to 'MISKO'" ng-click="data.name='MISKO'"/>
<input changecase ng-model="data.name" />
When you type a name in (view to model), you will see that the model is always lowercase. But, when you click a button and programatically change the name (model to view), the input field is always uppercase.
Another usage for formatters and parsers is when you want to store dates in UTC time and display them in local time on inputs, I created the below datepicker directive and utcToLocal filter for this.
(function () {
'use strict';
angular
.module('app')
.directive('datepicker', Directive);
function Directive($filter) {
return {
require: 'ngModel',
link: function (scope, element, attr, ngModel) {
element.addClass('datepicker');
element.pickadate({ format: 'dd/mm/yyyy', editable: true });
// convert utc date to local for display
ngModel.$formatters.push(function (utcDate) {
if (!utcDate)
return;
return $filter('utcToLocal')(utcDate, 'dd/MM/yyyy');
});
// convert local date to utc for storage
ngModel.$parsers.push(function (localDate) {
if (!localDate)
return;
return moment(localDate, 'DD/MM/YYYY').utc().toISOString();
});
}
};
}
})();
It uses this utcToLocal filter that ensures the input date is in the correct format before converting to local time.
(function () {
'use strict';
angular
.module('app')
.filter('utcToLocal', Filter);
function Filter($filter) {
return function (utcDateString, format) {
if (!utcDateString) {
return;
}
// append 'Z' to the date string to indicate UTC time if the timezone isn't already specified
if (utcDateString.indexOf('Z') === -1 && utcDateString.indexOf('+') === -1) {
utcDateString += 'Z';
}
return $filter('date')(utcDateString, format);
};
}
})();
moment.js is used to convert local to utc dates.
pickadate.js is the datepicker plugin used
I have the following input field:
<input type="text" class="span2" ng-model="mynumber">
mynumber has the value 0.55 which is loaded on pageload from a rest service. My problem is now, how can I format the number for different languages/countries? For example, in German, the value should be formatted with a comma (,) instead of a period (.). And if the user changes the number the number should be converted to . instead of ,, if I send it back to the rest service.
This should also work for larger numbers like 90,000.00, which should be 90.000,00 in German...
If I use the angular-locale_de-at.js, I can format the number on a normal output with this:
{{mynumber | number}}
but that does not work for an input field.
How can I handle this? The values should be (printed) formatted in the input field.
If I canage the type of the input field to number
<input type="number" class="span2" ng-model="mynumber">
it works in chrome but not in IE or FF. in chrome i get 0,55. but not in other browsers.
any ideas?
I've written the directive you are looking for. See the code on GitHub.
Also see this answer on SO
Using AngularJS directive to format input field while leaving scope variable unchanged
It will probably not work with <input type="number"/>, use <input type="text"/> instead
Mh,
my first idea would be to separate the concerns here. You're having the model mynumber on the one side and the representation on the other side. Those are distinct from my point of view.
So what you can do is to introduce a directive (I once did this for date values in AngularJS 1.1.5, so bit a bit of additional hacking could be necessary):
First we introduce a directive:
<input type="text" class="span2" ng-model="mynumber" number-format>
with the code:
var app = angular.module("your.directives", ['your.filters']);
app.directive("numberFormat", [
'$filter', function(filter) {
return {
replace: false,
restrict: "A",
require: "?ngModel",
link: function(scope, element, attrs, ngModel) {
var numberFormat;
if (!ngModel) {
return;
}
ngModel.$render = function() {
return element.val(ngModel.$viewValue);
};
var numberFilter = filter('myNumberFilter');
return ngModel.$formatters.push(function(value) {
return numberFilter(value);
});
}
};
}
]);
What you need for this, is a working myNumberFilter filter present, which could decide based on the language given (however you determine this) how to format the input value.
EDIT: I noticed, that AngularJS has it's own number filter, so i changed the name called. Let's add our own:
var app = angular.module("your.filters", []);
app.filter("myNumberFilter", ['SiteLanguage',
function(siteLanguage) {
return function(number) {
var language = siteLanguage.getLanguage(); //I assume you have a service that can tell you the current language
switch(language) {
case "de":
// return the variant of number for "de" here
break;
case "fr":
// return the variant of number for "fr" here
break;
default:
// return english variant here.
break;
};
}
]
});
You'll probably need a formatter function for the individual countries (instead of blindly relying on angular locale. And you probably need a service SiteLanguage which will give you the language on the current site.
Hope this helps :)