Using angularjs filter in input element
Angularjsasp.net Web-ApiAngularjs DirectiveAngularjs Problem Overview
I hope I haven't missed anything obvious in the doco, if I have I'm sure someone will help.
I'm using asp.net webapi to return a DTO, with date fields. These are serialized using JSON.Net (in format '2013-03-11T12:37:38.693').
I'd like to use a filter but in an INPUT element, is this possible or should I create a new filter or directive to accomplish this?
// this just displays the text value
<input ui-datetime type="text" data-ng-model="entity.date" />
// this doesn't work at all
<input ui-datetime type="text" data-ng-model="{{entity.date|date:'dd/MM/yyyy HH:mm:ss a'}}" />
// this works fine
{{entity.date|date:'dd/MM/yyyy HH:mm:ss a'}}
Is there any shortcut I'm missing?
Angularjs Solutions
Solution 1 - Angularjs
In short: if you want your data to have a different representation in the view and in the model, you will need a directive, which you can think of as a two-way filter.
Your directive would look something like
angular.module('myApp').directive('myDirective', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelController) {
ngModelController.$parsers.push(function(data) {
//convert data from view format to model format
return data; //converted
});
ngModelController.$formatters.push(function(data) {
//convert data from model format to view format
return data; //converted
});
}
}
});
HTML:
<input my-directive type="text" data-ng-model="entity.date" />
Here is a working jsFiddle example.
Solution 2 - Angularjs
Having different values in your input field and in your model goes against the very nature of ng-model. So I suggest you take the simplest approach and apply your filter inside the controller, using a separate variable for formatted date, and employing watchers to keep formatted and original dates in sync:
HTML:
<input ui-datetime type="text" data-ng-model="formattedDate" />
JS:
app.controller('AppController', function($scope, $filter){
$scope.$watch('entity.date', function(unformattedDate){
$scope.formattedDate = $filter('date')(unformattedDate, 'dd/MM/yyyy HH:mm:ss a');
});
$scope.$watch('formattedDate', function(formattedDate){
$scope.entity.date = $filter('date')(formattedDate, 'yyy/MM/dd');
});
$scope.entity = {date: '2012/12/28'};
});
Solution 3 - Angularjs
If your input only displays data
If you actually need an input to simply display some information and it is some other element that changes the Angular model you can make an easier change.
Instead of writing new directive simply DO NOT USE the ng-model
and use good, old value
.
So instead of:
<input data-ng-model={{entity.date|date:'dd/MM/yyyy HH:mm:ss'}}" />
This will do:
<input value="{{entity.date|date:'dd/MM/yyyy HH:mm:ss'}}" />
And works like a charm :)
Solution 4 - Angularjs
Complete example that formats numbers, inserting spaces every 3 characters, starting from the end:
'use strict'
String::reverse = ->
@split('').reverse().join('')
app = angular.module('app', [])
app.directive 'intersperse', ->
require: 'ngModel'
link: (scope, element, attrs, modelCtrl) ->
modelCtrl.$formatters.push (input) ->
return unless input?
input = input.toString()
input.reverse().replace(/(.{3})/g, '$1 ').reverse()
modelCtrl.$parsers.push (input) ->
return unless input?
input.replace(/\s/g, '')
Usage:
<input ng-model="price" intersperse/>
Plunkr example: http://plnkr.co/edit/qo0h9z
Solution 5 - Angularjs
Angular has built in date format functionality, but to apply it to an input where you'll eventually like to get the raw (unformatted) date, you need to create a custom directive.
Example Directive:
(function () {
'use strict';
angular.module('myApp').directive('utcDate', ['$filter', function ($filter) {
return {
restrict: 'A', //restricting to (A)ttributes
require: 'ngModel',
link: function (scope, elem, attrs, model) {
if (!model) return;
var format = 'MM/dd/yyyy h:mm:ss a';
var timezone = 'UTC';
//format the date for display
model.$formatters.push(function (value) {
//using built-in date filter
return $filter('date')(value, format, timezone);
});
//remove formatting to get raw date value
model.$parsers.push(function (value) {
var date = Date.parse(value);
return !isNaN(date) ? new Date(date) : undefined;
});
}
};
}]);
})();
Then to apply it:
<input type="text" ng-model="$ctrl.DateField" utc-date />
Solution 6 - Angularjs
You wouldn't need to create a new filter from zero, since angular has already a built-in filter for date types. http://docs.angularjs.org/api/ng.filter:date
I believe that's exactly what you need.