【问题标题】:ReplaySubject it is not updating the array when new object is added AngularReplaySubject 添加新对象时不会更新数组Angular
【发布时间】:2021-02-22 09:03:37
【问题描述】:

我正在处理一个错误,当我尝试创建新的 page 对象时,它会发送到后端但它没有更新数组,我需要重新加载页面以查看所有数组。

我在前端的async 中使用 Observable。 我尝试控制台记录page.component.tsngOnInit,但是当我添加新页面并导航到页面时,ngOnInit 没有调用。

在创建新页面时会发生这种情况。 它会将我发送到pages 的路线,在那里我会显示所有页面列表。 但是当我创建新的Page 时,它会返回一个错误,上面写着。 ERROR Error: Error trying to diff 'Here is the name of the object'. Only arrays and iterables are allowed.

更新:正如 Marco 所说,发生这种情况是因为我将页面混合为对象而不是遍历数组 但我无法解决它,我需要你的帮助。 在pageModelpage.service.ts 中,当我添加新对象时,它只返回添加的Object,而不是整个数组,我认为存在问题,但我不知道如何解决。 但是如果我重新加载页面,那么我会看到我所有的数组。

这是我更新的代码。

这是我的代码。

  export class PagesService {
  public baseUrl = environment.backend;
  private data = new ReplaySubject<any>();
  public userID = this.authService.userID;
  public editDataDetails: any = [];
  public subject = new Subject<any>();
  private messageSource = new BehaviorSubject(this.editDataDetails);
  getPageID = this.messageSource.asObservable();

  constructor(private http: HttpClient, private authService: AuthService) { }

  public getPages() {
    return this.http.get<any>(`${this.baseUrl}/pages/${this.userID}`).subscribe(res => this.data.next(res));
  }
  public pageModel(): Observable<Page[]> {
    return this.data.asObservable(); // Here it throws error
  }
  public getPage(id): Observable<any> {
    return this.http.get(`${this.baseUrl}/page/${id}`);
  }

  public setPage(page: Page, id: string) {
    const api = `${this.baseUrl}/page`;
    const user_id = id;
    this.http.post<any>(api, page, {
      headers: { user_id }
    }).subscribe(res => this.data.next(res));
  }

  changeMessage(message: string) {
    this.messageSource.next(message)
  }

  public updateDate(id: string, page: Page) {
    const api = `${this.baseUrl}/page/${id}`;
    return this.http.put<any>(api, page).subscribe(res => this.data.next(res.data));
  }

来自答案的更新代码。

  public updateDate(id: string, page: Page) {
    const api = `${this.baseUrl}/page/${id}`;
    return this.http.put<any>(api, page).subscribe(res => {
      this.lastSetOfData = res;
      this.data.next(this.lastSetOfData);
    });
  }    
}




export class Page {
  _id = "";
  name = "";
  slogan = "";
  description = "";
  url = "";
  telephone: number;
  pageUrl: string;
  website: string;
  founded: number;
  organization: number;
  email: string;
  coverImage: string;
  profileImage: string;
  specialty?: Specialty[];
  branches: Branches[];
  locations?: Location[];
  phone?:Phone;
  userRole?: string;
  roles?: Roles[];
}
export class Roles {
  role= "";
  userID = "";
}

这是 page.component 的 HTML。

  <div class="main" *ngIf="!showWeb">
    <div *ngFor="let page of pages$ | async" class="card width-900">
      <app-pages-list class="d-flex width-900" [page]="page" [details]="'details'"></app-pages-list>
    </div>
    <div>
    </div>
  </div>

这是 TS 文件。

public pages$: Observable<Page[]>;
ngOnInit(): void {    
this.pageService.getPages();
this.pages$ = this.pageService.pageModel();
}

这是我创建新页面时的代码。

  export class CreatePageComponent implements OnInit {
  public page = new Page();
  search;
  public branch = [];

  constructor(public router: Router,
    public branchesService: BranchesService,
    public authService: AuthService,
      public pageService: PagesService,
      public shareData: SenderService) { }

  ngOnInit(): void {
  }
  createPage() {
    this.page.url = this.page.name;
    this.page.branches = this.branch;
    this.page.locations = [];
    this.page.specialty = [];
    this.page.roles = [];
    this.page.phone = this.page.phone;
    this.page.pageUrl = `${this.page.name.replace(/\s/g, "")}${"-Page"}${Math.floor(Math.random() * 1000000000)}`;
    this.pageService.setPage(this.page, this.authService.userID);
  }
  addBranch(event) {
      this.branch.push(event);
      this.search = "";
  }
  removeBranch(index) {
      this.branch.splice(index, 1);
  }

}

【问题讨论】:

  • 请改进您的问题,因为它很难清楚地理解。您的 HTML 文件中是否有打字稿代码?您有一个从未使用过且从未声明过的 pages 变量。其他变量从不声明。
  • 我认为问题在于您使用空数组初始化数组。然后,您将项目添加到该数组。角度变化检测通过比较实例来检查他是否需要重新渲染。如果它是同一个数组的实例,它不会触发重新渲染。我不确定是否是问题所在,因为我不知道去哪里找。
  • @Marco 我已经编辑了我的代码并删除了未使用的代码。
  • @Marco 我已经编辑了我的问题,添加了其他返回一些错误的代码。
  • 如果您在服务上正确使用了类型,您可能不会遇到这个问题。现在一切都是any,并且您将对象放在需要数组的位置。

标签: javascript angular typescript rxjs observable


【解决方案1】:

根据我对您代码的理解,您的错误被抛出,因为 data 变量包含 2 种类型的对象。

PagesServices

  • getPages 中,您向data 提供Page 的列表。
  • setPageupdatePage 中,你给data 一个Page 的实例。
private data = new ReplaySubject<any>();

当您创建一个新页面时,data 保存您创建的最后一个页面(不是数组)。然后你尝试迭代这个页面。

<div *ngFor="let page of pages$ | async"

此错误来自您无法迭代 Page 对象这一事实。 您应该停止使用any,以便此类错误发生在编译时,而不是运行时。您还需要存储页面数组的实例,在帖子后将项目添加到数组中,然后重播整个数组。

代码

public updateDate(id: string, page: Page) {
    const api = `${this.baseUrl}/page/${id}`;
    return this.http.put<any>(api, page).subscribe((res) => {   
        const index: number = lastSetOfData.findIndex((_page: Page) => _page._id === res._id);
        lastSetOfData[index] = res;
        lastSetOfData = [...lastSetOfData];
        this.data.next(lastSetOfData);
    });
}

updateDate 函数也应命名为 updatePage

【讨论】:

  • 感谢您的回答,您的想法是什么?你能写一些我能看出你在想什么的代码吗?
【解决方案2】:

问题是@Marco 的回复中确定的问题。我从那里开始详细说明。

有几种方法可以解决这个问题。最快的方法可能是将实例变量 lastSetOfData 添加到 PagesService 中,您可以在其中保存数组的最后一个版本。然后在getPages 方法中初始化lastSetOfData。最后在setPage 方法中更新lastSetOfData,在lastSetOfData 末尾附加服务返回的页面,并使用ReplaySubject 通知它。

所以代码可能是这样的

export class PagesService {
  public baseUrl = environment.backend;
  // specify the type of data notified by the ReplaySubject
  private data = new ReplaySubject<Array<Page>>();
  // define lastSetOfData as an array of Pages
  private lastSetOfData: Array<Page> = [];
  ....
  public getPages() {
    return this.http.get<any>(`${this.baseUrl}/page/${this.userID}`).subscribe(res => {
     // res should be an array of Pages which we use to initialize lastSetOfData
     lastSetOfData = res;
     this.data.next(lastSetOfData)
    });
  }
  ....

  public setPage(page: Page, id: string) {
    const api = `${this.baseUrl}/page`;
    const user_id = id;
    this.http.post<any>(api, page, {
      headers: { user_id }
    }).subscribe(res => {
         // update lastSetOfData appending resp, which should be a Page 
         // not the use of the spread operator ... to create a new Array
         lastSetOfData = [...lastSetOfData, resp];
         // now you notify lastSetOfData
         this.data.next(lastSetOfData)
    });
  }
  // probably you have to modify in a similar way also the method updateTable
  public updateDate(id: string, page: Page) {
    ....
  }
  ....
  ....
}

考虑到这可能是解决问题的最快方法。检查它是否有效,然后您可能想尝试重构代码以寻找更符合 rx 习惯的解决方案。但我的建议是先看看这是否能解决问题。

【讨论】:

  • 是的,这是有效的。但是updateDate 的代码是如何编辑的要更新的对象?因为我试过这样lastSetOfData = res; this.data.next(lastSetOfData),但没用。
  • PUT HTML 请求返​​回了什么?当然是已编辑的项目,因此您需要从数组中删除旧项目,然后添加已编辑的项目(API返回的项目)。
  • @Picci 没时间详细说明我的答案,谢谢。
  • @Marco @Picci 关于它所说的更新。 core.js:6006 ERROR Error: Error trying to diff '[object Object]'. Only arrays and iterables are allowed
  • 我认为 Marco 强调了这一点,他是真正给出问题答案的人。
【解决方案3】:

问题是你把一个对象放在你的replaysubject中,尽管在其他地方应该有一个数组。

next(myarray)
next(myobject)

这不会神奇地将对象附加到数组中。

为此,您需要这样的东西:

data.pipe(take(1)).subscribe(list => {
    list.push(newvalue);
    data.next(list);
});

基本上你取最后一个值,一个新项目,然后推送新列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多