How to make an object property function into an ev

2019-09-10 15:42发布

问题:

This is a follow up to this question that was about function references between components.

If I have a ParentComponent:

export class ParentComponent {
  public myParentVariable = 'Hello world';

  test() {
    console.log(this.myParentVariable);
  }

  public options: any[] = [
    {label: 'Item 1', onClick: this.test},
    {label: 'Item 3', onClick: this.test},
    {label: 'Item 6', onClick: this.test},
    {label: 'Item 2', onClick: this.test}
  ];
}

As you can see I pass a reference to test in the onClick property.

Then I have a ChildComponent which I pass options to from the ParentComponent:

<child [options]="options"></child>

And in child I have a list:

<ul class="dropdown-list">
  <li *ngFor="#option of options" (click)="option.onClick()"></li>
</ul>

As you can see in the template I then call onClick from before as (click)="option.onClick()".

The problem now is that from here it has no idea what this.myParentVariable is anymore since we're just passing a reference to test. So instead I want to make onClick into an EventEmitter which I can then call .emit() on, so that we execute test with the correct data binding, like shown here.

But since I pass down an array containing objects that contains a property that reference a function, I'm not quite sure how to make the single onClick property of each object an EventEmitter.

I tried making the entire options into an outputted EventEmitter but that failed miserably.

Any ideas on how to achieve this?

回答1:

this keyword in JavaScript depends on context. In most cases, the value of this is determined by how a function is called. Since TypeScript is a typed superset of JavaScript it behaves the same, please follow the links below.

ECMAScript 5 introduced Function.prototype.bind. Calling f.bind(someObject) creates a new function with the same body and scope as f, but where this occurs in the original function, in the new function it is permanently bound to the first argument of bind, regardless of how the function is being used.

function's this keyword, bind method

You could also use arrow functions as alternative.



回答2:

By passing parent functions to the child, you are tightly coupling the parent component to the child (which is normally not recommended). In other words, you are revealing information about parent methods to the child. Ideally, the child should not know anything about the parent. (This makes the (child) component more reusable.)

Instead of your current approach, i.e., instead of passing down parent function references to the child, pass down some IDs or some other unique values. When a click event occurs in the child, pass the ID or unique value back up to the parent (using an output property/EventEmitter):

<li *ngFor="#option of options" (click)="myEmitter.emit(option.uniqueValue)"></li>

Then in the parent, map that value to the appropriate function call and call it:

<child [options]="options" (myEmitter)="doWork($event)"></child>
doWork(uniqueValue) {
   // map uniqueValue to some function and call it
}

Another added benefit with this approach is that you won't have to mess with this contexts.