kamilk kamilk - 18 days ago 7
TypeScript Question

Output part of two-way binding doesn't work for custom directive

I am trying to implement a directive with a property that would support a two-way binding like this:

[(appfocused)]="isFocused"
. A component could set its
isFocused
property to true to focus on an element but the variable would get reverted to false once the element loses focus.

I've read (here and here) that the key to implementing this kind of binding is having two properties for
@Input
and
@Output
named the same but with the output one suffixed with
Change
. So this is my implementation so far (in TypeScript):

@Directive({
selector: '[appfocused]'
})
export class FocusedDirective implements OnChanges, AfterViewInit {
@Input('appfocused')
focused: boolean;

@Output('appfocused')
focusedChange: EventEmitter<boolean> = new EventEmitter();

constructor(private elementRef: ElementRef, private renderer: Renderer) { }

ngAfterViewInit() {
this.renderer.listen(this.elementRef.nativeElement, 'blur', () => {
this.setFocused(false);
});
}

ngOnChanges() {
if (this.focused) {
setTimeout(() => {
this.elementRef.nativeElement.focus();
}, 0);
}
}

private setFocused(value: boolean) {
this.focused = value;
this.focusedChange.emit(this.focused);
console.log('focused set to ' + value);
}
}


The input property works fine but I can't get the output property to work. Thanks to the
console.log
call, I know the
focusedChange.emit()
line is being hit but it has no effect on the value the property is bound to.

Here is a working example (plunker). It includes a dummy component which displays and sets the value of the
isFocused
property:

@Component({
selector: 'my-app',
template: `
<div>
<textarea [(appfocused)]="isFocused"></textarea>
</div>
<a (click)="focus()" href="#">Focus</a>
{{isFocused}}
`,
})
export class App {
isFocused = false;

focus() {
this.isFocused = true;
}
}


Pressing the Focus link focuses on the
textarea
as expected. However, clicking out of it does not reset the
isFocused
variable. Pressing the link again has no effect - the value of
isFocused
was already true so Angular doesn't detect any changes.

What am I missing?

Answer

As the article you've mentioned stated, for the two-way binding shorthand syntax to work the output's name has to be the same as the input plus the "Change" suffix.

So you're almost there - you just have to name the @Output() properly: not "appfocused", but "appfocusedChange" instead:

@Output('appfocusedChange')
focusedChange: EventEmitter<boolean> = new EventEmitter();
Comments