문제와 가능한 해결책을 완전히 이해하려면 파이프 및 구성 요소에 대한 각도 변경 감지에 대해 논의해야합니다.
파이프 변경 감지
무 상태 / 순수 파이프
기본적으로 파이프는 상태 비 저장 / 순수입니다. 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
.
Plunker
이것은 아마도 Angular 2 관용구가 될 것입니다. 배열이 파이프를 공급하고 있고 배열이 변경 될 수있는 경우 (배열 참조가 아닌 배열의 항목) 상태 저장 파이프를 사용해야합니다.
pure:false
파이프와changeDetection: ChangeDetectionStrategy.OnPush
컴포넌트에 추가하십시오 .