【问题标题】:Angular 6 - Using Observable to trigger event between siblingsAngular 6 - 使用 Observable 触发兄弟姐妹之间的事件
【发布时间】:2018-11-15 23:13:40
【问题描述】:

所以我在 app-component 中有一个 div 集合,其中填充了我的 app-cube 组件,并且我在父组件中有一个填充有 GridProp 对象的数组,它的索引代表集合中的每个单元格,这意味着我创建了每个“单元格”的内容基于数组 [i] 的内容。

在数组 [i] 中,我有一个对象,其中包含一个 Observable 以及其他内容,例如单元格的内容。 当我更新单元格 i 中的某些内容时,我想更新单元格 [i-1][i+1] 的兄弟姐妹

我试图通过让每个 app-cube 订阅数组中匹配对象中的 observable 来做到这一点。这样我应该能够简单地向数组中任何索引处的观察者发送一个值,并让它触发订阅的组件订阅处理程序,对吧?

数组中的对象属于这个类

export class GridProp implements OnInit {
  private terrain = new Terrain();
  coord: Array<number>;
  cont: {};

  observer: Observer<string>;
  observable: Observable<string> = new Observable((observer: Observer<string>) => {
    this.observer = observer;
  });
}

然后在cube-component.ts中

@Component({
  selector: 'app-cube',
  templateUrl: './cube.component.html',
  styleUrls: ['./cube.component.css']
})
export class CubeComponent implements OnInit {
  @Input() point; // Represents the relevant GridProp object

  ngOnInit() {
    this.point.observable.subscribe(this.handleCubeUpdate);
  }

  handleCubeUpdate() {
    console.log('handling!');
    this.updateWalls();
  }

  updateWalls() {
    // Do stuff to the "walls" values used in this cube.component.html
  }
  toggleWalls() {
    for (const direction in this.point.cont.terrain.walls) {
      if (this.getSide(direction) && this.getSide(direction).cont.terrain.isBlock) {
        this.point.cont.terrain.walls[direction] = false;
        this.getSide(direction).observer.next(true);
      } else {
        this.point.cont.terrain.walls[direction] = true;
      }
    }
    this.updateWalls();
  }
}

在父组件中,我分发了 app-cube

<div class="grid-point-z" *ngFor="let z of y">
  <div 
    class="grid-point"
    attr.data-point="{{ z.coord }}" 
    [ngStyle]="{ 
      'z-index': z.coord[2]
      }">
    <app-cube  
      [point]="z"
      [coord]="z"
      [level]="level"
      (click)="clickPoint(z)"
      (change)="onCubeChange($event)">
      {{z}}
    </app-cube>
  </div>
</div>

它因错误而崩溃

ERROR TypeError: this.updateWalls is not a function
    at SafeSubscriber.push../src/app/assets/cube/cube.component.ts.CubeComponent.handleCubeUpdate [as _next] (cube.component.ts:71)

所以我猜它在 CubeComponent 的订阅中无法识别 this.handleCubeUpdate。有解决办法吗?

【问题讨论】:

  • 这是一个函数updateWalls吗?在我看来,handleCubeUpdate 正在被调用,但看起来 handleCubeUpdate 正在调用 updateWall。另外,IMO,我会在您的 GridProp 组件中使用 Subject。 ```私人主题=新主题(); observable: Observable = subject.asObservable(); ```
  • 糟糕,谢谢。我在 updateWalls 函数调用中进行了编辑。它位于 handleCubeUpdate 函数内部。我按照您的描述更改了代码,但问题是一样的。 handleCubeUpdate 能够 console.log “处理!”,但是 this.updateWalls();在崩溃日志中是“不是函数”
  • updateWalls 是在哪里声明的?正如@dK 所说,最好使用主题或行为主题。
  • 并且 observable.subscribe 声明你订阅的对象 observable.subscribe(p => console.log(p); }。没有下一个,你的 observable 是没用的。
  • updateWalls 在 cube.component.ts 中声明。它处理 HTML 文件中使用的值。 .next() 稍后也会在 cube.component.ts 中调用,为了清楚起见,我会将其添加到帖子中

标签: angular rxjs


【解决方案1】:

现在对我来说更有意义的是,我可以尝试在这里回答这个问题。 希望我能正确表达this :)。

因此,当您在 subscribe 中传递函数引用时,handleCubeUpdate 中的 this 具有不同的上下文。 您可以将函数实现更改为箭头函数。

handleCubeUpdate = () => {
  this.updateWalls();
}

此外,如果您的updateWalls 正在调用另一个函数,那么您应该更改该updateWalls 函数箭头函数。

【讨论】:

  • 接受的答案。这让我可以通过触发他们相应的 observables 来联系兄弟姐妹,就像我想要的那样,谢谢。
  • 实际上它并不像我想象的那样完全有效。虽然我设法触发了兄弟姐妹的可观察,但“this”指的是原始点。我把handleCubeUpdate接触到的所有函数都改成了箭头函数,但它们似乎总是指触发兄弟更新墙壁的点
【解决方案2】:

从我的角度来看,我们有一些设计问题。

在 Angular 中,组件可以使用它们的 @Input()@Output() 属性相互交互(输入 - 了解父组件的更改并更新视图,输出 - 通知其他人内部的更改)。

所以最好添加到您的CubeComponent 输出事件somethingChanged 并在如您所说的更新某些内容时发出此事件。

export class CubeComponent implements OnInit {
  @Input() point; // Represents the relevant GridProp object
  @Output() somethingChanged = new EventEmitter<GridProp>();

  ngOnInit() {
  }

  notifyOthersComponentAboutUpdate() {
    let id = this.point; // You can emit point or some id, to identify it later in parent component
    this.somethingChanged.emit(id);
  }
}

在包含app-cube 组件数组的父组件(app-component)中创建一个方法updateSiblings(idOfComponentWhichWasUpdated) 并从app-cube 组件调用它

<div class="grid-point-z" *ngFor="let z of y">
  <div 
    class="grid-point"
    attr.data-point="{{ z.coord }}" 
    [ngStyle]="{ 
      'z-index': z.coord[2]
      }">
    <app-cube  
      [point]="z"
      [coord]="z"
      [level]="level"
      (somethingChanged)="updateSiblings($event)">
      {{z}}
    </app-cube>
  </div>
</div>

updateSiblings 方法中,您可以访问您的y 数组(*ngFor="let z of y"),其中包含app-cube 组件(y: GridProp[])的所有数据。
您有已更新的组件 ID,因此您可以在数组 y 中找到它,您还可以找到他的兄弟姐妹并更新他们的数据。 Angular 将自动检测 y 数组中的更改,并且您的兄弟姐妹将在 UI 级别更新。

【讨论】:

  • 我最终使用了您的解决方案。我在管理“墙”渲染的 GridProp 中保留了一个 observable,并使用父组件触发单击点的任何兄弟的 observable,包括“this”点,使每个上下文保持不变。谢谢!
  • 很高兴听到这个消息。很高兴帮助@NachoDawg
【解决方案3】:

您是否考虑过在 Angular 中使用 REDUX 架构?

Here's a good tutorial 了解它的工作原理和好处。我使用完全相同的教程来了解和实施。

然后,您可以将该数组保存在您的商店中并调度操作来改变事物。每个动作都可以包含更新自身的逻辑以及围绕它的两个项目。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-09
    • 1970-01-01
    • 2020-01-31
    • 1970-01-01
    • 2019-04-03
    • 2019-05-06
    • 2023-03-03
    相关资源
    最近更新 更多