Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MdInput doesn't set required asterisk when the validator is set in reactive forms #2574

Closed
bboehm86 opened this issue Jan 9, 2017 · 59 comments · Fixed by #23362
Closed

MdInput doesn't set required asterisk when the validator is set in reactive forms #2574

bboehm86 opened this issue Jan 9, 2017 · 59 comments · Fixed by #23362
Assignees
Labels
area: material/input forms This issue is related to Angular Forms integration P2 The issue is important to a large percentage of users, with a workaround

Comments

@bboehm86
Copy link

bboehm86 commented Jan 9, 2017

Bug, feature request, or proposal:

The placeholder from the MdInput does not show the asterisk (*) when we use Validators.required in the FormControl.

What is the expected behavior?

The asterisk should be shown as it is when setting the requiredattribute.

What are the steps to reproduce?

http://plnkr.co/edit/XqkOmF502Q8RFj8qcUwp?p=preview
or take a look at the input-container-demo

What is the use-case or motivation for changing an existing behavior?

To be not dependet on the template to visually indicate that the input is required.

Which versions of Angular, Material, OS, browsers are affected?

all (to my knowlege it never worked)

@fxck
Copy link
Contributor

fxck commented Jan 9, 2017

Likely the same problem as the one that this PR fixes #2565

@bboehm86
Copy link
Author

bboehm86 commented Jan 9, 2017

Probably not exactly the same because the control does not have a required state since it is a validator (other than in the disabled case)

@fxck
Copy link
Contributor

fxck commented Jan 9, 2017

Yeah but the underlying problem seems to be the same, only checking the existence of the required input, instead of checking whether required validator exists(which I think will be a little tricky).

@fxck

This comment has been minimized.

@bboehm86
Copy link
Author

While you are at it... the md-select seems to have the same issue (plnkr). Should i open another ticket for that?

@kara
Copy link
Contributor

kara commented Jan 10, 2017

Currently there isn't a great way to determine if Validators.required has been added to the control because form validators are combined into an aggregate function at setup. I think the correct solution to this might be to add a better way to retrieve individual validators in core @angular/forms.

In the meantime, you'll have to use the required property binding when using md-select or md-input with reactive forms. You'll probably want to use it with the Validators.required to avoid changed after checked errors.

I'll keep this open so we can track, but I think the change will have to be in core first.

@bboehm86
Copy link
Author

@kara thanks for the info. I guess we'll have to wait then :-)

..and Kara is right, there seems not to be a great way to get a hold of the Validators.required (or any other validator).. Actually I only found one, but for anyone who rly needs to get a hold of its existance of the required you can do smth like:

myControl = new FormControl('', Validators.required);
validationResult = myControl.validator(new FormControl); // will be {required: true}

@calbot
Copy link

calbot commented Apr 13, 2017

Using the required attribute doesn't work well if the input is inside an ngIf I have found. The core forms automatically adds the required validator to the control when the required attribute is used on the input but it doesn't go away once the input is no longer in the dom. Maybe that's a separate issue for the core side not cleaning up when an element is removed from the dom. This leads to a form appearing invalid when it shouldn't be.

@jelbourn jelbourn added P4 A relatively minor issue that is not relevant to core functions forms This issue is related to Angular Forms integration labels Jun 6, 2017
@eltonplima
Copy link

I'm using this solution:

<input formControlName="name" [required]="formGroup.get('name').errors !== null && formGroup.get('name').errors.required">

@luisfpg
Copy link

luisfpg commented Dec 21, 2017

Knowing if the required validation was added or not doesn't seems like something that core angular is going to support, because validators are just functions.
It is even possible to write a custom validation function that conditionally checks required, such as if (something) return Validators.required(control) else null.
So, I would like to suggest adding a new attribute such as matRequired, which just visually add the asterisk, but doesn't touch the validators.
To be honest, this is the only solution I can think on this case, given that angular is unlike to support the requirement.
This was proposed in #4090, but it was closed as a dupe of the current issue, but they are, in fact, different things. I'm suggesting in this one because it is the currently open issue.

@Toub
Copy link

Toub commented Jan 16, 2018

Related to angular/angular#13461

@jelbourn jelbourn assigned mmalerba and unassigned kara Jan 16, 2018
@luisfpg
Copy link

luisfpg commented Feb 2, 2018

Simply add <input ....... [required]="true" />

@ajaysattikar That would be template-driven forms. This whole discussion is about reactive forms.

@luisfpg
Copy link

luisfpg commented Mar 2, 2018

@isherwood Yes, sorry. Edited to avoid confusion.

@fxck
Copy link
Contributor

fxck commented Mar 26, 2018

@eltonplima except that it disappears as soon as the form is valid, which means that it would cause problems with for example the outline appearance, along with missing validChanges on form controls this is nothing that can be easily dealt with.. cc @mmalerba

edit: nevermind, you can use status changes with filter to get validity change

edit2: ok, that's not gonna work, what I would need is an observable array of errors / validity states......... which of course you can't get.... angular/angular#10530 ffs...

@Antoniossss

This comment has been minimized.

@niccolofanton
Copy link

Hi All, I'm experiencing the same issue using Control Value Accessor with a material autocomplete, for now the only solution is to apply the above directive mentioned by @sambaptista ?

Yes

@BruneXX
Copy link

BruneXX commented Sep 25, 2020

I think my issue is a bit different... Material/ReactiveForm is completely ignoring the required validation when I'm using the CVA so also I'm passing errorStateMatcher but still not work... The input is not getting red borders and neither the asterisk, it's like is a not required input.. any suggestion to deal with that?

@mackelito
Copy link

@BruneXX have you checked so that your control is correct? does {{ yourControl.errors }} contain an object?

@BruneXX
Copy link

BruneXX commented Sep 28, 2020

Hi @mackelito yep, it already has an object and it's correct, but I've already solved that, subscribing it to statusChanges of the parent form, the sad thing is that I've to pass the parent fromGroup in order to subscribe, unfortunately I haven't found other way to make it work.

@mackelito
Copy link

This is exactly what I hope to avoid 😬

@BruneXX
Copy link

BruneXX commented Sep 28, 2020

@mackelito haha me too, but sadly I don't have too much time to review that.. I had to go with that approach until found another way.

@mackelito
Copy link

@BruneXX I have been trying on and off for like 2 days now.. not sure I can put more time on it 😄

@mackelito
Copy link

@BruneXX Wohoo.. I just solved my issue.. almost to simple.. I just needed to add the [formControl]...
[formControl]="ngControl"

  constructor(
    @Optional() @Self() public ngControl: NgControl,
  ) {
    if (this.ngControl != null) {
      // Setting the value accessor directly (instead of using the providers) to avoid running into a circular import.
      this.ngControl.valueAccessor = this;
    }
  }

So only thing missing was to attach the formcontrol to the input.. Badabom badabing! :D
Hope it helps you as well..

@BruneXX
Copy link

BruneXX commented Sep 28, 2020

@mackelito Excellent! 👍 I've tried something like that some days ago, but I've ran into the circular dependency like you said in your code comment and I've leaved as it was, maybe I'll give this a second try later. Thank you!

@mackelito
Copy link

@BruneXX sounds like you are using the provider for NG_VALUE_ACCESSOR?.. if so.. remove it :)

@mackelito
Copy link

FYI.. it works but another error in the console...

ERROR TypeError: Cannot set property validator of [object Object] which has only a getter

Not sure yet what it referes to :)

@Totati
Copy link
Contributor

Totati commented Sep 28, 2020

Hi, this is my long used solution, It handles validator changes too.

2 directives handling change

@Directive({ selector: '[matInput][formControl]:not([required]), [matInput][formControlName]:not([required])' })
export class InnoRequiredMatInput implements DoCheck {
  constructor(private readonly input: MatInput) { }

  ngDoCheck() {
    const isRequired = (this.input.ngControl?.control as any)?.isRequired ?? false;
    if(isRequired !== this.input.required){
      this.input.required = isRequired;
      this.input.ngOnChanges();
    }
  }
}

@Directive({ selector: 'mat-select[formControl]:not([required]), mat-select[formControlName]:not([required])' })
export class InnoRequiredMatSelect implements DoCheck {
  constructor(private readonly input: MatSelect) { }

  ngDoCheck() {
    const isRequired = (this.input.ngControl?.control as any)?.isRequired ?? false;
    if(isRequired !== this.input.required){
      this.input.required = isRequired;
    }
  }
}

2 monkey patches for adding extra logic to validation

const _clearValidators = AbstractControl.prototype.clearValidators;
AbstractControl.prototype.clearValidators = function(){
  (this as any).isRequired = false;
  _clearValidators.call(this);
}

const _setValidators = AbstractControl.prototype.setValidators;
AbstractControl.prototype.setValidators = function(newValidator: ValidatorFn | ValidatorFn[] | null): void {
  (this as any).isRequired = false;
  _setValidators.call(this, newValidator);
}

Custom required validator that uses Validators.required inside

export class MatValidators{
  static required(control: AbstractControl): ValidationErrors | null{
    (control as any).isRequired = true;
    return  Validators.required(control);
  }
} 

Example

@mrtabaa
Copy link

mrtabaa commented Dec 31, 2020

Simply add <input ....... [required]="true" />

@ajaysattikar That would be template-driven forms. This whole discussion is about reactive forms.

This solution worked perfectly along with formControlName. Thanks!

@mmalerba
Copy link
Contributor

Now that angular/angular#42838 has landed we should be able to implement this

@mmalerba mmalerba added P2 The issue is important to a large percentage of users, with a workaround and removed P4 A relatively minor issue that is not relevant to core functions blocked This issue is blocked by some external factor, such as a prerequisite PR labels Jul 22, 2021
crisbeto added a commit to crisbeto/material2 that referenced this issue Aug 13, 2021
…ator

Resolves the long-standing issue where the required asterisk wasn't being shown in the form field label when using the `required` validator from Forms.

Fixes angular#2574.
@crisbeto crisbeto self-assigned this Aug 13, 2021
crisbeto added a commit to crisbeto/material2 that referenced this issue Aug 13, 2021
…ator

Resolves the long-standing issue where the required asterisk wasn't being shown in the form field label when using the `required` validator from Forms.

Fixes angular#2574.
mmalerba pushed a commit that referenced this issue Oct 12, 2021
…ator (#23362)

Resolves the long-standing issue where the required asterisk wasn't being shown in the form field label when using the `required` validator from Forms.

Fixes #2574.
mmalerba pushed a commit that referenced this issue Oct 12, 2021
…ator (#23362)

Resolves the long-standing issue where the required asterisk wasn't being shown in the form field label when using the `required` validator from Forms.

Fixes #2574.

(cherry picked from commit 69029b4)
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Nov 12, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: material/input forms This issue is related to Angular Forms integration P2 The issue is important to a large percentage of users, with a workaround
Projects
None yet
Development

Successfully merging a pull request may close this issue.