Get form control from raw element in Angular2
Asked Answered
H

3

5

Let's say I'm writing a custom attribute directive for use on Angular2 form elements. I want to be able to use my attribute like so:

<form [formGroup]="myFormGroup">
  <input type="text" [myHighlight] formControlName="name" />
</form>

Inside my directive I have something like this:

@Directive({selector: '[myHighlight]'})
export class MyHighlightDirective {
  constructor (private el: ElementRef) {
    this.el.nativeElement.style.backgroundColor = 'yellow';
  }
};

But now let's say I want to do something on all changes to the input control. Maybe I want to randomize the color on each change.

The HostListener('onChange') event from the Angular2 guide works when the user is typing into the input box. But it doesn't get activated for setValue events that are called on the form control, even with emitEvent set to true. The Angular docs on Forms say that setValue will cause a valueChanges event to be emitted on the form control object. But I don't want to have to pass this in to the directive every time I use it, as that's clunky.

Is there any other way for the directive to get access to the original form control, given that it just has the element reference?

Headlight answered 21/3, 2017 at 19:43 Comment(0)
T
8

Angular DI to the rescue!

You can use the Angular Dependency Injection system to inject the FormControl attached to the host element of your directive.

@Directive({selector: '[myHighlight]'})
export class MyHighlightDirective {
  constructor (private el: ElementRef, private formControl: FormControl) {
    // have fun with formControl.valueChanges
    .......

  }
};

Why this works:

Angular registers directives into the injector of the element to which the directive is attached. So, when you ask for an instance of a particular directive, the first lookup will be on the host element first. (The same thing applies to components)

Towrope answered 21/3, 2017 at 20:19 Comment(12)
I'm getting a No provider for FormControl! when I do that and I have FormsModule imported into my module. When I add FormControl to the providers part of my module, I get Syntax error Can't resolve all parameters for FormControl: (?, ?, ?). at SyntaxError.BaseErrorHeadlight
Is your app divided into multiple NgModules?Towrope
Yes. I tried also importing my directive into the top AppModule, but it didn't take when I tried to use it, so instead I have a module which exports it, and then the module which applies it in the template is importing that module.Headlight
And which module is importing FormModule? Your AppModule, the module declaring MyHighlightDirective, the module containing the component using MyHighlightDirective or some other shared module that's just exporting?Towrope
MyHighlightDirective is importing it, and the module which has the input in its template is also importing it.Headlight
any chance you're using lazy loading?Towrope
We are using it, yes.Headlight
oops, i'm an idiot - try importing ReactiveFormsModule insteadTowrope
Both MyHighlightModule and the module with the input are importing it.Headlight
Let us continue this discussion in chat.Towrope
Seems because of the specifics of my situation, using lazy loading and breaking the app by modules, this wasn't possible. But thanks for the answer and help! It seems it would be possible without the added complexity of how my app is structured.Headlight
using FormControl didn't work for me either but NgControl did, as illustrated in this answer: https://mcmap.net/q/1813668/-angular-updatevalueandvalidity-from-directiveSuzy
E
6

Inject NgControl which is type of FormControlName

import { NgControl } from '@angular/forms';

@Directive({selector: '[myHighlight]'})
export class MyHighlightDirective {
  constructor (private el: ElementRef, private formControl: NgControl) {
    this.el.nativeElement.style.backgroundColor = 'yellow';
    ....
    //listen to validation status
    this.formControl.statusChanges.subscribe((state)=>{
       ...
    });
  }
};
Engracia answered 27/4, 2017 at 6:34 Comment(0)
T
0

What worked for me was

@ContentChild(NgModel)
public set model(model: NgModel) {
    ...
}

and you can access the control with model.control.

Tripping answered 4/12, 2018 at 14:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.