Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

apply formatting filter dynamically in a ng-repeat

My goal is to apply a formatting filter that is set as a property of the looped object.

Taking this array of objects:

[
  {
    "value": "test value with null formatter",
    "formatter": null,
  },
  {
    "value": "uppercase text",
    "formatter": "uppercase",
  },
  {
    "value": "2014-01-01",
    "formatter": "date",
  }
]

The template code i'm trying to write is this:

<div ng-repeat="row in list">
    {{ row.value | row.formatter }}
</div>

And i'm expecting to see this result:

test value with null formatter
UPPERCASE TEXT
Jan 1, 2014

But maybe obviusly this code throws an error:

Unknown provider: row.formatterFilterProvider <- row.formatterFilter

I can't immagine how to parse the "formatter" parameter inside the {{ }}; can anyone help me?

See the plunkr http://plnkr.co/edit/YnCR123dRQRqm3owQLcs?p=preview

like image 890
IcedBlind Avatar asked Jan 31 '14 23:01

IcedBlind


Video Answer


3 Answers

The | is an angular construct that finds a defined filter with that name and applies it to the value on the left. What I think you need to do is create a filter that takes a filter name as an argument, then calls the appropriate filter (fiddle) (adapted from M59's code):

HTML:

<div ng-repeat="row in list">
    {{ row.value | picker:row.formatter }}
</div>

Javascript:

app.filter('picker', function($filter) {
  return function(value, filterName) {
    return $filter(filterName)(value);
  };
});

Thanks to @karlgold's comment, here's a version that supports arguments. The first example uses the add filter directly to add numbers to an existing number and the second uses the useFilter filter to select the add filter by string and pass arguments to it (fiddle):

HTML:

<p>2 + 3 + 5 = {{ 2 | add:3:5 }}</p>
<p>7 + 9 + 11 = {{ 7 | useFilter:'add':9:11 }}</p>

Javascript:

app.filter('useFilter', function($filter) {
    return function() {
        var filterName = [].splice.call(arguments, 1, 1)[0];
        return $filter(filterName).apply(null, arguments);
    };
});
like image 129
Jason Goemaat Avatar answered Oct 19 '22 12:10

Jason Goemaat


I like the concept behind these answers, but don't think they provide the most flexible possible solution.

What I really wanted to do and I'm sure some readers will feel the same, is to be able to dynamically pass a filter expression, which would then evaluate and return the appropriate result.

So a single custom filter would be able to process all of the following:

{{ammount | picker:'currency:"$":0'}}

{{date | picker:'date:"yyyy-MM-dd HH:mm:ss Z"'}}

{{name | picker:'salutation:"Hello"'}} //Apply another custom filter

I came up with the following piece of code, which utilizes the $interpolate service into my custom filter. See the jsfiddle:

Javascript

myApp.filter('picker', function($interpolate ){
    return function(item,name){
       var result = $interpolate('{{value | ' + arguments[1] + '}}');
       return result({value:arguments[0]});
    };
});
like image 13
JackB Avatar answered Oct 19 '22 10:10

JackB


One way to make it work is to use a function for the binding and do the filtering within that function. This may not be the best approach: Live demo (click).

<div ng-repeat="row in list">
  {{ foo(row.value, row.filter) }}
</div>

JavaScript:

$scope.list = [
  {"value": "uppercase text", "filter": "uppercase"}
];
$scope.foo = function(value, filter) {
  return $filter(filter)(value);
};
like image 6
m59 Avatar answered Oct 19 '22 10:10

m59