94

I have a data driven Angular application. I have a toggle component which I pass in a toggled state. My issue is that the two way data binding does not seem to work unless i pass in the toggle boolean as an object. Is there a way to get this to work without using an EventEmitter or passing the variable in as an object. This is to be a reusable component and the application is heavily data driven so passing the value in as an object in not an option. My code is....

toggle.html

<input type="checkbox" [(ngModel)]="toggled" [id]="toggleId" name="check"/>

toggle.component.ts

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

@Component({
  moduleId: module.id,
  selector: 'toggle-switch',
  templateUrl: 'toggle-switch.component.html',
  styleUrls: ['toggle-switch.component.css']
})

export class ToggleSwitchComponent {

  @Input() toggleId: string;
  @Input() toggled: boolean;

}

parent.component.html

<toggle-switch toggleId="toggle-1" [(toggled)]="nongenericObject.toggled"></toggle-switch>
1
  • 5
    I'd like to stress the importance of mitch's comment on this. To make two-way-binding with a parent component's var work, the @Output decorated EventEmitter has to be named as the corresponding @Input with a Change suffix at the end as in: @Input() toggled: boolean; @Output() toggledChange: EventEmitter<boolean> = new EventEmitter<boolean>(); Commented May 28, 2018 at 18:31

2 Answers 2

168

For [(toggled)]="..." to work you need

  @Input() toggled: boolean;
  @Output() toggledChange: EventEmitter<boolean> = new EventEmitter<boolean>();

  changeValue() {
    this.toggled = !(this.toggled); 
    this.toggledChange.emit(this.toggled);
  }

See also Two-way binding

[UPDATE] - 25 June 2019
From @Mitch's comment below:
It's worth noting that the @Output name must be the same as the @Input name, but with Change on the end. You can't call it onToggle or something. It's a syntax convention.

Sign up to request clarification or add additional context in comments.

10 Comments

He even imported EventEmitter. It might not as bad as you expect ;-)
@silentsod It's nonetheless rather annoying that Angular doesn't have a built-in way to handle 2-way binding on one attribute. Seems like a super basic feature; yet, it's simply not supported. Also, the Angular docs are incredibly convoluted and difficult to comprehend, so I wouldn't be surprised if OP didn't understand how to do this even after reading them.
It's worth noting that the @Output name must be the same as the @Input name, but with Change on the end. You can't call it onToggle or something. It's a syntax convention.
@Mitch: Wow! This has frustrated me for weeks! Is there anywhere in the documentation that describes this convention? There surely must be... I just must have missed it.
@ProxyTech it is documented here: angular.io/guide/two-way-binding: The [()] syntax is easy to demonstrate when the element has a settable property called x and a corresponding event named xChange. However I agree that this should be emphasized.
|
13

Although the question has more than 2 years old I want to contribute my 5 cents...

It isn't a problem about Angular, its about how Javascript works... Simple variables (number, string, boolean, etc) are passed by value whereas complex ones (objects, arrays) are passed by reference:

You can read more about this in Kyle Simpson´s series You dont know js:

https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch2.md#value-vs-reference

So, you can use an @Input() object variable to share scope between components without need to use emitters, observers and whatever similar.

// In toggle component you define your Input as an config object
@Input() vm: Object = {};

// In the Component that uses toggle componet you pass an object where you define all needed needed variables as properties from that object:
config: Object = {
    model: 'whateverValue',
    id: 'whateverId'
};

<input type="checkbox" [vm]="config" name="check"/>

This way you can modify all object properties and you get same value in both components because they share same reference.

2 Comments

This can both be a blessing and a nightmare. I've had a real bad time once because the reference was being passed and I didn't know and a bug ticket was created. I couldn't understand why it was happening, even creating new objects and so on. But now, I just needed a simple two way binding and it works perfectly
Link is broken - looks like they're partway through a new edition but the original is still there: github.com/getify/You-Dont-Know-JS/blob/1st-ed/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.