Таблица матов Angular Material не возвращает никаких результатов при фильтрации

Я попытался воссоздать мат-таблицу с помощью пакета Angular Material. Примеры циновок и столов недавно были значительно улучшены, и это потрясающе. Они предоставили этот пример здесь с функциями сортировки и фильтрации.

Я попытался воспроизвести это в контексте моего собственного приложения, используя фиктивные данные в минимальном примере на stackblitz здесь .

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

Одно различие, о котором я могу думать здесь, заключается в том, что в моем примере я использую класс в своем объявлении dataSource: dataSource: MatTableDataSource<Match>;, тогда как в примере, предоставленном людьми Angular, это интерфейс: dataSource: MatTableDataSource<UserData>;.

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


person Atticus29    schedule 28.08.2019    source источник


Ответы (4)


В официальном примере Angular stackblitz dataSource имеет массив объектов глубиной 1 уровень, как видно из console.log(this.dataSource.filteredData):

введите описание изображения здесь

Однако ваш dataSource дважды вставляет объект другого уровня в объект Match (т.е. объекты MatchDetails и MoveInVideo вложены внутри объекта Match):

введите описание изображения здесь

Таким образом, предоставленная функция фильтра не будет работать для вас, потому что она проверяет только первый уровень объекта (совпадение) со значением фильтра. Фактически, он работает правильно, возвращая [], потому что ни одно из значений в ваших объектах Match не соответствует значению фильтра. Если вы выполните поиск по запросу «originalFilterPosterId», он вернет все объекты, потому что все объекты Match содержат это значение.

Вы можете исправить это, установив общий filterPredicate в ngOnInit () перед вызовом обработчика событий applyFilter(). Эта универсальная функция сможет фильтровать вложенные объекты. Все благодарности @Sagar Kharche и @mighty_mite источнику ответов Stackover.

ngOnInit(){
  this.dataSource.paginator = this.paginator;
  this.dataSource.sort = this.sort;

  //set a new filterPredicate function
  this.dataSource.filterPredicate = (data, filter: string) => {
    const accumulator = (currentTerm, key) => {
      return this.nestedFilterCheck(currentTerm, data, key);
    };
    const dataStr = Object.keys(data).reduce(accumulator, '').toLowerCase();
    // Transform the filter by converting it to lowercase and removing whitespace.
    const transformedFilter = filter.trim().toLowerCase();
    return dataStr.indexOf(transformedFilter) !== -1;
  }
}

//also add this nestedFilterCheck class function
nestedFilterCheck(search, data, key) {
  if (typeof data[key] === 'object') {
    for (const k in data[key]) {
      if (data[key][k] !== null) {
        search = this.nestedFilterCheck(search, data[key], k);
      }
    }
  } else {
    search += data[key];
  }
  return search;
}

Вам не нужно ничего менять в вашей applyFilter() функции.

Пример разветвленного Stackblitz ⚡

person terahertz    schedule 08.09.2019
comment
Большое спасибо! Кстати, есть идеи, почему ни сортировка, ни пагинатор тоже не работают? - person Atticus29; 09.09.2019
comment
Сортировка не работает по той же причине, что и фильтр, вложенные объекты. Вы можете легко исправить это, переопределив dataSource.sortingDataAccessor, см. Этот вопрос о переполнении стека. Я не уверен, почему ваш пагинатор не работает ..? - person terahertz; 09.09.2019
comment
Поскольку вы показали только небольшую часть своего приложения, я не уверен, действительно ли вам нужен массив вложенных объектов для хранения в вашем приложении. Нет смысла имитировать ответ API. Решение @nash тоже работает, но я бы добавил к его решению, сказав, что вы можете создать другой класс модели, то есть класс ViewModel с глубиной 1 уровня, специально для этой таблицы. Поэтому в будущем, если вы хотите добавить больше столбцов для отображения из других объектов (например, MoveInVideo), вы можете просто добавить другое свойство к этому классу ViewModel, сопоставить значение вашего ответа api и отобразить массив из. - person terahertz; 09.09.2019
comment
Спасибо за это. Спас меня в критический момент. - person GauthamK; 25.04.2020

Обычно фильтр работает, сериализуя данные первого уровня объекта в строку, а затем выполняет быструю проверку на включение строки. Это не работает для глубоких объектов. Таким образом, вы не можете полагаться на фильтрацию по умолчанию и должны предоставить filterPredicate.

В документах говорится

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

Пример фильтрации только по местоположению:

    this.dataSource.filterPredicate = (match: Match, filter: string) => {
      return match.matchDeets.location.toLowerCase().includes(filter);
    };
person Gökhan Kurt    schedule 07.09.2019

Как упоминалось в других ответах, функция filter фильтрует только один уровень. Однако в вашем вопросе я не вижу причин для того, чтобы ваши данные были на один уровень глубже. В вашем шаблоне вы отображаете только элементы из matchDeets, так почему бы просто не использовать matchDeets в качестве dataSource. Это заставит вашу функцию фильтра снова работать.

В вашем компоненте просто измените dataSource на следующее.

dataSource: MatTableDataSource<MatchDetails>;

// Other code goes here

this.dataSource = new MatTableDataSource(matchArray.map(match => match.matchDeets));

Это изменение будет неизменным, поэтому matchArray по-прежнему будет содержать другие данные из вашего класса. Поскольку ваш шаблон, похоже, не заботится о них, мы просто передадим dataSouce то, что нам нужно, то есть matchDeets.

Теперь в вашем шаблоне вам просто нужно обновить отображаемые значения с {{entry.matchDeets.rank}} на {{entry.rank}}, с {{entry.matchDeets.weightClass}} на {{entry.weightClass}} и так далее и так далее.

Теперь ваша функция фильтра будет работать. Вот рабочий пример на StackBlitz.

person nash11    schedule 08.09.2019

Чтобы разрешить любой тип массива, MatTableDataSource должен выглядеть в модели следующим образом.

  dataSource = new MatTableDataSource<any>();

Чтобы очистить фильтр:

this.dataSource.filter="";

Чтобы увидеть, что будет отображаться:

this.dataSource.data

Чтобы начать процесс фильтрации с элемента ввода, подключите keydown и keyup

<input id='search' #search  matinput type='text' (keydown)="onKeyDown(search,$event)" (keyup)='onSearchChanged(search, $event)' />

//this catches the backspace
onKeyDown(searchInput, event) {
      if (event.key === "Backspace") {
         this.filter = this.dataSource.filter = searchInput.value;
         this.cdf.detectChanges();
      }
   }

//this changes the filter as they type in the input field
onSearchChanged(search, event) {
      this.filter = search.value;
      this.dataSource.filter = this.filter;
      this.cdf.detectChanges();
   }

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

person JWP    schedule 28.08.2019
comment
Спасибо, Джон! Пример, над которым я работал, и многие из этих функций у меня уже есть. Могу я попросить вас взглянуть на приведенный мной пример stackblitz? - person Atticus29; 29.08.2019