【问题标题】:Angular 8 UI observable not triggering change detection - ChangeDetectionStrategy is set to defaultAngular 8 UI observable 未触发更改检测 - ChangeDetectionStrategy 设置为默认值
【发布时间】:2019-09-13 02:46:12
【问题描述】:

我有一个容器组件,其中注入了服务。该服务包含一个可观察的。在容器组件的模板中,我使用异步管道将该可观察对象绑定到子组件的输入属性。除了我必须与 UI 交互以触发更改检测并显示通过 observable 推送的数据之外,一切似乎都正常工作。

(不更新我的意思是:子组件html中的<ng-container *ngFor="let section of sections;">没有执行)

我尝试了很多方法,包括在父组件中创建另一个 observable,并通过该 observable 中继来自服务的数据 - 但这只是一种故障排除措施,非常麻烦。我相信它有同样的结果。

注意:

1. The service is pulling data from a .net backend via a socket (SignalR).
2. I am also using Angular Material's drag and drop library in this project.
3. **This post seems very similar**, but I can't discern a cause or solution from it: https://stackoverflow.com/questions/48955143/subscribing-to-observable-not-triggering-change-detection
4. I mention 3 because that person was using angular material's infinite scrolling library and I am using drag and drop. Maybe something in that library is interfering with change detection.
5. There is no OnPush change detection anywhere in this project

服务:

export class DocumentCreationService {

  private library = new Subject<DocumentSection[]>();
  public library$ = this.library.asObservable();
...
  private registerOnServerEvents(): void {

  this.proxy.on('documentSectionsRetrieved', (result: DocumentSection[]) => {
        this.library.next(result);
    });
  }
...
  public retrieveDocumentSections = () => {
    this.proxy.invoke('getDocumentSections')
              .then(() => console.log("getDocumentSections completed"))
              .catch(err => console.error(err));
  }
...
}

容器ts:

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent {
...
  constructor(public documentCreator: DocumentCreationService) { }
}

容器html:

<document-section-library #library [hidden]="selectedView !== 'library'" [sections]="documentCreator.library$ | async" [connectedDropTargets]="dropTargets">

儿童 ts:

@Component({
  selector: 'document-section-library',
  templateUrl: './document-section-library.component.html',
  styleUrls: ['./document-section-library.component.scss']
})
export class DocumentSectionLibraryComponent {
...
  @Input()
  public sections: DocumentSection[];
...

子 html:

  <div class="document-section-library__contentarea" cdkDropList id="sectionLibrary" #sectionLibrary="cdkDropList" [cdkDropListConnectedTo]="connectedDropTargets" [cdkDropListData]="sections" (cdkDropListDropped)="onDrop($event)">

    <ng-container *ngFor="let section of sections;">

      <ng-template [ngIf]="section.content && section.content.length > 0" [ngIfElse]="table">

        <document-section-custom class="document-section" cdkDrag 
          [id]="section.id"
          [templateId]="section.templateId" 
          [name]="section.name" 
          [enabled]="section.enabled" 
          [content]="section.content">
        </document-section-custom>

      </ng-template>

      <ng-template #table>
        <document-section-table class="document-section" cdkDrag 
          [id]="section.id" 
          [templateId]="section.templateId"
          [name]="section.name" 
          [enabled]="section.enabled" 
          [content]="section.content">
        </document-section-table>
      </ng-template>

    </ng-container>

  </div>

我希望 UI 立即反映通过 observable 推送的数据,而无需单击某些内容或以其他方式与 UI 交互。

在这种情况下,我试图用一个 observable 来做到这一点,因为我似乎也无法让它以任何其他方式工作。

最终,我真正想要的是将 DocumentSection 数组作为服务中的公共属性,通过服务上的方法更新该数组,并立即反映对数组的更改。我不确定我是否真的需要一个可观察的。我希望服务上的属性像组件上的本地属性一样。

所以两个问题:

  1. 这个变更检测问题是怎么回事,我该如何解决?

  2. 为什么不能(或可以)我,只需在服务上创建一个公共数组并正常绑定它(而不是通过异步管道)并让它以与公共数组相同的方式工作容器组件的属性?

【问题讨论】:

    标签: angular rxjs signalr angular8


    【解决方案1】:

    也许发生的事情是他们的订阅(容器视图中的async)是在初始文档到达之后发生的,所以 Angular 没有接收到该发射?

    我会尝试改变:

    private library = new Subject<DocumentSection[]>();
    

    private library = new BehaviorSubject<DocumentSection[]>([]);
    

    或者,您可以尝试完全不使用可观察对象,并绑定到通过 getter 访问的基本数组。

    private _library = []; //  update when data is fetched
    public get library() { return this._library || []; } // data-bind this
    

    【讨论】:

    • 感谢您的快速回复。我刚刚尝试了这两个建议 - 它们的行为与原来的相同。我开始认为 Angular Material 的 DnD 库可能会干扰正常的变化检测。
    • 我还将子组件的数组更改为 setter 并添加代码以在执行时记录日志。在数据检索后单击 UI 之前,该代码不会运行。 (触发 UI 渲染的相同 UI 交互)这正常吗?即使您将数据推送到可观察对象,组件(TS)代码也不会在下一个更改检测周期之前执行?我假设 TS 代码独立于 HTML 渲染运行 - 否则它如何知道发生了数据更改以触发更改检测。
    • 看起来很奇怪。你确定数据是按预期推送的吗?例如,如果您通过管道将 observable 打印到控制台,是否打印了任何内容?喜欢library$ = this.library.pipe(tap(docs =&gt; console.log(docs)));
    • 绝对是/曾经是。我做了很多故障排除,最终它似乎与我正在使用的信号器库有关。该库针对预 .NET Core 后端(适合我的用例)并且依赖于 JQuery。我用 .NET Core 2.2 替换了后端,将 Angular 库更新为 .NET Core 版本,并删除了 JQuery——之后一切正常。没有可观察的,只是一个常规数组。 @BettleJuice - 感谢您的建议和帮助。我很高兴知道宇宙/角度仍然按预期运行,这个错误让我怀疑。
    • 很高兴你找到了它。这是发现的最糟糕的错误。一个与您的代码无关但来自您认为可以安全使用的依赖项。因为它让你怀疑你所做的所有假设。比如“浏览器真的知道1 &lt; 2吗?”
    【解决方案2】:

    最终,这似乎是 Angular 和 JQuery 和/或信号器(.net 核心之前)库的某种冲突/问题 - 该信号器库依赖于 JQuery。我用更新的 .NET Core 版本替换了信号器库(当然,我也将后端更新为 .NET Core)并删除了 JQuery,一切正常。我希望我能为其他人找到一个更好的根本原因,但在与此作斗争 48 小时后,我很高兴继续前进。

    如果有人对这两个库可能导致问题的原因有任何想法/想法,我仍然感兴趣。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-09
      • 1970-01-01
      • 2017-04-16
      • 2021-09-02
      • 1970-01-01
      • 2020-03-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多