Search code examples
javascriptangularjsangularjs-materialmd-select

passing ngModel and ngChange with custom directive under md-select


I am making a custom directive on top of md-select. I am having issues with default behavior of ngModel and ngChange. I can't seem to make them both work together.

Currently I have this http://next.plnkr.co/edit/X34DUWtkyYhbwJP4?open=lib%2Fscript.js

The ngModel is being updated, but the ngChange doesnt seem to work.

I also tried a method shown in http://embed.plnkr.co/HZAHSyi9L8UQdE24zYYI/ but having issues when setting value with a timeout (assuming value comes from api).

app.controller("appCtrl", function($scope){
  $scope.items = [1,2,3,4,5,6];
  $scope.foo=2; // this works
  $scope.bar = function(foo)  {
    $scope.aux = foo;
  }
  setTimeout(function(){
    $scope.foo=5;
  }, 0); // this doesnt work
});

I want to make these two attributes to work as default md-select does.


Solution

  • When working with ng-model and custom directives, you can specify ngModel as a require, and then automatically get access to other directives like ngChange and ngRequired. I've updated your plunkr: http://next.plnkr.co/edit/VzYpZ2elmzV6XkbM?open=lib

    HTML

    <md-custom-select 
        ng-model="vm.SelectItems" 
        ng-change="vm.onselectchange()" 
        list="vm.ItemList">
    </md-custom-selector>
    

    JavaScript

    var app = angular.module("MaterialApp", ["ngMaterial"]);
    app.directive("mdCustomSelect", ["$compile", mdCustomSelect]);
    
    function mdCustomSelect($compile) {
        return {
            restrict: "E",
            require: {
                ngModelCtrl: '^ngModel'
            },
            scope: {
                ngModel: "<",
                list: "=",
                options: "<",
            },
            replace: true,
            link: function(scope, element, attrs, controllers) {
                scope.ngModelCtrl = controllers.ngModelCtrl;
    
                var searchTemplate = '<md-select-header aria-label="Select Header" class="demo-select-header"><input aria-label="InputSearchBox" ng-keydown="$event.stopPropagation()" ng-model="searchTerm" type="search" placeholder="Search items" class="md-text"></md-select-header>';
                var selectAllTemplate = '<div style="padding: 0px 0px 15px 5px; background-color: #efefef;"><md-checkbox class="md-warn" title="Select All" ng-model="checkAllChecked" ng-change="toggleSelectAll()">Check/Uncheck All </md-checkbox></div>';
                var multiSelectGroupTemplate = '<md-option ng-value="item.ItemID" ng-repeat="item in ItemList | filter: searchTerm">{{item.ItemName}}</md-option>';
                var completeTemplate = "";
                completeTemplate += '<md-select multiple ng-model="ngModel" ng-change="valChanged()" data-md-container-class="selectdemoSelectHeader">';
                completeTemplate += searchTemplate; //2 begin and end
                completeTemplate += selectAllTemplate; //3 begin and end
                completeTemplate += multiSelectGroupTemplate; //4 begin and end
                completeTemplate += " </md-select>"; //1 end
                element.html(completeTemplate);
                $compile(element.contents())(scope);
            },
            controller: ["$scope", function($scope) {
                var defaultValueProperty = ($scope.options == undefined || $scope.options.Value === undefined) ? "value" : $scope.options.Value;
                var defaultTextProperty = ($scope.options == undefined || $scope.options.Text === undefined) ? "name" : $scope.options.Text;
                $scope.isMultipleSelected = angular.isUndefined($scope.multiple) ? true : $scope.multiple;
                $scope.checkAllChecked = false;
    
                $scope.ItemList = [];
                var rawItemList;
    
                $scope.$watch("list", function(newValue) {
                    $scope.ItemList = newValue.map(item => {
                        return { ItemID: item[defaultValueProperty], ItemName: item[defaultTextProperty] };
                    });
                }, true);
    
                $scope.valChanged = function(){
                    $scope.ngModelCtrl.$setViewValue($scope.ngModel);
                }
    
                $scope.toggleSelectAll = function() {
                    if ($scope.checkAllChecked == false) {
                        $scope.ngModelCtrl.$setViewValue([]);
                    } else {
                        $scope.ngModelCtrl.$setViewValue($scope.ItemList.map(item => item.ItemID));
                    }
                };
            }]
        };
    }