programing

NgFor가 Angular2의 파이프로 데이터를 업데이트하지 않습니다.

muds 2023. 6. 15. 22:08
반응형

NgFor가 Angular2의 파이프로 데이터를 업데이트하지 않습니다.

배열)을이시나어을같다표다시이니합음이목록과생학는서에오리레▁with▁view▁in▁(▁iing)▁of다▁a,▁list▁the▁students어을▁thism▁scenario▁display'▁to니array표시이)과 함께 표시합니다.ngFor:

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

리스트에 다른 학생을 추가할 때마다 업데이트되는 것이 멋집니다.

그것줄내을때가줄때▁however▁it을,것그▁when▁a만.pipefilter은 생학이로으름,로으,

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

필터링 학생 이름 필드에 입력할 때까지 목록이 업데이트되지 않습니다.

여기에 plnkr 링크가 있습니다.

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

문제와 가능한 해결책을 완전히 이해하려면 파이프 및 구성 요소에 대한 각도 변경 감지에 대해 논의해야 합니다.

파이프 변경 감지

상태 비저장/순수 파이프

기본적으로 파이프는 상태 비저장/순수입니다.상태 비저장/순수 파이프는 단순히 입력 데이터를 출력 데이터로 변환합니다.은 아무것도에 어떤 있지 – a 은들아무기못때어문하떤속에가있않지지단다습니지고성.transform()例법.는 상태 비저장 파이프의할 수 . 변경되지 감지 가 없습니다.따라서 Angular는 상태 비저장/순수 파이프의 처리를 최적화할 수 있습니다. 입력이 변경되지 않으면 변경 감지 사이클 중에 파이프를 실행할 필요가 없습니다.다음과 같은 파이프의 경우{{power | exponentialStrength: factor}},power그리고.factor입력입니다.

문제는, 질에대는서해문이,,"#student of students | sortByName:queryElem.value",students그리고.queryElem.value입력이며, 입 파 프 입 니 이 다 및 력 니다입▁are▁inputs프입니다.sortByName상태 비저장/순수입니다. students배열(참조)입니다.

  • 학생을 추가해도 어레이 참조는 변경되지 않습니다.students변경되지 않으므로 상태 비저장/순수 파이프가 실행되지 않습니다.
  • 입력에 되면, 필입력무입력면되가언가,queryElem.value변경되므로 상태 비저장/순수 파이프가 실행됩니다.

배열 문제를 해결하는 한 가지 방법은 학생이 추가될 때마다 배열 참조를 변경하는 것입니다. 즉, 학생이 추가될 때마다 새 배열을 만드는 것입니다.우리는 이것을 할 수 있습니다.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()앱을 부트스트랩할 때 오류가 발생하지 않습니다."상태에 대한 문서:

개발 모드에서는 tick()이 두 번째 변경 감지 사이클을 수행하여 추가 변경 사항이 감지되지 않도록 합니다.이 두 번째 주기 동안 추가적인 변경 사항이 포착되면 앱의 바인딩은 한 번의 변경 사항 감지 패스로 해결할 수 없는 부작용이 발생합니다.이 경우 Angular 응용 프로그램은 모든 변경 감지를 완료해야 하는 동안 하나의 변경 감지 패스만 가질 수 있으므로 Angular는 오류를 발생시킵니다.

우리의 시나리오에서 나는 그 오류가 가짜라고 생각합니다/잘못이라고 생각합니다.우리는 상태 저장 파이프를 가지고 있으며, 출력은 호출될 때마다 바뀔 수 있습니다. 부작용이 있을 수 있고 괜찮습니다.NgFor는 파이프 이후에 평가되므로 잘 작동할 것입니다.

그러나 이 오류가 발생하면 실제로 개발할 수 없으므로 한 가지 해결 방법은 파이프 구현에 어레이 속성(즉, 상태)을 추가하고 항상 해당 어레이를 반환하는 것입니다.이 솔루션은 @pixelbits의 답변을 참조하십시오.

그러나 우리는 더 효율적일 수 있으며, 앞으로 보게 될 것처럼 파이프 구현에 어레이 속성이 필요하지 않을 것이며 이중 변경 감지를 위한 해결책도 필요하지 않을 것입니다.

구성 요소 변경 탐지

기본적으로 모든 브라우저 이벤트에서 각도 변경 감지는 모든 구성 요소를 검사하여 변경되었는지 여부를 확인합니다. 입력 및 템플릿(및 기타 항목)이 확인됩니다.

템플릿 이벤트)에만 더 인 구및입속에성력만의요소존이성입(트템릿벤플)가▁the▁if),▁use▁much▁we에▁can▁more및을 사용할 수 있습니다.onPush변경 감지 전략.이 전략을 사용하면 모든 브라우저 이벤트를 확인하는 대신 입력이 변경되고 템플릿 이벤트가 트리거될 때만 구성 요소를 확인합니다.그리고, 분명히, 우리는 그것을 이해하지 못합니다.Expression ... has changed after it was checked오류가 발생했습니다.은 이그는유 때문입니다.onPush", "Marked", "Marked", "Marked", "Marked", "Marked", "Marked", "Marked", "Marked", "Marked", "Marked", "Marked", "ChangeDetectorRef.markForCheck() 은 한번만 됩니다.따라서 템플릿 바인딩과 상태 저장 파이프 출력은 한 번만 실행/평가됩니다.상태 비저장/순수 파이프는 입력이 변경되지 않는 한 여전히 실행되지 않습니다.그래서 우리는 여전히 여기에 상태가 좋은 파이프가 필요합니다.

@ Martinez가 해결책은: 파이프 with @Eric Martinez 안제결다은음: 상태저파이:onPush@monkey의 을 참조하십시오이 솔루션에 대한 @caffinedmonkey의 답변을 참조하십시오.

참고로 이 솔루션을 사용하면transform()메소드는 매번 동일한 배열을 반환할 필요가 없습니다.하지만 저는 그것이 약간 이상하다고 생각합니다: 상태가 없는 상태의 파이프.좀 더 생각해보니...상태 저장 파이프는 항상 동일한 배열을 반환해야 합니다.그렇지 않으면 다음 항목에서만 사용할 수 있습니다.onPush구성 요소가 개발 모드에 있습니다.


, 과 @합니다: 동일한 참조를 , @Eric, @pixelbits와 함께 @Eric의 @pixelbits와 함께.onPush구성 요소가 허용하는 경우 변경 탐지.참조를 상태저파배참반으로 할 수 .onPush.

Plunker

이것은 아마도 Angular 2 관용구가 될 것입니다. 배열이 파이프를 공급하고 있고 배열이 변경될 수 있다면(배열 참조가 아닌 배열의 항목), 상태 저장 파이프를 사용해야 합니다.

Eric Martinez가 댓글에서 지적했듯이, 다음과 같이 덧붙였습니다.pure: false의 신에게에.Pipe 겸 식가와장.changeDetection: ChangeDetectionStrategy.OnPush의 신에게에.Component장식가가 당신의 문제를 해결해 줄 것입니다.여기 작업 중인 플런커가 있습니다.로 변경ChangeDetectionStrategy.Always작동하기도 합니다.그 이유는 이렇습니다.

파이프의 각도2 가이드에 따라:

파이프는 기본적으로 상태 비저장입니다..pure@Pipe의 장식가.false이 설정은 Angular의 변경 감지 시스템에 입력이 변경되었는지 여부에 관계없이 각 사이클마다 이 파이프의 출력을 확인하도록 지시합니다.

에 대해서는 기본적으로 모든 바인딩이 매 주기마다 검사됩니다.의 경우pure: false되었습니다, 이 파프가추습에서 로 되는 것 . 변경 감지 방법이 에서 변경된 것 같습니다.CheckAlwaysCheckOnce수행상의 이유로와 함께OnPush구성 요소에 대한 바인딩은 입력 속성이 변경되거나 이벤트가 트리거될 때만 확인됩니다.감지기에 은 변감지에대자내세용다중음오부참요시십분의 하십시오.angular2다음 링크를 확인하십시오.

데모 플런크르

변경 탐지 전략을 변경할 필요는 없습니다.상태 저장 파이프를 구현하면 모든 것이 작동하기에 충분합니다.

상태 저장 파이프입니다(다른 변경 사항은 없습니다).

@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로 설정하여 파이프를 불순물로 만듭니다.플라잉 히어로즈 파이프를 다음과 같이 불순하게 만들 수 있습니다.

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

이 작업을 수행하기 전에 순수 파이프부터 시작하여 순수 파이프와 불순물의 차이를 이해해야 합니다.

Pure pipe Angular는 입력 값에 대한 순수 변경을 감지할 때만 순수 파이프를 실행합니다.순수 변경은 기본 입력 값(문자열, 숫자, 부울, 기호) 또는 변경된 개체 참조(날짜, 배열, 함수, 개체)에 대한 변경입니다.

각도는 (복합) 객체 내의 변경 사항을 무시합니다.입력 월을 변경하거나 입력 배열에 추가하거나 입력 개체 속성을 업데이트하는 경우 순수 파이프를 호출하지 않습니다.

이것은 제한적으로 보일 수 있지만 또한 빠릅니다.객체 참조 검사는 차이에 대한 심층 검사보다 훨씬 빠르기 때문에 Angular는 파이프 실행과 뷰 업데이트를 모두 건너뛸 수 있는지 여부를 신속하게 확인할 수 있습니다.

따라서 변경 탐지 전략을 사용할 수 있는 경우에는 순수 파이프가 좋습니다.당신이 할 수 없을 때, 당신은 불순한 파이프를 사용할 수 있습니다.

이제 일을 너무 복잡하게 만들 필요가 없습니다!

파이프를 불순물로 만들어도 Angular의 최신 버전에서는 개발 모드에서 오류가 발생하지 않습니다.현재 수락된 답변에 언급된 오류는 5.5년 전(이 질문이 게시된 직후)에 해결된 이 문제와 관련이 있다고 생각합니다.

Angular는 이제 템플릿에 나타나는 일반 배열과 동일하게 불순물 파이프에 의해 반환되는 배열의 변경을 감지하는 데 사용하는 것으로 알고 있습니다(기본 변경 감지 전략을 사용할 때). 따라서 내용이 변경되지 않았다면 배열 참조가 변경되어도 문제가 없다고 생각합니다.매번 새로운 배열을 생성하면 오류가 발생하지 않으므로 파이프를 불순물로 만들면 됩니다. 그러면 효과가 있습니다.

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'sortByName',
  pure: false
})
export class SortByNamePipe implements PipeTransform {
  transform(value, queryString) {
    return value.filter(student => new RegExp(queryString).test(student.name));
  }
}

순수: 거짓을 하는 대신에.구성 요소의 값을 .vmdk = Object.assign([, NEW_ARY)으로 딥 복사하고 바꿀 수 있습니다. 여기서 NEW_ARY는 수정된 어레이입니다.

각 6에 적용되며 다른 각 버전에도 적용됩니다.

해결 방법: 생성자에서 수동으로 파이프 가져오기 및 이 파이프를 사용한 호출 변환 방법

constructor(
private searchFilter : TableFilterPipe) { }

onChange() {
   this.data = this.searchFilter.transform(this.sourceData, this.searchText)}

사실 당신은 파이프도 필요 없습니다.

파이프 추가 매개변수에 추가하고 배열 변경 후 바로 변경하며 순수 파이프를 사용하더라도 목록이 새로 고쳐집니다.

임대 항목 | 파이프:param

이 사용 사례에서는 데이터 필터링에 파이프인츠 파일을 사용했습니다.순수 파이프를 사용하는 것보다 성능이 훨씬 좋습니다.다음과 같은 용도로 사용:

import { YourPipeComponentName } from 'YourPipeComponentPath';

class YourService {

  constructor(private pipe: YourPipeComponentName) {}

  YourFunction(value) {
    this.pipe.transform(value, 'pipeFilter');
  }
}

불순물 파이프를 만드는 것은 성능 면에서 비용이 많이 들기 때문에 불순물 파이프를 만드는 것이 아니라 데이터가 변경될 때 데이터 복사본을 만들고 원본 데이터 변수에 복사본 참조를 재할당하여 데이터 변수의 참조를 변경합니다.

            emp=[];
            empid:number;
            name:string;
            city:string;
            salary:number;
            gender:string;
            dob:string;
            experience:number;

            add(){
              const temp=[...this.emps];
              const e={empid:this.empid,name:this.name,gender:this.gender,city:this.city,salary:this.salary,dob:this.dob,experience:this.experience};
              temp.push(e); 
              this.emps =temp;
              //this.reset();
            } 

언급URL : https://stackoverflow.com/questions/34456430/ngfor-doesnt-update-data-with-pipe-in-angular2

반응형