带有BehaviorSubject的Angular api服务不会实时刷新组件数据

时间:2019-06-15 16:40:24

标签: angular typescript rxjs

我创建了一个CRUD服务,该服务可以调用经典API。我使用行为主题在应用程序的所有组件之间共享数据。问题是,当我执行Crud操作时,并不是所有组件中的数据都实时更新。如果我在其他组件中导航,数据将更新。

我不明白为什么它不能实时运行,也找不到我的错误来源。

如果有人可以帮助我,那将非常好:)

我尝试调用get Candidates()函数,使用组件的每个ngOnInit()中的next()函数更新数据存储,但行为相同

github存储库:https://github.com/jbty/signalr-majority-vote/tree/master/signalR-majority-vote/ClientApp

api角度服务:

export class ApiCandidateService {
  public candidatesList: Observable<Candidate[]>;
  private baseUrl: string;
  private _candidatesList: BehaviorSubject<Candidate[]>;
  private dataStore: {
    candidatesList: Candidate[];
  };
  private httpOptions: { headers: HttpHeaders } = {
    headers: new HttpHeaders({
      "Content-Types": "applications/json"
    })
  };

  public constructor(private http: HttpClient) {
    this.baseUrl = "api/Candidates";
    this.dataStore = { candidatesList: [] };
    this._candidatesList = <BehaviorSubject<Candidate[]>>new BehaviorSubject([]);
    this.candidatesList = this._candidatesList.asObservable();
  }

  public getCandidates() {
    this.http.get<Candidate[]>(this.baseUrl, this.httpOptions).subscribe(
      (data: Candidate[]) => {
        this.dataStore.candidatesList = data;
        this._candidatesList.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public getCandidate(id: number | string) {
    this.http.get<Candidate>(`${this.baseUrl}/${id}`, this.httpOptions).subscribe(
      (data: Candidate)  => {
        let notFound = true;

        this.dataStore.candidatesList.forEach((item, index) => {
          if (item.candidateId == data.candidateId) {
            this.dataStore.candidatesList[index] = data;
            notFound = false;
          }
        });

        if (notFound) {
          this.dataStore.candidatesList.push(data);
        }

        this._candidatesList.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public createCandidate(newCandidate: Candidate) {
    this.http.post<Candidate>(`${this.baseUrl}`, newCandidate, this.httpOptions).subscribe(
      (data: Candidate) => {
        this.dataStore.candidatesList.push(data);
        this._candidatesList.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public updateCandidate(id: number | string, updatedCandidate: Candidate) {
    this.http.put<Candidate>(`${this.baseUrl}/${id}`, updatedCandidate, this.httpOptions).subscribe(
      () => {
        this.dataStore.candidatesList.forEach((candidate, i) => {
          if (candidate.candidateId == updatedCandidate.candidateId) {
            this.dataStore.candidatesList[i] = updatedCandidate;
          }
        });

        this._candidatesList.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public deleteCandidate(id: number | string) {
    this.http.delete<Candidate>(`${this.baseUrl}/${id}`, this.httpOptions).subscribe(
      response => {
        this.dataStore.candidatesList.forEach((candidate, i) => {
          if (candidate.candidateId == Number(id)) {
            this.dataStore.candidatesList.splice(i, 1);
          }
        });

        this._candidatesList.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error("An error occurred:", error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(`Backend returned code ${error.status}, ` + `body was: ${error.error}`);
    }
    // return an observable with a user-facing error message
    return throwError("Something bad happened; please try again later.");
  }
}

示例组件

export class DeleteCandidatesComponent implements OnInit {
  public candidate: Observable<Candidate>;
  public candidateId: string;

  constructor(
    public nav: NavigationService,
    private route: ActivatedRoute,
    private candidateService: ApiCandidateService
  ) {
    this.candidateId = this.route.snapshot.params.id;
  }

  public ngOnInit(): void {
    this.candidate = this.candidateService.candidatesList.pipe(
      map(candidate => candidate.find(item => item.candidateId == Number(this.candidateId)))
    );

    this.candidateService.getCandidate(this.candidateId);
  }

  public onDefinitivlyDeleteCandidate(id: string) {
    this.candidateService.deleteCandidate(id);
    this.nav.outletsNav('classement', 'manage/candidates')
  }
}

我的观点

<header>
  <h2>
    êtes vous sûr de vouloir supprimer définitvement
    <strong>{{ (candidate | async)?.firstName }} {{ (candidate | async)?.lastName }}</strong> ?
  </h2>
</header>
<aside class="submit">
  <button mat-button color="primary" (click)="onDefinitivlyDeleteCandidate(candidateId)">
    Oui
  </button>
  <button mat-button color="warn" (click)="nav.outletsNav('classement', 'manage/candidates')">
    annuler
  </button>
</aside>

3 个答案:

答案 0 :(得分:0)

  

更新

export class ApiCandidateService {

  private baseUrl: string;
  private dataStore: {
    candidatesList: Candidate[];
  };
  private _store = new BehaviourSubject<Candidate []>(dataStore);
  public state$: this._store.asObservable();
  public candidatesList$

  private httpOptions: { headers: HttpHeaders } = {
    headers: new HttpHeaders({
      "Content-Types": "applications/json"
    })
  };

  public constructor(private http: HttpClient) {
    this.baseUrl = "api/Candidates";

    this.candidatesList$ = this.state$.pipe(map(state => 
                            state.candidatesList), 
                            distinctUntilChanged());

  }

  public getCandidates() {
    this.http.get<Candidate[]>(this.baseUrl, this.httpOptions).subscribe(
      (data: Candidate[]) => {
        this.dataStore.candidatesList = data;
        this._store.next(Object.assign({}, 
           this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public getCandidate(id: number | string) {
    this.http.get<Candidate>(`${this.baseUrl}/${id}`, this.httpOptions).subscribe(
      (data: Candidate)  => {
        let notFound = true;

        this.dataStore.candidatesList.forEach((item, index) => {
          if (item.candidateId == data.candidateId) {
            this.dataStore.candidatesList[index] = data;
            notFound = false;
          }
        });

        if (notFound) {
          this.dataStore.candidatesList.push(data);
        }

        this._store.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public createCandidate(newCandidate: Candidate) {
    this.http.post<Candidate>(`${this.baseUrl}`, newCandidate, this.httpOptions).subscribe(
      (data: Candidate) => {
        this.dataStore.candidatesList.push(data);
        this._store.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public updateCandidate(id: number | string, updatedCandidate: Candidate) {
    this.http.put<Candidate>(`${this.baseUrl}/${id}`, updatedCandidate, this.httpOptions).subscribe(
      () => {
        this.dataStore.candidatesList.forEach((candidate, i) => {
          if (candidate.candidateId == updatedCandidate.candidateId) {
            this.dataStore.candidatesList[i] = updatedCandidate;
          }
        });

        this._store.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public deleteCandidate(id: number | string) {
    this.http.delete<Candidate>(`${this.baseUrl}/${id}`, this.httpOptions).subscribe(
      response => {
        this.dataStore.candidatesList.forEach((candidate, i) => {
          if (candidate.candidateId == Number(id)) {
            this.dataStore.candidatesList.splice(i, 1);
          }
        });

        this._store.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error("An error occurred:", error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(`Backend returned code ${error.status}, ` + `body was: ${error.error}`);
    }
    // return an observable with a user-facing error message
    return throwError("Something bad happened; please try again later.");
  }
}
  

在视图组件中

 export class DeleteCandidatesComponent implements OnInit {
  public candidate: Observable<Candidate>;
  public candidateId: string;

  constructor(
    public nav: NavigationService,
    private route: ActivatedRoute,
    private candidateService: ApiCandidateService
  ) {
    this.candidateId = this.route.snapshot.params.id;

**this.candidate$ = this.candidateService.candidatesList$.pipe(
      map(candidate => candidate.find(item => item.candidateId == Number(this.candidateId)))
    );**
  }

  public ngOnInit(): void {


    this.candidateService.getCandidate(this.candidateId);
  }

  public onDefinitivlyDeleteCandidate(id: string) {
    this.candidateService.deleteCandidate(id);
    this.nav.outletsNav('classement', 'manage/candidates')
  }
}
  

在模板中

{{ (candidate$ | async)?.firstName }} {{ (candidate$ | 
             async)?.lastName }} 

答案 1 :(得分:0)

您正在更新ngOnInit()中的属性,只有在导航到该视图时才会调用该属性,因此只有在导航至该组件时才会更新。

您可以创建一个get函数来获取该值并在视图中对其进行更新。

组件:

 export class DeleteCandidatesComponent implements OnInit {
  public candidate: Observable<Candidate>;
  public candidateId: string;

  constructor(
    public nav: NavigationService,
    private route: ActivatedRoute,
    private candidateService: ApiCandidateService
  ) {
    this.candidateId = this.route.snapshot.params.id;
  }

  public ngOnInit(): void {}

  public onDefinitivlyDeleteCandidate(id: string) {
    this.candidateService.deleteCandidate(id);
    this.nav.outletsNav('classement', 'manage/candidates')
  }

  get getCandidates():  Observable<Candidate>{
     this.candidate = this.candidateService.candidatesList.pipe(
      map(candidate => candidate.find(item => item.candidateId == 
       Number(this.candidateId)))
    );
    this.candidateService.getCandidate(this.candidateId);
    return this.candidate;
  }

}

视图:

<header>
  <h2>
    êtes vous sûr de vouloir supprimer définitvement
    <strong>{{ (getCandidates)?.firstName }} {{ (getCandidates)?.lastName }}</strong> ?
  </h2>
</header>
<aside class="submit">
  <button mat-button color="primary" (click)="onDefinitivlyDeleteCandidate(candidateId)">
    Oui
  </button>
  <button mat-button color="warn" (click)="nav.outletsNav('classement', 'manage/candidates')">
    annuler
  </button>
</aside>

答案 2 :(得分:0)

问题可能是我的组件不相同。我主要有两个路由器插座,但第二个路由器的数据却无法实时更新

您怎么看?