简体   繁体   中英

Two-Way data binding in *ngFor

I have created a component that is meant to be a switch. You would use it much the same as you would a checkbox. This is a stripped down version.

my-switch.component.ts:

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

@Component({
    selector: 'my-switch',
    template:    `<a (click)='toggle()'>
                    <span *ngIf='value'>{{onText}}</span>
                    <span *ngIf='!value'>{{offText}}</span>
                  </a>`
})
export class MySwitchComponent {
    @Input() onText: string = 'On';
    @Input() offText: string = 'Off';
    @Input() value: boolean;

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

    position: string;

    toggle() {
        this.value = !this.value;
        this.change.emit(this.value);
    }
}

My plan is to use it like this:

parent-component.ts

import {Component} from '@angular/core';
import {MySwitchComponent} from 'my-switch.component';

@Component({
    selector: 'my-sites',
    directives: [MySwitchComponent]
    template: `<table>
                 <tr *ngFor='let item of items'>
                   <td>
                     <my-switch 
                       [(value)]='item.options.option1'
                       (change)='logItem(item)'>
                     </my-switch>
                   </td>
                 </tr>
               </table>`
})
export class MySitesComponent {
    items: Object[] = [
        {options: { option1: false }}
    ];

    logItem(item) {
        console.log(item)
    }   
}

Again, this is simplified, but I think illustrates what I expect. My expectation is that when the switch is clicked, the view updates from "Off" to "On" and the value of the option is logged. The problem is that the value that is logged looks like this:

{options: {option1: false}}

My belief is that the items being iterated over are read-only. I know that I can work around this issue, but I would like to know if what I am trying to do is possible, or ill-advised, and also why it doesn't work.

Angular "de-sugars" the [(x)] syntax into an x input property for property binding and an xChange output property for event binding. -- reference

Therefore, if you name your input property value , you must name your output property valueChange :

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

That's the only piece of the puzzle you were missing. You now have two-way databinding between a parent and a child component.

If you want to execute some logic when the child changes/ emit() s the value, catch the (valueChange) event in the parent component:

(valueChange)='logItem(item)'>

Plunker


I also suggest

console.log(JSON.stringify(item))

In parent child scenario you can take advantage of two-way binding with xxxChange Output property as shown below, ,
In Parent - [(xxx)]="someValue"
In Child - @Input xxx: boolean;
@Output() xxxChange = new EventEmitter <boolean> ();

Note that xxxChange property. which is missing in your case

Now, Look here for the code- Plunker

<td> From Parent - {{item.options.option1}}   <--------------------value will keep changing
                     <my-switch 
                       [(value)]='item.options.option1'>  <---------two way binding
                     </my-switch>
 </td>

Note here, [()] represents two way binding so in parent you don't need to use (valueChange)="someValue=$event" to catch the change. [(value)]='item.options.option1' will automatically bind you new or changed value to item.options.option1 .


export class MySwitchComponent {

    @Input() value: boolean;
    @Output() valueChange = new EventEmitter <boolean> ();  <----xxxChange output property

...
...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM