1

I have a built a model-driven (reactive) form, as shown here, in Angular 2.

My html looks like this:

<form [formGroup]="userForm" (ngSubmit)="onSubmit(userForm.value, userForm.valid)">
    <label for="firstName">First Name:</label>
    <input type="text" formControlName="firstname" id="firstName" required>

    <label for="lastname">Last Name:</label>
    <input type="text" formControlName="lastname" id="lastName" required>

    <br>

    <label for="email">Email:</label>
    <input type="email" formControlName="email" id="email">

    <br>
</form>

In my .ts file:

import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms';
...

ngOnInit() {
    this.paymentForm = this.formBuilder.group({
        firstname: ['', Validators.required],
        lastname: ['', Validators.required],
        email: ['',],


    })

    this.userForm.valueChanges.subscribe(value => {
        console.log(value);           
    });
}

I've added the required attribute in my template as well, as suggested by angular docs

Quoting:

required remains, not for validation purposes (we'll cover that in the code), but rather for css styling and accessibility.

What I want is to cycle through each form field and add a * to the associated label if the field is required.

So, First Name reads First Name *; and so on.

How would I go about doing that. Thanks.

Snowman
  • 2,465
  • 6
  • 21
  • 32
  • What do you mean with "if the field is required"? Do you still want or plan to keep the `required` attribute, or should it be removed? – Günter Zöchbauer Feb 21 '17 at 07:38
  • @GünterZöchbauer: I still plan to keep the attribute. I just want the labels to be updated. Basically, If I have 10 fields and 8 are required, I want those 8 fields' labels to be edited automatically, rather than having to manually add/remove the asterisks. – Snowman Feb 21 '17 at 07:50

1 Answers1

1
@Directive({
  selector: '[required]'
})
export class LabelRequiredDirective {
  constructor(private elRef:ElementRef){}

  ngAfterContentInit() {
    this.elRef.nativeElement.labels.forEach(l => l.textContent += ' *');
  }
}

Because the selector matches every element that has the required attribute, it is applied to all elements where the label should be updated.

Sadly nativeElement.labels is only supported in Chrome. For other browsers another strategy is necessary to get the label associated with an input element (See also Find html label associated with a given input)

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    Thanks for answering. I'll try it out and let you know. – Snowman Feb 21 '17 at 07:50
  • I'm not able to get this to work. I've created a new file, put the code in it; included it in my app.module declarations. Since there is no custom name involved, I haven't edited my form template. Do I need to? (Btw, I fixed some TS errors I was getting by saying `export class LabelRequiredDirective` and `this.elRef.nativeElement`. I hope that was okay.) – Snowman Feb 21 '17 at 09:20
  • Sounds fine. Have you checked if the constructor and `ngAfterContentInit` are called? – Günter Zöchbauer Feb 21 '17 at 09:25
  • Yes, they're being called. `this.elRef.nativeElement` returns the element as expected; but the labels array is empty. I'm checking in the latest chrome. Anyway, I get the gist now. I can take it from here. Thanks for your help. – Snowman Feb 21 '17 at 09:32
  • Works for me https://plnkr.co/edit/CeuUpAxjzT1lAFiU829L?p=preview. `for="lastname"` didn't work, because `id="lastName"` differs in uppercase `N`. It adds ` *` though. Didn't spend too much time in reasoning about that. You could also just add a CSS class using `@HostBinding('class.required') isRequired = true; and add the `*` using CSS (for example using content). – Günter Zöchbauer Feb 21 '17 at 09:49
  • Cool. Thanks again. – Snowman Feb 21 '17 at 09:53