'$scope not update from AngularJS directive

I implemented custom directive for if select element, in which if there is only one item then it selects that item by default else it works as regular drop down which works fine.

My problem is I have two drop down as cascade. A Company list and a Department list. The Department list depends on a Company list. So when only one Company is selected by default then Department list will also update respectively by default.

For that I try to use ng-change but It does not access updated value it shows old value in log.

My angularjs code is below:

angular.module('TestApp', [
]);
angular.module('TestApp').directive('advanceDropdown', function () {
    var directive = {}
    directive.restrict = 'A';
    directive.require = 'ngModel';
    directive.scope = {
        items: '=advanceDropdown',
        model: '=ngModel',
        key: '@key',
        change: '&ngChange'
    }
    directive.link = function (scope, element, attrs, ctrl) {
        scope.$watch('items', function (n, o) {
            if (scope.items.length == 1) {
                scope.model = scope.items[0][scope.key];
                scope.change();
            }
        });
    } 

    return directive;
});


(function () {
    'use strict';
    angular.module('TestApp').controller('TestCtrl', ['$scope', TestCtrl]);

    function TestCtrl($scope) {

        $scope.departmentList = [];
        $scope.companyList = [];
        $scope.designation = new Designation();
        $scope.active = null;
        $scope.BindDepartments = function () {
            
            console.log('updated companyId:' + $scope.designation.CompanyId);

            //Code here
            //Load department base on selected company
            //$scope.departmentList = data;
        }

        $scope.GetCompanyById = function (companyId) {
            //Get company data by id 
            //recived from server
            $scope.companyList = [{CompanyId:2,CompanyName:'xyz'}];
        }


        $scope.Init = function () {
            $scope.active = null;
            $scope.designation = new Designation();
            $scope.GetCompanyById(2); 
        }
    }

})();

function Designation() {
    this.DesignationId = 0;
    this.DesignationName = '';
    this.DepartmentId = 0;
    this.CompanyId = 0;
}
Designation.prototype = {
    constructor: Designation
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.2/angular.min.js"></script>
<div ng-app="TestApp" ng-controller="TestCtrl">
<select ng-model="designation.CompanyId"                 
        ng-options="dept.CompanyId as dept.CompanyName for dept in companyList"
        ng-change="BindDepartments()"
        advance-dropdown="companyList" key="CompanyId" required>
    <option value=""  disabled>--Select Company--</option>
</select>                    
</div>

In a BindDepartment() method $scope.designation.CompanyId value was not updated.

It Show log updated company id:0

I expect updated company id:2

Plunker demo



Solution 1:[1]

Avoid using isolate scope with the ng-model controller.

//directive.scope = {
//    items: '=advanceDropdown',
//    model: '=ngModel',
//    key: '@key',
//    change: '&ngChange'
//}

directive.scope = false;

Instead use the $setViewValue method to change the model value.

directive.link = function (scope, element, attrs, ctrl) {
    scope.$watchCollection(attrs.advanceDropdown, function (n, o) {
        if (n && n.length == 1) {
            ctrl.$setViewValue(n[0]);
        }
    });
}

The $setViewValue method will automatically invoke the ng-change expression.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 georgeawg