【问题标题】:Angular: how to send data from child component to parent component?Angular:如何将数据从子组件发送到父组件?
【发布时间】:2021-02-15 12:30:19
【问题描述】:

我正在学习 Angular。我已经阅读了documentation 和五篇博客文章,它像泥巴一样清晰。我认为有两个模板。子模板有一个按钮。当用户单击按钮时,我希望在父模板中显示一个元素(说明卡)。当用户再次单击按钮时,我希望元素不显示。即,子按钮打开和关闭父元素。

<button (click)="toggleInstructions()">Instructions</button>

在我的标题(子)组件中,我创建了一个输出事件发射器sendData、一个布尔变量showInstructions,以及一个在truefalse 之间切换showInstructions 的函数。在记录showInstructions(效果很好!)后,它会调用sendData 来发出数据。我创建了一个新变量myData,因为我对this 的使用感到困惑。

import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent {
  @Output() sendData = new EventEmitter<boolean>();

  showInstructions: boolean = false;

  toggleInstructions(myData: boolean) {
    console.log("Clicked!");
    switch (true) {
      case (this.showInstructions === true):
      this.showInstructions = false;
      break;

      case (this.showInstructions === false):
      this.showInstructions = true;
      break;

      default:
      console.log("Error in switch-case.");
    }
    console.log(this.showInstructions);
    myData = this.showInstructions;
    console.log(myData);
    this.sendData.emit(myData);
  }
}

在父组件中,我导入了子组件(HeaderComponent)并创建了一个函数toggledInstructions 来接收数据。当我单击该按钮时,没有任何记录。

import { Component } from '@angular/core';
import { HeaderComponent } from './header/header.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  toggledInstructions(myData: boolean) {
    console.log("Data coming in!");
    console.log(myData);
  }
}

我很困惑这应该如何工作。除了数据变量名称(myData)之外,我在连接发送和接收功能的documentation 中什么都看不到。

感谢您的帮助!

【问题讨论】:

  • 你能提供stackblitz吗?
  • 请提供app.component.html

标签: angular


【解决方案1】:

我发现这个 ZeroesAndOnes blog post 很好地解释了这一点。要解决我的答案,请从博客文章开始并启动其代码。

child.component.html 更改为:

<div style="border: solid 1px gray; padding: 15px; margin-top: 15px;">
    <h2>Child Component</h2>
    <button (click)="clickButton()">Show Element in Parent</button>
</div>

子模板没有什么特别之处。只是一个触发控制器功能的按钮。

child.component.ts 更改为:

import { Component, EventEmitter, Output } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
})
export class ChildComponent {
  showElement: boolean = false;
  @Output() buttonClicked: EventEmitter<boolean> = new EventEmitter<boolean>();

  clickButton() {
    console.log("(Child) Value before toggle: " + this.showElement);
    console.log("(Child) Button clicked!");
    switch (true) {
      case (this.showElement === true):
      this.showElement = false;
      break;

      case (this.showElement === false):
      this.showElement = true;
      break;

      default:
      console.log("Error in switch-case.");
    }
    console.log("(Child) Value after toggle: " + this.showElement);
    this.buttonClicked.emit(this.showElement);
  }

}

变量列表以showElement开头,这是一个boolean,初始化为false,即隐藏父元素。

Next 是另一个变量,它是 Angular 魔法的第一部分。变量是buttonClicked,它有装饰器@Output()。这个装饰器意味着该变量将在控制器外部可用。该变量的类型为EventEmitter&lt;boolean&gt;,并为其分配了一个新的事件发射器。如果您喜欢较短的行,可以省略类型:

@Output() buttonClicked = new EventEmitter<boolean>();

接下来是在truefalse 之间切换showElement 的函数。这个函数的底部是 Angular 魔法的第二部分。调用输出事件并发送showElement 变量以由事件发射器发射。

现在将父模板app.component.html 更改为:

<div style="border: solid 1px gray; padding: 15px;">
  <app-child (buttonClicked)="parentEventHandlerFunction($event)">
  </app-child>
  
  <h1>Parent Component</h1>
  <span><b>Value emitted from child component is:</b></span>
  {{ valueEmittedFromChildComponent }}
  
  <div *ngIf="valueEmittedFromChildComponent"><span style="color: red;"><b>My awesome element!</b></span>
  </div>
  
</div>

最大的 Angular 魔法在父模板中,特别是在这一行:

  <app-child (buttonClicked)="parentEventHandlerFunction($event)">
  </app-child>

指令app-child 将子模板嵌入到父模板中。 buttonClicked 是子控制器中的事件发射器。 parentEventHandlerFunction 是父控制器中的一个函数。 $event 是从子级发送给父级的对象。这条单行结合了子控制器和父控制器的部分,用等号连接它们。这就是 Angular 的巨大魔力。

现在设置父控制器app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent {
  valueEmittedFromChildComponent: boolean;

  parentEventHandlerFunction(valueEmitted){
    this.valueEmittedFromChildComponent = valueEmitted;
    console.log("(Parent) Value emitted: " + valueEmitted);
  }
}

我们有一个名为valueEmittedFromChildComponent 的变量,即boolean。然后我们有一个函数parentEventHandlerFunction 和一个参数valueEmitted$event 对象来自父模板中的幻线,并赋值给局部变量valueEmittedFromChildComponent

回到父模板,我们显示局部变量valueEmittedFromChildComponent 并在*ngIf 中使用它来显示和隐藏元素。

ZeroesAndOne 总结了他们的博客文章,“总之,Angular 使得创建子组件变得非常容易,这些子组件使用 @Output() 装饰器向其父组件发送数据。”哈!这不是我所说的“非常容易”。相比之下,在 AngularJS 中,两个模板将共享一个控制器。一个模板中的按钮绑定到控制器中的一个函数,该函数切换一个变量,该变量绑定到第二个模板中的一个元素以显示和隐藏一个元素。数据从第一个模板到控制器中的单个变量再到第二个模板。这“非常简单”。

在 Angular 中,子模板中的按钮触发子控制器中的一个函数,与 AngularJS 相同。但是你需要用事件发射器制作一个@Output() 装饰器,并从切换函数调用事件发射器。然后将数据发送到父模板中的魔术线,该模板结合了子控制器和父控制器的内容。然后数据到父控制器,然后返回父模板显示或隐藏目标元素。

让我们重复一遍。当用户单击子模板中的按钮时,数据将转到子控制器,其中一个函数切换值,然后将数据从函数发送到事件发射器。事件发射器将数据发送到父模板,父模板将数据发送到父控制器,父控制器将数据发送回父模板以显示或隐藏元素。这不是我所说的容易。

你会如何在 React 中做到这一点? React 更像 AngularJS 还是 Angular?

【讨论】:

    【解决方案2】:

    看起来你需要$event 变量来获取发射值:

    <button (click)="toggleInstructions($event)">Instructions</button>
    

    toggledInstructions(event: boolean) {
        console.log("Data coming in!", myData);
    }
    

    事件绑定(fooevent)允许绑定到DOM事件和EventEmitter事件

    更新:

    工作代码示例:

    ChildComponent.ts:

    @Output() public onFooClicked = new EventEmitter<boolean>();
    
    showMenu() {
      this.onFooClicked .emit(true);
    }
    

    app.component.ts:

    <foo (onFooClicked)="onClicked($event)">
    </foo>
    
    onClicked(event: boolean) {
        console.log(`Somebody clicked`, event);
    }
    

    In addition, there is a great article how to pass data.

    【讨论】:

    • 也许,但肯定有其他问题。我添加了$event,但我没有看到“数据传入!”在控制台中。该文档不使用$event
    • @ThomasDavidKehoe 您是否已将event 添加为方法的参数?请看我更新的答案。
    猜你喜欢
    • 2018-07-07
    • 2018-08-12
    • 2017-09-13
    • 1970-01-01
    • 2020-09-28
    • 2019-09-07
    • 2022-08-02
    相关资源
    最近更新 更多