Ogglas Ogglas - 21 days ago 5
AngularJS Question

AngularJS 1.4.8 creates an empty option in select when my model is updated

I have a service where you can select different types of agreement documents. When I load the page for the first time, the agreement drop down is always populated correctly and the correct value is displayed in the browser. The agreement is fetched from the

vm.getAgreement()
function. Like this:

enter image description here

However it is also possible to update the AgreementDocumentType. This is done by calling the
vm.save()
function which in turn calls
vm.getAgreement()
again. I debugged the code to see when the error happens and it is when
vm.agreement = data;
is set for the second time. The object
data
is however exactly the same as the first time,
vm.agreement.AgreementDocumentType
is
3
. Every other field is updated correctly but the select list looks like this after save:

enter image description here

Looking at the generated HTML the weird thing is that the option with value 3 is marked as selected, as it should be, but still no value is displayed in the drop down list:
value="3" selected="selected">
. If I then select
Agreement3
or any other value the empty value (
<option value="? number:3 ?"></option>
) directly disappears from the list. I'm using AngularJS version 1.4.8 if it can be of any help. My best guess is that when
vm.agreement.AgreementDocumentType
is re-populated the binding is lost for a split second and therefore an empty value is created and for some reason not removed until the drop down is interacted with. Has anyone experienced this and solved it?

Generated HTML:

<select name="Agreement" class="form-control ng-valid ng-valid-required ng-dirty ng-touched" ng-model="vm.agreement.AgreementDocumentType" required="">
<option value="? number:3 ?"></option>
<!-- ngRepeat: option in vm.agreementTypes -->
<option ng-selected="vm.agreement.AgreementDocumentType == option.id" ng-repeat="option in vm.agreementTypes" ng-value="option.id" class="ng-binding ng-scope" value="1">
Agreement1
</option>
<!-- end ngRepeat: option in vm.agreementTypes -->
<option ng-selected="vm.agreement.AgreementDocumentType == option.id" ng-repeat="option in vm.agreementTypes" ng-value="option.id" class="ng-binding ng-scope" value="2">
Agreement2
</option>
<!-- end ngRepeat: option in vm.agreementTypes -->
<option ng-selected="vm.agreement.AgreementDocumentType == option.id" ng-repeat="option in vm.agreementTypes" ng-value="option.id" class="ng-binding ng-scope" value="3" selected="selected">
Agreement3
</option>
<!-- end ngRepeat: option in vm.agreementTypes -->
</select>


Html:

<select name="Agreement" class="form-control" ng-model="vm.agreement.AgreementDocumentType" required>
<option ng-selected="vm.agreement.AgreementDocumentType == option.id"
ng-repeat="option in vm.agreementTypes"
ng-value="option.id">
{{option.label}}
</option>
</select>


Controller:

vm.agreementTypes = [
{ id: 1, label: 'Agreement1' },
{ id: 2, label: 'Agreement2' },
{ id: 3, label: 'Agreement3' }
];

vm.save = function () {
agreementResource.update({ agreementId: vm.agreementId }, vm.agreement).$promise.then(function () {
toastr.success('Agreement saved');
vm.getAgreement();
});
};

vm.getAgreement = function () {
agreementResource.get({ agreementId: vm.agreementId }).$promise.then(function (data) {
vm.agreement = data;
});
}

vm.getAgreement();

Answer

I think because you are building the options manually, angularjs internals are not binding your model properly when it gets changed from scope side, there is more than selected="selected" to do, this internal are made by the ngOptions directive, consider using it instead of building your options list manually.

<select
    name="Agreement"
    class="form-control" 
    ng-model="vm.agreement.AgreementDocumentType"
    ng-options="option.id as option.label for option in vm.agreementTypes"
    required>
</select>
Comments