이 시나리오에서는 다음을 사용하여보기에 학생 목록 (배열)을 표시합니다 ngFor
.
<li *ngFor="#student of students">{{student.name}}</li>
다른 학생을 목록에 추가 할 때마다 업데이트되는 것이 멋집니다.
그러나, 나는 그것을 줄 때 pipe
까지 filter
학생의 이름으로,
<li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>
필터링 학생 이름 필드에 무언가를 입력 할 때까지 목록을 업데이트하지 않습니다.
Hello_world.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.ts
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;
}
}
답변
문제와 가능한 해결책을 완전히 이해하려면 파이프 및 구성 요소에 대한 각도 변경 감지에 대해 논의해야합니다.
파이프 변경 감지
무 상태 / 순수 파이프
기본적으로 파이프는 상태 비 저장 / 순수입니다. Stateless / Pure 파이프는 단순히 입력 데이터를 출력 데이터로 변환합니다. 그들은 아무것도 기억하지 못하기 때문에 어떤 속성도 가지고 있지 않습니다 transform()
. 단지 방법 일뿐 입니다. 따라서 Angular는 상태 비 저장 / 순수 파이프의 처리를 최적화 할 수 있습니다. 입력이 변경되지 않으면 변경 감지주기 동안 파이프를 실행할 필요가 없습니다. {{power | exponentialStrength: factor}}
, power
와 같은 파이프의 factor
경우 입력입니다.
이 질문에 대해 "#student of students | sortByName:queryElem.value"
, students
그리고 queryElem.value
입력하고, 파이프 sortByName
무 / 순수하다. students
배열 (참조)입니다.
- 학생이 추가되면 배열 참조 가 변경
students
되지 않고 변경되지 않습니다. 따라서 상태 비 저장 / 순수 파이프가 실행되지 않습니다. - 필터 입력에 무언가를 입력하면
queryElem.value
변경되므로 stateless / pure 파이프가 실행됩니다.
배열 문제를 해결하는 한 가지 방법은 학생이 추가 될 때마다 배열 참조를 변경하는 것입니다. 즉, 학생이 추가 될 때마다 새 배열을 만듭니다. 다음과 concat()
같이 할 수 있습니다 .
this.students = this.students.concat([{name: studentName}]);
이것이 효과가 있지만, 우리 addNewStudent()
가 파이프를 사용하고 있기 때문에 우리의 방법을 특정 방식으로 구현할 필요는 없습니다. push()
배열에 추가하는 데 사용하고 싶습니다 .
상태 저장 파이프
상태 저장 파이프에는 상태가 있습니다. 일반적으로 transform()
메서드 뿐만 아니라 속성이 있습니다 . 입력이 변경되지 않은 경우에도 평가해야 할 수 있습니다. 파이프가 상태 저장 / 비 순수라고 지정하면 pure: false
-Angular의 변경 감지 시스템이 구성 요소의 변경 사항을 확인하고 해당 구성 요소가 상태 저장 파이프를 사용할 때마다 입력이 변경되었는지 여부에 관계없이 파이프의 출력을 확인합니다.
students
참조가 변경되지 않은 경우에도 파이프가 실행되기를 원하기 때문에 효율성이 떨어지더라도 원하는 것처럼 들립니다 . 파이프를 상태 저장 만하면 오류가 발생합니다.
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의 답변 에 따르면 “이 오류는 개발 모드에서만 발생합니다 (베타 0부터 기본적으로 활성화 됨). enableProdMode()
앱을 부트 스트랩 할 때 호출 하면 오류가 발생하지 않습니다.” 에 대한 문서ApplicationRef.tick()
상태 :
개발 모드에서 tick ()은 추가 변경이 감지되지 않도록 두 번째 변경 감지주기를 수행합니다. 이 두 번째주기 중에 추가 변경이 선택되면 앱의 바인딩에는 단일 변경 감지 단계에서 해결할 수없는 부작용이 있습니다. 이 경우 Angular 응용 프로그램은 모든 변경 감지를 완료해야하는 하나의 변경 감지 패스 만 가질 수 있으므로 Angular에서 오류가 발생합니다.
우리의 시나리오에서는 오류가 가짜 / 오해의 소지가 있다고 생각합니다. 상태 저장 파이프가 있으며 호출 될 때마다 출력이 변경 될 수 있습니다. 부작용이있을 수 있으며 괜찮습니다. NgFor는 파이프 후에 평가되므로 잘 작동합니다.
그러나이 오류가 발생하여 개발할 수는 없으므로 한 가지 해결 방법은 파이프 구현에 배열 속성 (즉, 상태)을 추가하고 항상 해당 배열을 반환하는 것입니다. 이 솔루션에 대한 @pixelbits의 답변을 참조하십시오.
그러나 우리는 더 효율적일 수 있으며 앞으로 보게 될 파이프 구현에 배열 속성이 필요하지 않으며 이중 변경 감지에 대한 해결 방법이 필요하지 않습니다.
구성 요소 변경 감지
기본적으로 모든 브라우저 이벤트에서 Angular 변경 감지는 모든 구성 요소를 통해 변경되었는지 확인합니다. 입력 및 템플릿 (및 기타 항목)이 확인됩니다.
구성 요소가 입력 속성 (및 템플릿 이벤트)에만 의존하고 입력 속성이 변경 불가능하다는 것을 알고 있다면 훨씬 더 효율적인 onPush
변경 감지 전략을 사용할 수 있습니다 . 이 전략을 사용하면 모든 브라우저 이벤트를 확인하는 대신 입력이 변경되고 템플릿 이벤트가 트리거 될 때만 구성 요소가 확인됩니다. 그리고 분명히이 Expression ... has changed after it was checked
설정 에서는 오류가 발생 하지 않습니다 . 이는 onPush
구성 요소가 다시 “표시”( ChangeDetectorRef.markForCheck()
) 될 때까지 다시 확인되지 않기 때문 입니다. 따라서 템플릿 바인딩 및 상태 저장 파이프 출력은 한 번만 실행 / 평가됩니다. Stateless / Pure 파이프는 입력이 변경되지 않는 한 여전히 실행되지 않습니다. 따라서 여기에는 여전히 상태 저장 파이프가 필요합니다.
@EricMartinez가 제안한 솔루션은 onPush
변경 감지 기능이있는 상태 저장 파이프입니다 . 이 솔루션에 대한 @caffinatedmonkey의 답변을 참조하십시오.
이 솔루션을 사용하면 transform()
메서드가 매번 동일한 배열을 반환 할 필요가 없습니다. 나는 그것이 조금 이상하다는 것을 알았다 : 상태가없는 상태 저장 파이프. 좀 더 생각해 보면 stateful 파이프는 항상 같은 배열을 반환해야합니다. 그렇지 않으면 onPush
개발 모드의 구성 요소 에서만 사용할 수 있습니다 .
그래서 결국 나는 @Eric과 @pixelbits의 대답의 조합을 좋아한다고 생각합니다. 동일한 배열 참조를 반환하는 상태 저장 파이프 onPush
, 구성 요소가 허용하는 경우 변경 감지. 상태 저장 파이프는 동일한 배열 참조를 반환하므로 .NET Framework로 구성되지 않은 구성 요소와 함께 파이프를 계속 사용할 수 있습니다 onPush
.
이것은 아마도 Angular 2 관용구가 될 것입니다. 배열이 파이프를 공급하고 있고 배열이 변경 될 수있는 경우 (배열 참조가 아닌 배열의 항목) 상태 저장 파이프를 사용해야합니다.
답변
에릭 마르티네스는 추가 의견에서 지적 pure: false
당신을 Pipe
장식하고 changeDetection: ChangeDetectionStrategy.OnPush
당신에게 Component
당신의 문제를 해결합니다 장식. 다음은 작동하는 플 런커입니다. 로 변경해 ChangeDetectionStrategy.Always
도 작동합니다. 그 이유는 다음과 같습니다.
파이프는 기본적으로 상태 비 저장입니다. 데코레이터 의
pure
속성을 로 설정하여 파이프를 상태 저장으로 선언해야합니다 . 이 설정은 Angular의 변경 감지 시스템이 입력이 변경되었는지 여부에 관계없이 매 사이클마다이 파이프의 출력을 확인하도록 지시합니다.@Pipe
false
의 ChangeDetectionStrategy
경우 기본적으로 모든 바인딩이 매주기마다 확인됩니다. 때 pure: false
파이프가 추가되어, 나는 변화 검출 방법은에서로 변경 생각 CheckAlways
에 CheckOnce
성능상의 이유로. 를 사용하면 OnPush
입력 속성이 변경되거나 이벤트가 트리거 될 때만 구성 요소에 대한 바인딩이 확인됩니다. 의 중요한 부분 인 변경 감지기에 대한 자세한 내용은 angular2
다음 링크를 확인하세요.
답변
ChangeDetectionStrategy를 변경할 필요가 없습니다. Stateful Pipe를 구현하면 모든 것이 작동 할 수 있습니다.
이것은 상태 저장 파이프입니다 (다른 변경 사항 없음).
@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는 파이프 실행과 뷰 업데이트를 모두 건너 뛸 수 있는지 여부를 신속하게 결정할 수 있습니다.
이러한 이유로 변경 감지 전략을 사용할 수있는 경우 순수 파이프가 선호됩니다. 할 수 없을 때는 불순한 파이프를 사용할 수 있습니다.
답변
pure : false 대신. this.students = Object.assign ([], NEW_ARRAY);로 구성 요소의 값을 전체 복사하고 바꿀 수 있습니다. 여기서 NEW_ARRAY는 수정 된 배열입니다.
각도 6에서 작동하며 다른 각도 버전에서도 작동합니다.
답변
해결 방법 : 생성자에서 파이프를 수동으로 가져오고이 파이프를 사용하여 변환 메서드를 호출합니다.
constructor(
private searchFilter : TableFilterPipe) { }
onChange() {
this.data = this.searchFilter.transform(this.sourceData, this.searchText)}
사실 넌 파이프도 필요 없어
답변
파이프 추가 매개 변수에 추가하고 배열 변경 직후에 변경하십시오. 순수 파이프로도 목록이 새로 고쳐집니다.
항목의하자 | 파이프 : 매개 변수