1

I'm using AngularJs on my project and i've a property on my viewModel that is connected to a dropdown (< select >)

that dropdown have a empty value witch is selected by default, what i want is to prevent user to select that empty value after he select some other value.

ive started to look to $watch, but i dont know if there is some way to cancel the "changing oof that property", some thing like this:

$scope.$watch('myProp', function (newVal, oldVal, scope) {
    if (newVal) { scope.preventDefault(); }
}

any idea, this is the base idea, on a more advanced development i need to ask users for a confirmation.

any ideas?

Flavio CF Oliveira
  • 5,235
  • 13
  • 40
  • 63

4 Answers4

2

You can just add ng-required to the select. If there is no initial value to the model then an empty option will be added and on change to a valid value it will remove the empty option

EDITED jsFiddle to revert to previous value and to include the ng-change directive.

From the docs:

The expression is not evaluated when the value change is coming from the model.

This is useful in not interfering with change listeners and creating an infinite loop when reverting the old value in the $apply function

Controller

$scope.options = [{value: 'abc'},{value: 'def'}];

var confirmDialog = function(newVal, yes, no) {
    // obviously not a good way to ask for the user to confirm
    // replace this with a non blocking dialog

    //the timeout is only for the confirm since it's blocking the angular $digest
    setTimeout(function() {
        c = confirm('Is it ok? [' + newVal.value + ']');
        if(c) {
            yes();
        }
        else {
            no();
         }
    }, 0);
};

//Asking for confirmation example
function Ctrl($scope) {
    $scope.options = [{value: 'abc'},{value: 'def'}];

    $scope.select = undefined;
    var oldSelect = undefined;
    $scope.confirmChange = function(select) {
        if(oldSelect) {
            confirmDialog(select,
                 function() {
                    oldSelect = select;
                 },
                 function() {
                    $scope.$apply(function() {$scope.select = oldSelect;});
                });
        }
        else {
            oldSelect = select;
        }
    }
}

Template

<div ng-controller="Ctrl">
  <select ng-model="select" ng-options="o.value for o in options"
          ng-required ng-change="confirmChange(select)">
  </select>
</div>
georgeawg
  • 48,608
  • 13
  • 72
  • 95
Liviu T.
  • 23,584
  • 10
  • 62
  • 58
2

what i want is to prevent user to select that empty value after he select some other value

This should happen automatically for you, as long as you don't assign the ng-model property a value initially. So using the <select> shown below, don't initialize $scope.selected_year in your controller:

<select ng-model="selected_year" ng-options="year for year in years"></select>

When the list displays initially, Angular will have added an option like this to the HTML, since $scope.selected_year is not currently set to a valid option/value:

<option value="?" selected="selected"></option>

After selecting a valid choice, that option will magically disappear, so the user will not be able to select it again. Try it in this fiddle.

If the ng-model property already has a valid value assigned when the select list is first displayed, then you can assign a controller function to the undocumented ng-change parameter:

<select ... ng-change="preventUserFromDoingXzy()">

Inside function preventUserFromDoingXzy() you can do what you need to do to control what the user can select, or modify the model.

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
0

Probably the easiest, cleanest thing to do would be adding an initial option and setting disabled on it:

<option value="?" selected="selected" disabled></option>
Stiggler
  • 2,800
  • 20
  • 21
-1

Actually, it is easier to remove the empty value. Suppose you have a list of options:

$scope.options = [{value: ''}, {value: 'abc'},{value: 'def'}];

and a select:

<select ng-model="select" ng-options="o.value for o in options"></select>

Then $watch the model:

$scope.$watch('select', function(value) {
    if (value && value !== '') {
        if ($scope.options[0].value === '') {
            $scope.options = $scope.options.slice(1);
        }
    }
}, true);

See it in action here.

PS Don't forget the objectEquality parameter in the $watch or it won't work!

asgoth
  • 35,552
  • 12
  • 89
  • 98
  • In the fiddle it seems ng-options already adds an empty option, so there is no need to add one manually – Liviu T. Jan 04 '13 at 15:21
  • Indeed, very strange. It seems angular's select already does that out of the box – asgoth Jan 04 '13 at 15:27
  • @LiviuT.: it seems, if you don't add a child option, with an empty value in the body of the select tag, angular will add it for you. – asgoth Jan 04 '13 at 15:35
  • Thanks in advance, it is a possible solution but, my question goes on a different direction, i want to cancel the user action to change a property. like on jquery with o.preventDefault() – Flavio CF Oliveira Jan 04 '13 at 15:35
  • 1
    @asgoth, Angular will only add an empty option to the HTML if the ng-model property does not have an initial (valid) value. Once a valid value is selected, the empty option magically disappears. – Mark Rajcok Jan 04 '13 at 16:05
  • That does not answer the question – Cyril CHAPON Aug 02 '16 at 12:57
  • see also: https://stackoverflow.com/questions/45051160/angularjs-how-to-cancel-change-event-on-dropdown-when-the-confirm-dialog-is-fa – awiebe Aug 12 '17 at 07:34