Using angularjs filter in input element

Angularjsasp.net Web-ApiAngularjs Directive

Angularjs 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionleon.ioView Question on Stackoverflow
Solution 1 - Angularjsholographic-principleView Answer on Stackoverflow
Solution 2 - AngularjsStewieView Answer on Stackoverflow
Solution 3 - AngularjsAtaisView Answer on Stackoverflow
Solution 4 - AngularjsValentin VView Answer on Stackoverflow
Solution 5 - Angularjspistol-peteView Answer on Stackoverflow
Solution 6 - AngularjsodiseoView Answer on Stackoverflow