在Angular中,从孙子组件调用祖父组件的方法。

huangapple go评论76阅读模式
英文:

Angular - Calling grandparent component method from grandchild component

问题

以下是您要的代码的翻译部分:

Grandparent component:

    export class DepartmentComponent {
      department: Department = {} as Department;
    
      public sendBlockToBlockchain(message: string) {
          //这是由孙子组件调用的方法!
      }
    }

.

   
      <mat-card *ngIf=department>
        <h1>{{ department.name }}</h1>   
        <app-department-message [department]=department></app-department-message>
      </mat-card>
   
the component between them:

    export interface MessageComponent {
      sendMessage(message: string): void;
    }

    export class DepartmentMessageComponent {
      @Input() department: Department = {} as Department;
    }
.

    <ng-container *ngIf="department" [ngSwitch]="department.id">
      <app-client-message *ngSwitchCase="1"></app-client-message>
      <app-buyer-message *ngSwitchCase="2"></app-buyer-message>
      <app-financier-message *ngSwitchCase="3"></app-financier-message>
      <app-shipper-message *ngSwitchCase="4"></app-shipper-message>
      <app-producer-message *ngSwitchCase="5"></app-producer-message>
      <app-spectator-message *ngSwitchDefault></app-spectator-message>
    </ng-container>

Grandchild component: [There are 5 departments, I'll use buyer in this example]

    import {MessageComponent} from "../department-message/department-message.component";
    
    export class BuyerMessageComponent implements MessageComponent {
      sendMessage(message: string) {
           //我希望这里调用 sendBlockToBlockchain! <-----
      }
    }

.

    <textarea rows="3" cols="40" #message></textarea>
    <button (click)="sendMessage(message.value)">Send Message</button>

如何实现这一点?是否有更好的方法来完成我正在做的事情?谢谢!
英文:

Grandparent component:

export class DepartmentComponent {
department: Department = {} as Department;
public sendBlockToBlockchain(message: string) {
//This is the method to be called by the grandchild!
}
}

.

  <mat-card *ngIf=”department”>
<h1>{{ department.name }}</h1>   
<app-department-message [department]=“department”></app-department-message>
</mat-card>

the component between them:

export interface MessageComponent {
sendMessage(message: string): void;
}
export class DepartmentMessageComponent {
@Input() department: Department = {} as Department;
}

.

<ng-container *ngIf="department" [ngSwitch]="department.id">
<app-client-message *ngSwitchCase="1"></app-client-message>
<app-buyer-message *ngSwitchCase="2"></app-buyer-message>
<app-financier-message *ngSwitchCase="3"></app-financier-message>
<app-shipper-message *ngSwitchCase="4"></app-shipper-message>
<app-producer-message *ngSwitchCase="5"></app-producer-message>
<app-spectator-message *ngSwitchDefault></app-spectator-message>
</ng-container>

Grandchild component: [There are 5 departments, I'll use buyer in this example]

import {MessageComponent} from "../department-message/department-message.component";
export class BuyerMessageComponent implements MessageComponent {
sendMessage(message: string) {
//I want this to call sendBlockToBlockchain! <-----
}
}

.

<textarea rows="3" cols="40" #message></textarea>
<button (click)="sendMessage(message.value)">Send Message</button>

How do I do this? And is there a better way to do what I am doing? Thanks!

答案1

得分: 2

沿着你所在的道路前进

最佳实践是,不要直接在孙组件中调用函数,而是使用@Output事件向更高级的组件发送消息。这个想法是子组件不应该能够影响其父组件的状态。这会导致许多难以诊断的错误。

子组件 TypeScript

export class BuyerMessageComponent implements MessageComponent {
@Output messageChange = new EventEmitter<string>(); // 将其命名为任何有意义的名称
sendMessage(message: string) {
messageChange.emit(message);
}
...
}

然后,父组件可以监听输出并像子组件一样重新发出它。

父组件 HTML

<ng-container *ngIf="department" [ngSwitch]="department.id">
<app-client-message *ngSwitchCase="1"></app-client-message>
<app-buyer-message *ngSwitchCase="2" (messageChange)="forwardMessage($event)"></app-buyer-message> // 注意输出事件
<app-financier-message *ngSwitchCase="3"></app-financier-message>
<app-shipper-message *ngSwitchCase="4"></app-shipper-message>
<app-producer-message *ngSwitchCase="5"></app-producer-message>
<app-spectator-message *ngSwitchDefault></app-spectator-message>
</ng-container>

继续这个模式,直到达到数据需要流向的级别。

更好的方法:服务

显然,从曾曾孙发送消息到曾曾祖父可能会变得相当繁琐,因为每一层之间都需要有输出绑定来转发数据,而在你的情况下,由于我想你也希望从多个不同的孙子那里发送消息,情况会变得更糟。

另一种方法是将sendBlockToBlockchain移动到某种可以注入到子组件中的共享服务中。我不知道你需要做多少重构,但这是我会采取的方法。

修订后的子组件 TypeScript

export class BuyerMessageComponent implements MessageComponent {
constructor(private blockchainService: BlockchainService) {}  // 将新创建的区块链服务注入到子组件中
sendMessage(message: string) {
this.blockchainService.sendBlockToBlockchain(message);
}
...
}

你需要将sendBlockToBlockchain的代码移动到新的服务中,并将该服务添加到你的模块提供者中,以便进行注入。

服务负责“拥有”数据,而组件负责向服务“请求”当前数据是一种良好的实践。这允许组件负责呈现数据,而服务负责管理数据。这使得你可以从多个地方更新数据,而不会有多个副本漂浮,前提是你正确配置了你的模块。

这种方法可能需要你将大部分区块链的状态管理从祖父组件中移出,放到服务中,但这对于维护来说可能更好。DepartmentComponent可能还需要注入区块链服务,以便从服务中获取状态。

英文:

Going Down the Road You Are On

It is best practice to, instead of calling the function directly in the grandchild, to use @Output events to send a message to the higher components. The idea is that the child components should not be able to impact their parent's state. This causes lots of difficult to diagnose bugs.

child component TS

export class BuyerMessageComponent implements MessageComponent {
@Output messageChange = new EventEmitter&lt;string&gt;(); // name this to whatever makes sense
sendMessage(message: string) {
messageChange.emit(message);
}
...
}

The parent component then can listen for the output and re-emit it just like the child did from the button.

parent component HTML

&lt;ng-container *ngIf=&quot;department&quot; [ngSwitch]=&quot;department.id&quot;&gt;
&lt;app-client-message *ngSwitchCase=&quot;1&quot;&gt;&lt;/app-client-message&gt;
&lt;app-buyer-message *ngSwitchCase=&quot;2&quot; (messageChange)=&quot;forwardMessage($event)&quot;&gt;&lt;/app-buyer-message&gt; // notice the output event
&lt;app-financier-message *ngSwitchCase=&quot;3&quot;&gt;&lt;/app-financier-message&gt;
&lt;app-shipper-message *ngSwitchCase=&quot;4&quot;&gt;&lt;/app-shipper-message&gt;
&lt;app-producer-message *ngSwitchCase=&quot;5&quot;&gt;&lt;/app-producer-message&gt;
&lt;app-spectator-message *ngSwitchDefault&gt;&lt;/app-spectator-message&gt;
&lt;/ng-container&gt;

Continue this pattern until you reach the level the data needs to flow up to.

A Better Approach: Services

Obviously this can get pretty tedious to send a message up from a great-grandchild to its great-grandparent, since every layer between needs to have output bindings to forward the data along, and in your case it gets even worse with the multiple different grandchildren which I assume you also want to send messages from.

Another approach would be to move the sendBlockToBlockchain to some sort of shared service that can be injected into the child components. I don't know how much refactoring you would need to do, but this is the approach I would take.

revised child component TS

export class BuyerMessageComponent implements MessageComponent {
constructor(private blockchainService: BlockchainService) {}  // Inject the newly created blockchain service into the child component
sendMessage(message: string) {
this.blockchainService.sendBlockToBlockchain(message);
}
...
}

You will need to move the sendBlockToBlockchain code into the new service and add add the service to your module's providers so it gets injected.

It is good practice to have services be responsible for "owning" data, and having components "ask" them for the current data. This allows the components to be in charge of presenting the data, and the services in charge of managing data. This lets you update data from multiple places without multiple copies floating around, assuming you configure your module correctly.

This approach will likely require you to move a lot of the state management of the blockchain out of the grandparent component, and into the service, but that is probably better for maintenance anyway. The DepartmentComponent will likely also need the blockchain service injected into it so it can get the state from the service.

huangapple
  • 本文由 发表于 2023年6月1日 02:03:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76376211.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定