【问题标题】:Inter component property binding in Angular 2Angular 2 中的组件间属性绑定
【发布时间】:2016-07-21 02:38:31
【问题描述】:

子组件有两个数据绑定输入属性,一个是拼写错误string (inputVariable),另一个是string[] (inputArray) 类型。

import {Component, Inject, Input} from 'angular2/core';
@Component(
{
    selector: 'child-app',
    template: `
    {{inputVariable}}
    <input type="button" (click)="onButtonOneClick()" value="changeFoo">
    <ul>
      <li *ngFor="#el of inputArray"> {{el}} </li>
    </ul>        
    <input type="button" (click)="onButtonTwoClick()" value="ChangeLi">       
    `
})
export class ChildAppComponent {
  @Input() inputVariable: string;
  @Input() inputArray: string[];
  onButtonOneClick() {
    this.inputVariable = 'new string';
  }
  onButtonTwoClick() {
    this.inputArray[0] = 'New element String';
  } 
}

父组件具有相同的属性并在模板内初始化子组件对应的属性([inputArray]="inputArray" inputVariable="inputVariable"

import {Component} from 'angular2/core';
import {ChildAppComponent} from './childApp.component';

@Component({
selector: 'my-app',
template:
`
{{inputVariable}}
    <input type="button" (click)="onButtonOneClick()" value="changeFoo">
    <ul>
      <li *ngFor="#el of inputArray"> {{el}} </li>
    </ul>        
    <input type="button" (click)="onButtonTwoClick()" value="ChangeLi"> 
   <hr>    
    <child-app [inputArray]="inputArray" inputVariable="inputVariable"> </child-app>
  `,
  directives: [ChildAppComponent]
  })
 export class AppComponent {
   inputVariable: string = 'foo';
   inputArray: string[] = ['one', 'two'];

   onButtonOneClick() {
       this.inputVariable = 'new string';
   }
   onButtonTwoClick() {
      this.inputArray[0] = 'New element String';
   }
}

父组件和子组件内的按钮单击会更改相应属性的值(buttonOne -> inputVariable & buttonTwo -> inputArray

当点击第二个按钮(改变string[]属性值)时,父组件和子组件都会发生变化

当点击第一个按钮(更改string 属性值)时,发生在父级或子级内部(无论我点击了哪个组件的按钮)

  • 为什么行为因属性类型而异?
  • 如何在子组件和父组件的string属性之间进行双向绑定?

【问题讨论】:

    标签: angular typescript data-binding


    【解决方案1】:

    对于从孩子到父母的绑定,您需要使用@Output()

    双向绑定

    import {Component, Inject, Input} from 'angular2/core';
    @Component({
        selector: 'child-app',
        template: `
        {{inputVariable}}
        <input type="button" (click)="onButtonOneClick()" value="changeFoo">
        <ul>
          <li *ngFor="#el of inputArray"> {{el}} </li>
        </ul>        
        <input type="button" (click)="onButtonTwoClick()" value="ChangeLi">       
        `
    })
    export class ChildAppComponent {
      @Input() inputVariable: string;
      // v added
      @Output() inputVariableChange:EventEmitter<string> = new EventEmitter<string>();
      @Input() inputArray: string[];
      // v added
      @Output() inputArrayChange:EventEmitter<string[]> = new EventEmitter<string[]>();
    
      onButtonOneClick() {
        this.inputVariable = 'new string';
        this.inputVariableChange.emit(this.inputVariable);
      }
      onButtonTwoClick() {
        this.inputArray[0] = 'New element String';
        this.inputArrayChange.emit(this.inputArray);
      } 
    }
    

    在父母中使用它像

    {{inputVariable}}
        <input type="button" (click)="onButtonOneClick()" value="changeFoo">
        <ul>
          <li *ngFor="#el of inputArray"> {{el}} </li>
        </ul>        
        <input type="button" (click)="onButtonTwoClick()" value="ChangeLi"> 
       <hr>    
        <!-- v added ( )
        <child-app [(inputArray)]="inputArray" inputVariable="inputVariable"> </child-app>
    

    命名是相关的。快捷方式绑定语法[(xxx)]="yyy" 仅在输入和输出命名为@Input() xxx@Output() xxxChange 时有效。否则必须使用长格式。

    [xxx]="zzz" (xxxChange)="zzz = $event"
    

    变化检测

    Angular 不检查对象或数组内部的变化,它只检查对象或数组是否与以前不同。

    如果仅修改了对象的属性或仅添加/删除/替换了元素,Angular 将不会注意到 - 例如在 *ngFor

    <ul>
      <li *ngFor="#el of inputArray"> {{el}} </li>
    </ul>        
    

    如果您绑定到一个属性而不仅仅是元素项,那么*ngFor 会识别更改

    <ul>
      <li *ngFor="#el of inputArray"> {{el.someProp}} </li>
    </ul>        
    

    一种解决方法是例如创建一个新数组

    this.inputArray.slice();
    

    或使用新的 (beta.2) trackBy 功能。
    另见http://www.bennadel.com/blog/3020-understanding-object-identity-with-ngfor-loops-in-angular-2-beta-3.htm

    创建一个副本(新的和不同的数组),这被 Angular 识别为更改。

    【讨论】:

    • 我对你说的有疑问-快捷绑定语法 [(xxx)]="yyy" 仅在输入和输出命名为 @Input() xxx@Output() xxxChange... 时才有效。我使用的父母 - &lt;child-app [inputArray]="inputArray" [(inputVariable)]="inputVariable1"&gt; &lt;/child-app&gt; 和我的孩子 - @Output() abc: EventEmitter&lt;string&gt; = new EventEmitter();....和某处 this.abc.emit(this.inputVariable);。这个怎么样?
    • 我不太明白你的评论是什么意思。约定是绑定到输出使用(outputName)=,输入使用[inputName],但组合形式[(xxxName)] 仅在名称遵循模式@Input() xxxName; @Output() xxxNameChange 时才有效。除了输出需要Change 后缀外,输入和输出名称需要相同。
    • 我应该寻找什么? [(inputVariable)]="inputVariable1"boot.ts?
    • 不需要为数组设置双向绑定(即引用类型)——这假设引用在子节点中没有被更改...即只有数组元素是改变,如OP所示。这是因为父和子都引用了同一个数组。相反,需要为字符串(即原始类型)设置双向绑定,因为父子节点都有自己的字符串。而不是[(inputArray)]="inputArray" inputVariable="inputVariable" 你想要相反的:[inputArray]="inputArray" [(inputVariable)]="inputVariable"
    • @MarkRajcok 另请注意,在数组的情况下,如果我将 inputArray 变量重新分配给某个新数组(inputArray = ['1','2'],则该更改不再影响父级。
    【解决方案2】:

    如果您希望父组件看到更新,您只能通过引用进行更改。您的数组就是这种情况,但您的字符串属性(以及更普遍的原始类型的属性)不是这种情况。

    要让某些东西适用于每种情况,您需要利用输出和双向绑定:

    @Component({
      (...)
    })
    export class ChildAppComponent {
      @Input() inputVariable: string;
      @Output() inputVariableChange: EventEmitter<string> = new EventEmitter();
      @Input() inputArray: string[];
      onButtonOneClick() {
        this.inputVariable = 'new string';
        this.inputVariableChange.emit(this.inputVariable);
      }
    
      (...)
    }
    

    这可以通过这种方式从父组件中使用:

    <child-app [inputArray]="inputArray"
               [(inputVariable)]="inputVariable"> </child-app>
    

    请参阅[(...]) 语法。在这种情况下,inputVariable 将在此属性的子组件中发生更新时透明地更新。

    您会注意到,Angular2 仅在绑定的引用发生更改时才检测到更改,而不是在相应内容(对象属性或数组中的元素)更新时检测到更改。

    这是默认行为,但您可以根据DoCheck 接口提供自己的行为。请参阅此用例的问题:

    【讨论】:

      猜你喜欢
      • 2018-02-13
      • 1970-01-01
      • 2017-09-20
      • 2019-03-23
      • 2017-11-29
      • 2017-07-25
      • 1970-01-01
      • 2020-03-22
      • 2017-02-15
      相关资源
      最近更新 更多