danday74
danday74

Reputation: 56966

Form label and form input in separate components

I have a form label in one component and its associated reusable custom input in a separate component. I have designed it like this because sometimes I want a label with the custom input and sometimes I don't. Sometimes I want the label to be above the custom input and sometimes I want it inline with the custom input. Thus separate components makes sense. It looks something like this (although the markup in the parent component may vary):

component1.component.html:

<label for="myInput">Label Text</label>
<app-component2></app-component2>

component2.component.html

<input id="myInput" ... loads of custom stuff here>

The only problem is that the label for and input id does not work. When I click the label it does not select the input when the input is in another component. How can I make it work?

Thanks

Upvotes: 1

Views: 914

Answers (3)

LHSnow
LHSnow

Reputation: 1266

You can relay the id attribute by reading the id as an @Input, rendering it to the form element in the template, and then removing it from the host element (as html ids need to be unique) once the template is initiated.

export class Component2 implements AfterContentInit {
  @Input() id: string;

  constructor(
    private readonly _elementRef: ElementRef,
    private readonly _renderer: Renderer2
  ) {}

  ngAfterContentInit(): void {
    // or rewrite it to another non-unique attribute
    this._renderer.removeAttribute(this._elementRef.nativeElement, 'id');
  }
}

And in the Component2 template

<input id="{{id}}">

This allows Component1 to use the selector of Component2 as if it was an input element.

<label for="myInput">Label Text</label>
<app-component2 id="myInput"></app-component2>

Upvotes: 1

danday74
danday74

Reputation: 56966

I got this working by doing this:

<label>
  <span>Label Text</span>
  <app-component2></app-component2>
</label>

where app-component2 contains the input element.

Upvotes: 1

hardik agarwal
hardik agarwal

Reputation: 47

You can wrap up both the components into a parent component and can pass the required id by emitting a event by on click of the label into the

Your code will be like

Parentcomponent.html

<parentComponent>
<component1 (emitId) ='setLabelId($event)'></component1>
<component2 [labelId]='labelId'></component2>
</parentComponent>

and parentComponent.ts will be

 export class parentComponent implements OnInit {
 const labelId : string;

  constructor() { }

  ngOnInit() {

  }

  setLabelId(id:string){
  this.labelId = id;
  }

  }

and component1.ts will be like

 export class Component1 implements OnInit {
 @Output() emitId = new EventEmitter<string>();

  constructor() { }

  ngOnInit() {

  }

handleLabelClick(id:string){
this.emitId.emit(id);
}

}

component1.html

<label onClick="handleLabelClick(myInput)" for="myInput">

where component2.ts will be like

 export class Component2 implements OnInit {
 @Input() labelId:string

  constructor() { }

  ngOnInit() {
    // this.labelId holds the value of required id
  }

}

and component2.html

<input id="myInput" ... loads of custom stuff here>

Upvotes: 1

Related Questions