aplicar o filtro de formatação dinamicamente em um ng-repeat

Meu objective é aplicar um filtro de formatação definido como uma propriedade do object em loop.

Tomando essa matriz de objects:

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

O código do modelo que estou tentando escrever é o seguinte:

 
{{ row.value | row.formatter }}

E eu estou esperando ver esse resultado :

 test value with null formatter UPPERCASE TEXT Jan 1, 2014 

Mas talvez obviusly este código lança um erro:

 Unknown provider: row.formatterFilterProvider <- row.formatterFilter 

Não consigo imaginar como analisar o parâmetro “formatter” dentro do {{}}; Alguém pode me ajudar?

Veja o plunkr http://plnkr.co/edit/YnCR123dRQRqm3owQLcs?p=preview

O | é uma construção angular que encontra um filtro definido com esse nome e aplica-o ao valor à esquerda. O que eu acho que você precisa fazer é criar um filtro que use um nome de filtro como argumento e, em seguida, chame o filtro apropriado (violino) (adaptado do código do M59):

HTML:

 
{{ row.value | picker:row.formatter }}

Javascript:

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

Graças ao comentário do @ karlgold, aqui está uma versão que suporta argumentos. O primeiro exemplo usa o filtro add diretamente para adicionar números a um número existente e o segundo usa o filtro useFilter para selecionar o filtro add por string e passar argumentos para ele (fiddle) :

HTML:

 

2 + 3 + 5 = {{ 2 | add:3:5 }}

7 + 9 + 11 = {{ 7 | useFilter:'add':9:11 }}

Javascript:

 app.filter('useFilter', function($filter) { return function() { var filterName = [].splice.call(arguments, 1, 1)[0]; return $filter(filterName).apply(null, arguments); }; }); 

Eu gosto do conceito por trás dessas respostas, mas não acho que elas fornecem a solução mais flexível possível.

O que eu realmente queria fazer e tenho certeza de que alguns leitores sentirão o mesmo, é poder passar dinamicamente uma expressão de filtro, que então avaliaria e retornaria o resultado apropriado.

Assim, um único filtro personalizado poderia processar todos os itens a seguir:

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

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

{{name | picker:'salutation:"Hello"'}} {{name | picker:'salutation:"Hello"'}} // Aplicar outro filtro personalizado

Eu criei o seguinte código, que utiliza o serviço $interpolate em meu filtro personalizado. Veja o jsfiddle :

Javascript

 myApp.filter('picker', function($interpolate ){ return function(item,name){ var result = $interpolate('{{value | ' + arguments[1] + '}}'); return result({value:arguments[0]}); }; }); 

Uma maneira de fazer isso funcionar é usar uma function para a binding e fazer a filtragem dentro dessa function. Essa pode não ser a melhor abordagem: demonstração ao vivo (clique).

 
{{ foo(row.value, row.filter) }}

JavaScript:

 $scope.list = [ {"value": "uppercase text", "filter": "uppercase"} ]; $scope.foo = function(value, filter) { return $filter(filter)(value); }; 

Eu tive uma necessidade um pouco diferente e modifiquei a resposta acima um pouco (a solução $interpolate atinge o mesmo objective, mas ainda é limitada):

 angular.module("myApp").filter("meta", function($filter) { return function() { var filterName = [].splice.call(arguments, 1, 1)[0] || "filter"; var filter = filterName.split(":"); if (filter.length > 1) { filterName = filter[0]; for (var i = 1, k = filter.length; i < k; i++) { [].push.call(arguments, filter[i]); } } return $filter(filterName).apply(null, arguments); }; }); 

Uso:

 {{ column.fakeData | meta:column.filter }} 

Dados:

  { label:"Column head", description:"The label used for a column", filter:"percentage:2:true", fakeData:-4.769796600014472 } 

( percentage é um filtro personalizado que cria um number )

Crédito neste post para Jason Goemaat.

Aqui está como eu usei.

 $scope.table.columns = [{ name: "June 1 2015", filter: "date" }, { name: "Name", filter: null }, ] etc...  {{ column.name | applyFilter:column.filter }}  app.filter('applyFilter', [ '$filter', function( $filter ) { return function ( value, filterName ) { if( !filterName ){ return value; } // In case no filter, as in NULL. return $filter( filterName )( value ); }; }]); 

Eu melhorei a resposta do @Jason Goemaat adicionando uma verificação se o filtro existe, e se não retornar o primeiro argumento por padrão:

 .filter('useFilter', function ($filter, $injector) { return function () { var filterName = [].splice.call(arguments, 1, 1)[0]; return $injector.has(filterName + 'Filter') ? $filter(filterName).apply(null, arguments) : arguments[0]; }; }); 

A versão mais nova do ng-table permite a criação de tabelas dinâmicas (ng-dynamic-table) com base em uma configuração de coluna. Formatar um campo de data é tão fácil quanto adicionar o formato ao valor do seu campo na matriz de colunas.

Dado

 { "name": "Test code", "dateInfo": { "createDate": 1453480399313 "updateDate": 1453480399313 } } columns = [ {field: 'object.name', title: 'Name', sortable: 'name', filter: {name: 'text'}, show: true}, {field: "object.dateInfo.createDate | date :'MMM dd yyyy - HH:mm:ss a'", title: 'Create Date', sortable: 'object.dateInfo.createDate', show: true} ] 
{{ $eval(column.field, { object: row }) }}

Acabei fazendo algo um pouco mais grosseiro, mas envolvendo menos:

HTML:

Use o operador ternário para verificar se existe um filtro definido para a linha:

 ng-bind="::data {{row.filter ? '|' + row.filter : ''}}" 

JS:

Na matriz de dados em JavaScript, adicione o filtro:

 , { data: 10, rowName: "Price", months: [], tooltip: "Price in DKK", filter: "currency:undefined:0" }, { 

Isso é o que eu uso (versão Angular 1.3.0-beta.8 acidental-haiku).

Este filtro permite que você use filtros com ou sem opções de filtro.

applyFilter irá verificar se o filtro existe em Angular, se o filtro não existir, então uma mensagem de erro com o nome do filtro estará no console do navegador assim …

O seguinte filtro não existe: greenBananas

Ao usar ng-repeat , alguns dos valores serão indefinidos. O applyFilter lidará com esses problemas com uma falha suave.

 app.filter( 'applyFilter', ['$filter', '$injector', function($filter, $injector){ var filterError = "The following filter does not exist: "; return function(value, filterName, options){ if(noFilterProvided(filterName)){ return value; } if(filterDoesNotExistInAngular(filterName)){ console.error(filterError + "\"" + filterName + "\""); return value; } return $filter(filterName)(value, applyOptions(options)); }; function noFilterProvided(filterName){ return !filterName || typeof filterName !== "string" || !filterName.trim(); } function filterDoesNotExistInAngular(filterName){ return !$injector.has(filterName + "Filter"); } function applyOptions(options){ if(!options){ return undefined; } return options; } }]); 

Então você usa o filtro que quiser, que pode ou não ter opções.

 // Where, item => { name: "Jello", filter: {name: "capitalize", options: null }}; 
{{ item.name | applyFilter:item.filter.name:item.filter.options }}

Ou você poderia usar com estruturas de dados separadas ao criar uma tabela.

 // Where row => { color: "blue" }; // column => { name: "color", filter: { name: "capitalize", options: "whatever filter accepts"}};   {{ row[column.name] | applyFilter:column.filter.name:column.filter.options }}   

Se você achar que precisa passar em valores mais específicos, você pode adicionar mais argumentos como este …

 // In applyFilter, replace this line return function(value, filterName, options){ // with this line return function(value, filterName, options, newData){ // and also replace this line return $filter(filterName)(value, applyOptions(options)); // with this line return $filter(filterName)(value, applyOptions(options), newData); 

Em seguida, no seu HTML, talvez o seu filtro também exija uma chave do object de linha

 // Where row => { color: "blue", addThisToo: "My Favorite Color" }; // column => { name: "color", filter: { name: "capitalize", options: "whatever filter accepts"}};   {{ row[column.name] | applyFilter:column.filter.name:column.filter.options:row.addThisToo }}