NgFor не обновляет данные с Pipe in Angular2

в этом сценарии я показываю список студентов (массив) в представлении с ngFor:

<li *ngFor="#student of students">{{student.name}}</li>

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

однако, когда я даю ему pipe to filter на имя студента

<li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>

он не обновляет список, пока я не введу что-то в поле Имя фильтрационного студента.

вот ссылка на plnkr.

она.HTML-код

<h1>Students:</h1>
<label for="newStudentName"></label>
<input type="text" name="newStudentName" placeholder="newStudentName" #newStudentElem>
<button (click)="addNewStudent(newStudentElem.value)">Add New Student</button>
<br>
<input type="text" placeholder="Search" #queryElem (keyup)="0">
<ul>
    <li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>
</ul>

sort_by_name_pipe.ТС

import {Pipe} from 'angular2/core';

@Pipe({
    name: 'sortByName'
})
export class SortByNamePipe {

    transform(value, [queryString]) {
        // console.log(value, queryString);
        return value.filter((student) => new RegExp(queryString).test(student.name))
        // return value;
    }
}

4 ответов


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

Обнаружение Изменения Трубы

без гражданства / чистые трубы

по умолчанию, трубы без гражданства/чисто. Трубы без состояния / pure просто преобразуют входные данные в выходные данные. Они ничего не помнят, поэтому у них нет никаких свойств – просто transform() метод. Угловой может поэтому оптимизировать обработку апатридов / pure трубы: если их входные сигналы не изменяют, то трубам не нужно быть исполненным во время цикла обнаружения изменения. Для такой трубы, как {{power | exponentialStrength: factor}}, power и factor входы.

для этого вопроса,"#student of students | sortByName:queryElem.value", students и queryElem.value входы и трубы sortByName является лицом без гражданства/чисто. students массив (ссылка).

  • когда студент добавляется, массив ссылка не меняет – students не меняет – следовательно, чисто труба без гражданства, не выполненный.
  • когда что-то вводится во входной фильтр, queryElem.value изменяется, следовательно, выполняется канал без состояния / pure.

один из способов исправить проблему массива-изменить ссылку на массив каждый раз, когда добавляется студент, т. е. создать новый массив каждый раз, когда добавляется студент. Мы могли бы сделать это с помощью concat():

this.students = this.students.concat([{name: studentName}]);

хотя это работает, наши addNewStudent() метод не должен быть реализован определенным образом только потому, что мы используем труба. Мы хотим использовать push() чтобы добавить к нашему массиву.

Трубы Состояния

трубы с состоянием имеют состояние - они обычно имеют свойства, а не только transform() метод. Возможно, их необходимо оценить, даже если их входные данные не изменились. Когда мы указываем, что труба является stateful / non-pure -pure: false - тогда всякий раз, когда система обнаружения изменений Angular проверяет компонент на наличие изменений, и этот компонент использует канал с состоянием, он проверяет выход трубы, ввод изменился или нет.

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

EXCEPTION: Expression 'students | sortByName:queryElem.value  in HelloWorld@7:6' 
has changed after it was checked. Previous value: '[object Object],[object Object]'. 
Current value: '[object Object],[object Object]' in [students | sortByName:queryElem.value

по данным @drewmoore это, " эта ошибка происходит только в режиме dev (который включен по умолчанию с beta-0). Если вы позвоните enableProdMode() при загрузке приложения ошибка не будет выброшена." Этот документы на ApplicationRef.tick() состояние:

в режиме разработки tick () также выполняет второй цикл обнаружения изменений, чтобы убедиться, что никаких дальнейших изменений не обнаружено. Если во время этого второго цикла происходят дополнительные изменения, то привязки в приложении имеют побочные эффекты, которые не могут быть разрешены за один проход обнаружения изменений. В этом случае Angular выдает ошибку, так как приложение Angular может иметь только один проход обнаружения изменений, во время которого все обнаружение изменений должны заполнить.

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

однако мы не можем действительно развиваться с этой ошибкой, поэтому одним обходным путем является добавление свойства массива (т. е. состояния) в реализацию канала и всегда возвращать этот массив. См. @pixelbits по отвечать за это решение.

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

Обнаружение Изменения Компонентов

по умолчанию в каждом событии браузера обнаружение угловых изменений проходит через каждый компонент, чтобы увидеть, изменился ли он-входы и шаблоны (и, возможно, другие вещи?) проверяются.

если мы знаем, что компонент зависит только от его входных свойств (и событий шаблона), и что входные свойства неизменяемы, мы можем использовать гораздо более эффективный onPush изменить стратегию обнаружения. В этой стратегии вместо проверки каждого события браузера компонент проверяется только при изменении входных данных и при запуске событий шаблона. И, по-видимому, мы этого не понимаем Expression ... has changed after it was checked ошибка с этой настройкой. Это потому что onPush компонент не проверяется снова, пока он не будет " отмечен" (ChangeDetectorRef.markForCheck()) еще раз. Таким образом, привязки шаблонов и выходы канала с состоянием выполняются/оцениваются только один раз. Трубы без состояния / pure по-прежнему не выполняются, если их входы не изменяются. Так что нам все еще нужна труба с состоянием.

это решение @EricMartinez предложил: stateful трубы с onPush обнаружения изменений. См. ответ @caffinatedmonkey для этого решения.

обратите внимание, что с этим решением transform() метод не должен возвращать один и тот же массив каждый раз. Я найдите это немного странным, хотя: статичная труба без состояния. Подумав об этом еще немного... канал с состоянием, вероятно, всегда должен возвращать один и тот же массив. В противном случае он может использоваться только с onPush компоненты в режиме dev.


Итак, после всего этого, я думаю, мне нравится комбинация ответов @Eric и @pixelbits: stateful pipe, который возвращает ту же ссылку на массив, с onPush обнаружение изменений, если компонент позволяет. Поскольку канал stateful возвращает то же самое ссылка на массив, труба все еще может использоваться с компонентами, которые не настроены с onPush.

Plunker

это, вероятно, станет угловой идиомой 2: если массив подает канал, и массив может измениться (элементы в массиве, а не Ссылка на массив), нам нужно использовать канал с состоянием.


как отметил Эрик Мартинес в комментариях, добавив pure: false на Pipe декоратор и changeDetection: ChangeDetectionStrategy.OnPush на Component декоратор исправит вашу проблему. вот рабочая plunkr. изменение ChangeDetectionStrategy.Always, также работает. Вот почему.

согласно Angular2 руководство по трубам:

трубы по умолчанию не имеют состояния. Мы должны объявить трубу статичной, установив pure свойства @Pipe декоратор false. Этот параметр указывает системе обнаружения изменений Angular проверять выход этой трубы каждый цикл, независимо от того, изменился вход или нет.

что касается ChangeDetectionStrategy по умолчанию, все привязки проверил каждый цикл. Когда pure: false добавлена труба, я считаю, что метод обнаружения изменений изменяется на from CheckAlways to CheckOnce по соображениям производительности. С OnPush, привязки для компонента проверяются только при изменении входного свойства или когда запускается событие. Для получения дополнительной информации о детекторах изменений, важная часть angular2, проверьте следующие ссылки:


Демо Plunkr

вам не нужно изменять ChangeDetectionStrategy. Реализация канала с состоянием достаточно, чтобы все работало.

Это канал с состоянием (никаких других изменений не было сделано):

@Pipe({
  name: 'sortByName',
  pure: false
})
export class SortByNamePipe {
  tmp = [];
  transform (value, [queryString]) {
    this.tmp.length = 0;
    // console.log(value, queryString);
    var arr = value.filter((student)=>new RegExp(queryString).test(student.name));
    for (var i =0; i < arr.length; ++i) {
        this.tmp.push(arr[i]);
     }

    return this.tmp;
  }
}

С угловое документации

чистые и нечистые трубы

есть две категории труб: чистые и нечистые. Трубы по умолчанию чистые. Каждая труба, которую вы видели до сих пор, была чистой. Вы делаете трубу нечистой, устанавливая ее чистый флаг в false. Вы могли бы сделать FlyingHeroesPipe нечистым, как это:

@Pipe({ name: 'flyingHeroesImpure', pure: false })

прежде чем делать это, понять разницу между чистым и нечистым, начиная с чистого труба.

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

Angular игнорирует изменения внутри (составных) объектов. Он не вызовет чистый канал, если вы измените входной месяц, добавите к входному массиву или обновите свойство входного объекта.

Это может показаться ограничительный, но и быстрый. Проверка ссылки на объект выполняется быстро-намного быстрее, чем глубокая проверка различий,-поэтому Angular может быстро определить, может ли он пропустить как выполнение канала, так и обновление вида.

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