Подтвердить что ты не робот

Применять фильтр форматирования динамически в ng-repeat

Моя цель - применить фильтр форматирования, который задан как свойство зацикленного объекта.

Взятие этого массива объектов:

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

Код шаблона, который я пытаюсь написать, таков:

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

И я ожидаю увидеть этот результат:

test value with null formatter
UPPERCASE TEXT
Jan 1, 2014

Но, возможно, obviusly этот код вызывает ошибку:

Unknown provider: row.formatterFilterProvider <- row.formatterFilter

Я не могу вообразить, как анализировать параметр "форматировать" внутри {{}}; может кто-нибудь мне помочь?

См. plunkr http://plnkr.co/edit/YnCR123dRQRqm3owQLcs?p=preview

4b9b3361

Ответ 1

| представляет собой конструкцию angular, которая находит определенный фильтр с этим именем и применяет его к значению слева. Я думаю, вам нужно создать фильтр, который принимает имя фильтра в качестве аргумента, затем вызывает соответствующий фильтр (скрипка) (адаптирован из кода M59):

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);
  };
});

Благодаря комментарию @karlgold здесь представлена ​​версия, поддерживающая аргументы. В первом примере фильтр add используется непосредственно для добавления чисел к существующему номеру, а второй использует фильтр useFilter, чтобы выбрать фильтр add по строке и передать ему аргументы (скрипка):

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);
    };
});

Ответ 2

Мне нравится концепция этих ответов, но не думаю, что они предоставляют наиболее гибкое возможное решение.

Что я действительно хотел сделать, и я уверен, что некоторые читатели будут чувствовать то же самое, это возможность динамически передавать выражение фильтра, которое затем оценивает и возвращает соответствующий результат.

Таким образом, один пользовательский фильтр сможет обрабатывать все из следующих действий:

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

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

{{name | picker:'salutation:"Hello"'}}//Применить другой настраиваемый фильтр

Я придумал следующий фрагмент кода, который использует службу $interpolate в моем настраиваемом фильтре. См. jsfiddle:

Javascript

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

Ответ 3

Один из способов заставить его работать - использовать функцию привязки и выполнять фильтрацию внутри этой функции. Это может быть не лучший подход: Живая демонстрация (нажмите).

<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);
};

Ответ 4

У меня была немного другая потребность и так немного изменил приведенный выше ответ (решение $interpolate попадает в ту же цель, но все еще ограничено):

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);
    };
});

Использование:

<td ng-repeat="column in columns">{{ column.fakeData | meta:column.filter }}</td>

Данные:

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

(percentage - настраиваемый фильтр, который строит number)

Ответ 5

Кредит в этом сообщении Джейсону Гоемату.

Вот как я его использовал.

$scope.table.columns = [{ name: "June 1 2015", filter: "date" },
                        { name: "Name", filter: null },
                       ] etc...

<td class="table-row" ng-repeat="column in table.columns">
  {{ column.name | applyFilter:column.filter }}
</td>

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 );
  };
}]);

Ответ 6

Я улучшил @Jason Goemaat немного, добавив проверку, если фильтр существует, и если не вернуть первый аргумент по умолчанию:

.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];
    };
});

Ответ 7

Более новая версия ng-table позволяет создавать динамические таблицы (ng-dynamic-table) на основе конфигурации столбца. Форматирование поля даты так же просто, как добавление формата к вашему значению поля в массиве столбцов.

Учитывая

{
    "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}
]

<table ng-table-dynamic="controller.ngTableObject with controller.columns" show-filter="true" class="table table-condensed table-bordered table-striped">
    <tr ng-repeat="row in $data">
        <td ng-repeat="column in $columns">{{ $eval(column.field, { object: row }) }}</td>
    </tr>
</table>

Ответ 8

Я закончил тем, что делал что-то более грубое, но меньше включал:

HTML:

Используйте тернарный оператор, чтобы проверить, существует ли фильтр для строки:

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

JS:

В массиве данных в Javascript добавьте фильтр:

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

Ответ 9

Это то, что я использую (Angular версия 1.3.0-beta.8 случайно-хайку).

Этот фильтр позволяет использовать фильтры с параметрами фильтра или без него.

applyFilter проверяет, существует ли фильтр в Angular, если фильтр не существует, тогда сообщение об ошибке с именем фильтра будет в консоли браузера, например...

Следующий фильтр не существует: greenBananas

При использовании ng-repeat некоторые из значений будут undefined. applyFilter будет обрабатывать эти проблемы с мягким сбоем.

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;
  }
}]);

Затем вы используете какой-либо фильтр, который может иметь или не иметь параметров.

// Where, item => { name: "Jello", filter: {name: "capitalize", options: null }}; 
<div ng-repeat="item in items">
  {{ item.name | applyFilter:item.filter.name:item.filter.options }}
</div>

Или вы можете использовать с отдельными структурами данных при построении таблицы.

// Where row => { color: "blue" };
// column => { name: "color", filter: { name: "capitalize", options: "whatever filter accepts"}};
<tr ng-repeat="row in rows">
  <td ng-repeat="column in columns">
    {{ row[column.name] | applyFilter:column.filter.name:column.filter.options }}
  </td>
</tr>

Если вы обнаружите, что вам требуется передать более конкретные значения, вы можете добавить больше таких аргументов...

// 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);

Затем в вашем HTML, возможно, ваш фильтр также требует ключа от объекта row

// Where row => { color: "blue", addThisToo: "My Favorite Color" };
// column => { name: "color", filter: { name: "capitalize", options: "whatever filter accepts"}};
<tr ng-repeat="row in rows">
  <td ng-repeat="column in columns">
    {{ row[column.name] | applyFilter:column.filter.name:column.filter.options:row.addThisToo }}
  </td>
</tr>