Smaller Work around for ng-pattern on Select

1.8k Views Asked by At

I am working on an angular app and I needed to put a pattern validation on a select element. Just using ng-pattern on select didn't work. So I created a hidden input with same model with ng-pattern on the hidden input, that didn't work either. So I created a text input with the ng-pattern and hid it via css. Which works beautifully.

Is there a smaller work around for this?

EDIT1: I think I should have added that the options are generated by ng-options

EDIT2: Edited the code snippet accordingly to show what I actually want.

function formCtrl($scope) {
  $scope.options = ['Please Select','Value 1','Value 2'];
  $scope.price = 'Please Select';
    $scope.onSubmit = function (form) {
        console.log(form.$valid);
    }
}
.show-none {
    display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-controller="formCtrl">
    <form name="myForm" ng-submit="onSubmit(myForm)">
        <select ng-model="price" ng-options="o as o for o in options"></select>
        <input type="text" class="show-none" ng-pattern="/^(?!.*(Please Select))/" ng-model="price" name="price_field"> <span ng-show="myForm.price_field.$error.pattern">Not a valid option!</span>

        <input type="submit" value="submit" />
    </form>
</div>

3

There are 3 best solutions below

9
On BEST ANSWER

You shouldn't use ng-pattern with hidden input field for this case, you should add value for each option, & then you should have required attribute make sure any of the option should selected before submiting. Adding name="price" will add form element to myForm object.

Markup

<select ng-model="price" name="price" required>
     <option>Please Select</option>
     <option value="TEST1">TEST1</option>
     <option value="TEST2">TEST2</option>
</select>

Update

If you wanted to make it using ng-options then it would be something like below. You don't need to add Please Select in your array, you could add it manually inside select

Markup

<form name="myForm" ng-submit="onSubmit(myForm)" novalidate>
  <select ng-model="price" ng-options="o for o in options" required>
    <option value="">Please select a person</option>
  </select>

  <input type="submit" value="submit" />
</form>

Code

(function(angular) {
  'use strict';
  angular.module('staticSelect', [])
    .controller('formCtrl', ['$scope', function formCtrl($scope) {
      $scope.options = ['Value 1', 'Value 2'];

      $scope.onSubmit = function(form) {
        console.log(form.$valid);
      }
    }]);
})(window.angular);

Demo here

3
On

You can also just disable certain options if you didn't want it to be possible to select them in the first place:

  <select ng-model="price">
    <option disabled>Please Select</option>
    <option>TEST1</option>
    <option>TEST2</option>
  </select>
0
On

Chiming in here as this is something that comes up time and time again and the suggested solutions here don't work for me. This is a really common use-case too.

I tried putting an empty option in, and it didn't work for me. I noticed that AngularJS was placing a value attribute in with the same value as the contents of the <option> tag. The select was ng-valid even with this option selected.

In my scenario I have placed the placeholder option i.e. SELECT A CENTRE as the first item in the list of locations (the scope variable I bind the options to). I hide the first item with conditional CSS, but select it initially by setting dc.user.CentreName to SELECT A CENTRE in the controller. This gives us the behaviour of it disappearing when the user has clicked any other option. Another option is to disable it which means it will still show at the top of the list but will be unselectable after selecting another item (was added in Angular 1.4x). I have also put that code in (the ng-disabled attribute), to illustrate this. You may safely remove it if you want to go the hidden route.

Next we want to use the standard Angular forms validation to ensure that some item other than the first is selected. I tried the other suggested approaches of putting in an empty option (with no value) and setting required on the select. So I went for a hidden input form element with a required and ng-pattern attribute using a negative lookahead assertion.

Note that we use ng-class to bind ng-valid and ng-invalid to the select so existing CSS styling will work.

Feels a bit hacky but it works.

<select ng-model="dc.user.CentreName"
        ng-class="{'ng-valid': orderForm.centreName.$valid, 'ng-invalid': !orderForm.centreName.$valid }">
    <option ng-repeat="location in locations"
            ng-disabled="$index < 1"
            ng-style="$index < 1 && {'display': 'none'} "
            value="{{location}}">
        {{location}}
    </option>
</select>

<input
    type="hidden"
    ng-model="dc.user.CentreName"
    ng-pattern="/^(?!SELECT)/"
    required/>

And in the controller... (note you can also use ng-init to set the placeholder value declaratively from the view, see https://stackoverflow.com/a/13775151/4707368)

function ShoppingController($scope [...]) {

   [...]

    $scope.dc = {
        user: {
            "CentreName": 'SELECT A CENTRE',
   [...]

Your CSS might look like this:

input.ng-touched.ng-invalid, select.ng-invalid
{
    background-color: rgba(199, 35, 35, 0.15);
}

input.ng-touched.ng-valid, select.ng-valid
{
   background-color: rgba(116, 173, 39, 0.16);
}