8

I'm using Angular 2.0 final release.

What i want to do? - i want to show some notifications (warnings, the requirements for the input) only when the input receive focus. Or even better, when his parent (the div) has mouse hover event.

Doing this from the parent(the div) is easy. Just (focus)=showNotifications() + an ngIf - and the job is done.

But i want to encapsulate this functionality inside the show-notifications component - and make it reusable..

Given that i pass the control inside the show-notifications using @Input() - i could do both this things if i have access to the native HTML input element.You can see how in show-notifications.component.ts. Here is the code:

app.component.html:

`<div>
   <label>Name: </label>
   <input formControlName="myName">
   <show-notifications [the_control]="myName" ></show-notifications>
</div>`

app.component.ts:

export class AppComponent {
    myName = new FormControl("defaultvalue",[some validators..])
}

show-notifications.component.html:

<div class="show_requirements" *ngIf="hasfocus or parentDivHasMouseHover"> // incorect code and logic - i know, but you can see the idea..

    <p *ngFor="let requirement of thisInputRequirements">{{requirement}}<p>

</div>

show-notifications.component.ts:

export class ShowNotificationsComponent {

    @Input() the_control;
    this.thisInputRequirements = // take them form firebase.
    this.thisInputCustomErrorMessages = // take them from firebase.

    // I implemented all this and works amazing but i want to do:

    //something like this:

    ngOnInit(){
        this.nativeInputElement = this.the_control.getNativeElement() // this of course doesn't work

        // the requirements are shown only when the input receive focus
        let source = Rx.DOM.focus(this.nativeInputElement);
        source.subscribe( x => this.hasFocus = true)

        // Or even better: 
        // the requirements are shown only when the parent has mouse hover
        // or any other event / endles posibilites here..

        let parent = getParentOf(this.nativeInputElement)
        let source = Rx.DOM.mouseover(parent);
        source.subscribe( x => this.parentDivHasMouseHover = true) // this will be some toggling functionality.
    }

}

Question:

How do i access the native element given that i have the formControl (myName = the_control) object?

Is there a better way to do notifications in a separate component - and make it reusable? I already use this successfully throughout my entire app - to show errors and input requirements..

Note: I tried to pass the hole html input first using hashtag syntax ( #myInput ) and form there, inside the show-notifications component, i tried to do: myInput.myName - to acces the control but i get undefined. The controls are not present on the native input element. And I need that control for validation:)

AIon
  • 10,703
  • 7
  • 39
  • 66
  • One option would be to create a custom form control. You could let the user pass in the HTML with a #input so you can bind it to the `ControlValueAccessor` field to be used. – Juan Mendes Mar 03 '17 at 14:34

3 Answers3

4

I think you could use # notation to pass the element to ShowNotificationsComponent:

export class ShowNotificationsComponent {
    @Input() the_control;
    @Input() elm;
    //...
}

Then in the template:

<input formControlName="myName" #elm>
<show-notifications [the_control]="myName" [elm]="elm"></show-notifications>
martin
  • 76,615
  • 21
  • 156
  • 193
  • 3
    Yes @Martin, thanks for the answer, I already did that, and works fine but still, i'm waiting for a solution that allows me to access the native element form inside the control. I can't believe they are not tied together in some way. Form the native input element i can't get the control. From the control i can't get the native element. How could this be? It doesn't sound right. I need to create all those # variables all over my code. Not clean. But yeah, works for now :) – AIon Sep 30 '16 at 09:24
  • 2
    Well, if you look into the source code for `FormControl` and `AbstractControl` it actually doesn't access the native element anywhere. See https://github.com/angular/angular/blob/2.1.0-beta.0/modules/%40angular/forms/src/model.ts#L76-L608 and https://github.com/angular/angular/blob/2.1.0-beta.0/modules/%40angular/forms/src/model.ts#L608-L798 – martin Sep 30 '16 at 09:34
  • So I'm not totally sure, but isn't the `name` attribute of the `nativeElement` what's being used in Template-Driven forms to name the FormControl in the FormGroup? If so (using your favorite DOM selector, I choose jQuery as it's what I know off the top of my head) do the following... `var element = $('[name={{ yourInputName }}]');` after which time you should be able to access it as a standard input form element. – Jarvis May 18 '17 at 19:28
  • @Alon did you ever find an Angular way to do this, as I also don't want to be plastering #name's all over my templates either just for a custom component to access the dom element? – rmcsharry Oct 18 '17 at 20:18
0

I found something that may be helpfull if you want to get some information from the Abstractcontrol as it was a normal Html Input Element, for example if you want to add a "Event Listener" to it, in order to know when user is interacting with the element, It could be done by using the valueChanges or statusChanges, both Observables from class AbstractControl, i used it to know when a element has changed and get the option selected to do more actions before the form is submited, I leave here my code example:

getValue() {
  this.myForm.get('myControlName').valueChanges.subscribe((optionValue) => {
    console.log(optionValue);
    this.doSomething(optionValue);
  });
}

And if you want to made some animation or changes when the user hover the parent maybe you can use some simple css trick like this:

.parent #child{
  opacity: 0;
}

.parent:hover #child{
  opacity: 1;
}

You can check this documentation for more details : https://angular.io/api/forms/AbstractControl#valueChanges

Mariel Quezada
  • 101
  • 2
  • 3
-3

This is very Basic. FormContrl is not required. you should create a local variable on input element with # and pass local variables to method

<div>
<label for="firstname">First Name</label>
<input name="fname" #nameFirst />
<label for="lastname">Last Name</label>
<input name="lname" #nameLast />
</div>
<button (click)="send(nameFirst , nameLast)">Submit</button>

export class HtmlExample {

    send(firstName: HTMLInputElement, lastName: HTMLInputElement): void {
        console.log(`Hello ${firstName.value}  ${lastName.value}`);
        firstName.value = '';
        lastName.value = '';
    }

}