2

Simply put, I want to two-way bind my Switch component to a boolean in a service

Something like this:

@Component({
    selector: 'my-switch',
    template: '...'
})

export class Switch {
    @Input() state: Boolean

    toggle() {
        if (this.state) {
            this.state = false
        } else {
            this.state = true
        }
    }
}

@Injectable()

class export FooService {
    theBoolean: Boolean
}

@Component({
    selector: 'my-app',
    template: '<my-switch [(state)]="_foo.theBoolean"></my-switch>{{ _foo.theBoolean }}'
})

export class App {
    constructor(private _foo: FooService) {}
}

So what should happen here is that, when the switch is toggled, the onChanges event in FooService should fire, and vice versa.

2
  • For two-way binding, you need to expose an stateChange Output event using EventEmitter. Commented May 1, 2016 at 22:12
  • Could you post this as an answer with some example code? Commented May 1, 2016 at 22:17

3 Answers 3

3

For two-way binding to work, you need to declare an Output event called 'stateChange'

@Output stateChange: EventEmitter<Boolean>=new EventEmitter();

Then in your toggle implementation:

toggle() {
    if (this.state) {
        this.state = false;
    } else {
        this.state = true
    }
    this.stateChange.emit(this.state);
}

In your HTML template:

 [(state)]="model"

Is equivalent to:

 [state]="model"  (stateChange)="model=$event"

Where $event is the argument passed to the emit method of the EventEmitter. Whenever state changes, it emits the stateChange event, which then updates the model in the parent component - thereby keeping the models in sync

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

6 Comments

Is the "Change" suffix at the end important? Or can it be anything?
Also your code at the top seems to be invalid, according to typescript.
Fixed, should have been an assignment
The 'Change' suffix is important for the [(model)] notation to work. It's a convention of Angular2 and cannot be customized
Thank you, this has always confused me. Great to know.
|
0

You could also try the following

Define a variable foo of type FooService as @Input() in the Component Switch and pass the foo property of App to the Switch Component su as

@Component({
    selector: 'my-app',
    template: '<my-switch [foo]="_foo"></my-switch>{{ _foo.theBoolean }}'
})

export class App {
    constructor(private _foo: FooService) {}
}

export class Switch {
    @Input() foo: Foo

    toggle() {
        this.foo.theBoolean = !this.foo.theBoolean;
}

This way you have only one instance of FooService shared between App and Switch. Therefore any change on it (procured either by App or Switch) is reflected on the other Component.

I hope this helps

Comments

0

You can also notice that you can easily implement a custom component that is ngModel / ngControl compliant by leveraging a custom value accessor. This way you could use ngModel, ngControl / ngFormControl make your component by part of the form (validation, value) like this:

<my-switch [(ngModel)]="_foo.theBoolean"
           #ctrl="ngModel" ngControl="switch"></my-switch>

Here is the way to implement this:

const SWITCH_VALUE_ACCESSOR = new Provider(NG_VALUE_ACCESSOR, { useExisting: forwardRef(() => Switch), multi: true});

@Component({
  selector: 'my-switch',
  template: '...'
})
export class Switch implements ControlValueAccessor {
  state: boolean;

  onChange = (_) => {};
  onTouched = () => {};

  writeValue(value: any): void {
    this.state = value;
  }

  toggle() {
    if (this.state) {
        this.state = false
    } else {
        this.state = true;
    }
    this.onChange(this.state);
  }

  registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
  registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}

See this link for more details:

Comments

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.