3

If you delete the initial text that is in the input element in the example below, then it will not show an error until you blur the element.

I want to have the input element underline turn red and the error show immediately when you delete the last character in the input.

Any ideas somebody?

var app = angular.module('myApp', ['ngMaterial', 'ngMessages']);

app.controller('DemoCtrl', function() {
  this.name = 'test text';
});
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Angular JS</title>
  <link
    rel="stylesheet"
    href="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.6/angular-material.min.css">
</head>
<body ng-app="myApp">
  <div ng-controller="DemoCtrl as demo">
    <form name="testForm">
      <md-input-container>
        <input
          name="testInput"
          ng-model="demo.name"
          required
          aria-label="test input"
          type="text">
        <ng-messages
          for="testForm.testInput.$error"
          role="alert">
          <ng-message when="required">
            <span>
              required
            </span>
          </ng-message>
        </ng-messages>
      </md-input-container>
    </form>
  </div>
  
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular-animate.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular-aria.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular-messages.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.6/angular-material.min.js"></script>
</body>
</html>
Pac0
  • 21,465
  • 8
  • 65
  • 74
Sámal Rasmussen
  • 2,887
  • 35
  • 36
  • this seems to be a quirk with `md-input-container`; Removing the `md-input-containter` shows the `ng-message` operating normally. I am trying to research the problem but I don't work with Material usually. – Claies Mar 05 '18 at 13:34
  • Don't hold your breath for a non-ugly fix. Unfortunately Angular Material 1 is in a state of woeful neglect. – Sámal Rasmussen Mar 05 '18 at 13:44
  • I never give up hope :P. I found the answer, and posted a way to solve this using Angular Material. – Claies Mar 05 '18 at 14:01
  • Would you look at that :) – Sámal Rasmussen Mar 05 '18 at 14:05
  • @Sammi the answer you marked as right doesn't make any sense. Please read the documentation of `md-is-error` -https://material.angularjs.org/latest/api/directive/mdInputContainer. It's zero effect solution because using `testForm.testInput.$invalid` as expression has zero effect here. On the other hand it adds a new directive to your application which can be avoided due to performance boosts. – lin Mar 05 '18 at 14:34
  • Well it works, so there's that. Also the way I am reading the documentation I see it saying that the default for md-is-error is `testForm.testInput.$touched && testForm.testInput.$invalid`. – Sámal Rasmussen Mar 05 '18 at 16:28
  • @Sammi, yep thats the point. Using `$touched` && `$invalid` as `md-is-error` directive is the same like only using `ng-change`. Because: You need to "touch" the input by using it / typing + it becomes valid / invalid while using. So, there is no need to use `md-is-error` here because it's exactly the same. No need to have an extra directive here. The effect of using `md-is-error` is = zero :) ... But well, if it works for you, use it. =) – lin Mar 05 '18 at 23:23
  • No there is an effect. Seriously have run Claies's example? *IT WORKS* The reason it works as far as I can tell is because the default of md-is-error is `$touched && $invalid` and if you change it to just `$invalid` then it will show the error before you blur the input, because `$touched` means that the input needs to have been blurred at least once before you can see an error. – Sámal Rasmussen Mar 06 '18 at 08:45
  • @lin All that, and the fact that the example you are using with `ng-change` is introducing something else; `setSubmitted`. setting the form to submitted is a pretty drastic step, and setting the form submitted after every change is really wacky; There are lots of other things that might happen in the background in response to that, including possibly triggering server calls, none of which are really helpful to the problem. – Claies Mar 07 '18 at 08:58
  • @Claies yea ... but your solution also uses `ng-change="testForm.$setSubmitted()"`. – lin Mar 07 '18 at 09:45
  • @Claies =) m8 ........ – lin Mar 07 '18 at 09:56

2 Answers2

4

This is a quirk with the way the animations are triggered within an md-input-container. Angular Material has a flag that allows you to change when the input is checked for errors, to minimize the animation loop. The defaults are a bit too restrictive, but can be changed.

md-input-container has an optional flag that can be added: md-is-error. This allows you to pass an expression to control when the input is checked for errors.

try this: <md-input-container md-is-error="testForm.testInput.$invalid">

var app = angular.module('myApp', ['ngMaterial', 'ngMessages']);

app.controller('DemoCtrl', function() {
  this.name = 'test text';
});
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Angular JS</title>
  <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.6/angular-material.min.css">
</head>
<body ng-app="myApp">
  <div ng-controller="DemoCtrl as demo">
    <form name="testForm">
      <md-input-container md-is-error="testForm.testInput.$invalid">
        <input name="testInput"
               ng-model="demo.name"
               required
               aria-label="test input"
               type="text">
        <ng-messages for="testForm.testInput.$error" role="alert">
          <ng-message when="required">
            <span>
              required
            </span>
          </ng-message>
        </ng-messages>
      </md-input-container>
    </form>
  </div>
  
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular-animate.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular-aria.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular-messages.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.6/angular-material.min.js"></script>
</body>
</html>
Claies
  • 22,124
  • 4
  • 53
  • 77
  • This doesn't make any difference at all. This also does not minimize the "animation" loop since there will be no class changes applied when `$valid` / `$invalid` hasn't changed, even in my solution in here. Also `testForm.testInput.$invalid` / `md-is-error` directive will be a part of the digest cycle which will be triggered every time `ng-change` triggers. IMO this approach will raise the "animation" loop because one more directive (in this case `md-is-error`) is a part of the digest cycle. – lin Mar 05 '18 at 14:19
  • Using `testForm.testInput.$invalid` as expression on the same input container has zero effect. – lin Mar 05 '18 at 14:36
  • @lin We seem to be on different pages here. The directive **does have an impact**. The default for `md-container-input` is *both* `$touched` (blur) and `$invalid`, and if either of these are not true, then the error animations are suppressed and the errors are not passed out to the children (like ng-messages). changing to strictly `$invalid` means that the errors are passed down if they happen after every change, rather than only on blur. – Claies Mar 05 '18 at 16:43
  • @lin after editing the code and removing the `ng-change`, I confirmed that my answer still works. I knew that I had tested it, but I had just copied the wrong code into my answer when I posted the solution. – Claies Mar 07 '18 at 12:48
3

One way to achieve this is by adding ng-change="testForm.$setSubmitted()" to your inputs. This will directly trigger the form validation on change. While using testForm.$setSubmitted() on ng-change your need to check your dependend functions. Maybe some other behaviors inside your application could be effected.

<input name="testInput"
       ng-model="demo.name"
       ng-change="testForm.$setSubmitted()"
       required
       aria-label="test input"
       type="text">

Example:

var app = angular.module('myApp', ['ngMaterial', 'ngMessages']);

app.controller('DemoCtrl', function() {
  this.name = 'test text';
});
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Angular JS</title>
  <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.6/angular-material.min.css">
</head>
<body ng-app="myApp">
  <div ng-controller="DemoCtrl as demo">
    <form name="testForm">
      <md-input-container>
        <input name="testInput"
               ng-model="demo.name"
               required
               aria-label="test input"
               ng-change="testForm.$setSubmitted()"
               type="text">
        <ng-messages for="testForm.testInput.$error" role="alert">
          <ng-message when="required">
            <span>
              required
            </span>
          </ng-message>
        </ng-messages>
      </md-input-container>
    </form>
  </div>
  
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular-animate.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular-aria.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular-messages.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.6/angular-material.min.js"></script>
</body>
</html>
lin
  • 17,956
  • 4
  • 59
  • 83
  • while this works, this is a pretty ugly workaround, especially if you have other elements in the form that rely upon the submitted state of the form. The problem isn't really with the state of the form, it's with how `md-input-container` handles the validation of the input contained within; it appears to suppress the changes until blur for animation reasons. – Claies Mar 05 '18 at 13:39