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

feat(AbstractControlDirective): implement hasError() and getError() #7255

Closed
0x-r4bbit opened this issue Feb 24, 2016 · 6 comments · Fixed by #11985
Closed

feat(AbstractControlDirective): implement hasError() and getError() #7255

0x-r4bbit opened this issue Feb 24, 2016 · 6 comments · Fixed by #11985
Labels
area: forms feature Issue that requests a new feature

Comments

@0x-r4bbit
Copy link
Contributor

Due to #7238 I realised, that when building template-driven forms, we don't have APIs such as hasError() and getError() on control instances.

E.g. having a tiny form like this:

<form>
  <input ngControl="name" #name="ngForm" minlength="3">
  <p *ngIf="name.errors.minlength">Too short!</p>
</form>

We always have to walk down the properties of ngControl instance to access the error type.

However, when this same form is built model-driven, all of a sudden we have additional APIs:

<form [ngFormModel]="someFormModel">
  <input ngControl="name" #name="ngForm" minlength="3">
  <p *ngIf="name.hasError('minlength')">Too short!</p>
</form>

What makes the difference, even though we seem to work with the same instance?

Well, when building model-driven forms, the form model is built in the component and then associated to a form in the DOM using [ngFormModel]. The model consists of Control and ControlGroup and it happens that Control extends AbstractControl which implements hasError() and getError().

We don't have this with template-driven forms, as we're only using the NgControlName directive, which extends NgControl which extends AbstractControlDirective, and that one doesn't implement hasError() or getError().

I'd expect the same APIs in the template no matter if I'm building template-driven forms or model-driven forms. So I propose extending AbstractControlDirective accordingly, as it has access to its Control.

Does that make sense?

@0x-r4bbit
Copy link
Contributor Author

UPDATE

Turns out, ngControl does have a handle to its actual Control instance via

class NgControlName {
...
get control(): Control { return this.formDirective.getControl(this); }
}

However, that one doesn't help either, as the control itself is not registered yet at the point we're accessing it from the template:

<input ngControl="name" #name="ngForm">
<p *ngIf="name.control.hasError('minLength')">...</p>

The control gets only registered the very first time the value of this control changes.

@0x-r4bbit
Copy link
Contributor Author

UPDATE

However, that one doesn't help either, as the control itself is not registered yet at the point we're accessing it from the template. The control gets only registered the very first time the value of this control changes.

Realised I can use the elvis operator to make this work (as this is what we use for expressions that evaluated asynchronously):

<input ngControl="name" #name="ngForm">
<p *ngIf="name.control?.hasError('minLength')">...</p>

However, I still think, that both strategies, template-driven and model-driven, should provide the same API in the template.

@0x-r4bbit
Copy link
Contributor Author

Further more, it'd be nice to have an API that tells if there are errors at all, so we can do sth. like:

<div *ngIf="controlInstance.hasErrors()">
  <!-- only now check if specific error messages are needed -->
</div>

This could also be done by extending hasError() to work without any argument (currently it requires a string for the error type).

hasError(errorCode: string = null, ...) {
  if (!errorCode) { return isEmpty(this.errors); }
  return isPresent(this.getError(errorCode, path));
}

@wardbell
Copy link
Contributor

it'd be nice to have an API that tells if there are errors at all

I'm confused. We have valid. Isn't that the same thing?

From the plunker in the Forms chapter

<input type="text" class="form-control" required
   [(ngModel)]="model.name"
   ngControl="name"  #name="ngForm" >
<div [hidden]="name.valid || name.pristine" class="alert alert-danger">
  Name is required
</div>

That could have been

 <div *ngIf="!name.valid && !name.pristine" class="alert alert-danger">
   Name is required
 </div>

The valid property isn't precisely the same but I rather doubt you'll find a time when you need the difference.

@wardbell
Copy link
Contributor

I do like the idea of sugaring AbstractControlDirective with getError and hasError methods.

I think it's as simple as adding:

getError(errorCode: string): any {
  return isPresent(this.control) ? this.control.getError(errorCode, this.path) : null;
}

hasError(errorCode: string): boolean {
  return isPresent(this.getError(errorCode, this.path));
}

That's consistent with its errors property

get errors(): {[key: string]: any} {
  return isPresent(this.control) ? this.control.errors : null;
}

@pkozlowski-opensource pkozlowski-opensource added feature Issue that requests a new feature area: forms labels Sep 1, 2016
cexbrayat added a commit to cexbrayat/angular that referenced this issue Sep 29, 2016
Allows cleaner expressions in template-driven forms.

Before:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.control.hasError('required')">Username is required</div>

After:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.hasError('required')">Username is required</div>

Fixes angular#7255
cexbrayat added a commit to cexbrayat/angular that referenced this issue Sep 29, 2016
Allows cleaner expressions in template-driven forms.

Before:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.control.hasError('required')">Username is required</div>

After:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.hasError('required')">Username is required</div>

Fixes angular#7255
cexbrayat added a commit to cexbrayat/angular that referenced this issue Sep 29, 2016
Allows cleaner expressions in template-driven forms.

Before:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.control.hasError('required')">Username is required</div>

After:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.hasError('required')">Username is required</div>

Fixes angular#7255
cexbrayat added a commit to cexbrayat/angular that referenced this issue Oct 10, 2016
Allows cleaner expressions in template-driven forms.

Before:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.control.hasError('required')">Username is required</div>

After:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.hasError('required')">Username is required</div>

Fixes angular#7255
cexbrayat added a commit to cexbrayat/angular that referenced this issue Oct 10, 2016
Allows cleaner expressions in template-driven forms.

Before:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.control.hasError('required')">Username is required</div>

After:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.hasError('required')">Username is required</div>

Fixes angular#7255
cexbrayat added a commit to cexbrayat/angular that referenced this issue Oct 13, 2016
Allows cleaner expressions in template-driven forms.

Before:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.control.hasError('required')">Username is required</div>

After:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.hasError('required')">Username is required</div>

Fixes angular#7255
alxhub pushed a commit that referenced this issue Oct 19, 2016
…11985)

Allows cleaner expressions in template-driven forms.

Before:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.control.hasError('required')">Username is required</div>

After:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.hasError('required')">Username is required</div>

Fixes #7255
btrigueiro pushed a commit to btrigueiro/angular that referenced this issue Oct 21, 2016
…ngular#11985)

Allows cleaner expressions in template-driven forms.

Before:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.control.hasError('required')">Username is required</div>

After:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.hasError('required')">Username is required</div>

Fixes angular#7255
@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 Sep 10, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: forms feature Issue that requests a new feature
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants