Subash Maharjan
Subash Maharjan

Reputation: 11

How to implement required validiation in md-select using custom directive template for md-select in angularJS?

I am setting up custom template for md-select using custom directive but form validiation and required validiation is not working. This is for refactoring template of md-select using custom directive as md-custom-select.

I have created template of md-select in custom directive passing models and lists for dropdown and also required attr. The main problem for this custom directive is , it doesnot validiate form.

I am converting :


    <md-select  required ng-model="vm.Search.ParentDivisionID" ng-change="vm.GetDivisionIDList()">
                                            <md-option ng-value="item.ID" ng-repeat="item in vm.ParentDivisionList">
                                                {{item.Name}}
                                            </md-option>
                                        </md-select>

TO:

<md-custom-select ng-model="vm.Search.ParentDivisionID" list="vm.ParentDivisionList" ng-required="true" ng-change="vm.GetDivisionIDList(vm.Search.ParentDivisionID)"></md-custom-select>


     (function () {
        'use strict';
        window.app.directive('mdCustomSelect', mdCustomSelect);
        mdCustomSelect.$inject = ['$compile'];
        function mdCustomSelect($compile) {
            var styleTemplate = "<style>\
            md-checkbox#checkAllBox{\
                margin: 16px 0px 10px 5px;\
            }\
            md-checkbox#checkAllBox .md-label {\
                margin-bottom: 0px !important;\
            }\
            md-checkbox#checkAllBox .md-label span {\
                margin-left: 3px !important;\
            }\
            md-checkbox#checkAllBox .md-icon {\
                height: 20px !important;\
                width: 20px !important;\
            }\
            .parentLabel .parentLabel label {\
                border-bottom: 1px solid #ccc;\
                color: #000;\
                padding-bottom: 6px;\
            }\
            md-checkbox#checkAllBox:not([disabled]).md-warn.md-checked .md-icon {\
                background-color: rgb(88, 104, 191) !important;\
            }\
            md-checkbox#checkAllBox.md-checked .md-icon:after {\
                height: 13px !important;\
                left: 5px !important;\
            }\
            .parentLabel .md-container-ignore {\
                padding: 0px;\
                margin: 0px;\
                border:none;\
            }\
            </style>";

            var selectAllTemplate = '<div style="padding: 0px 0px 15px 5px; background-color: #efefef; border-bottom: 1px solid #ccc;">\
                <md-checkbox aria-label="Parent Checkbox" class="md-warn" id="checkAllBox" title="Select All" ng-model="checkAllChecked" ng-change="toggleSelectAll()">Check/Uncheck All </md-checkbox>\
            </div>';
            function searchTemplate(placeholder) {
                if (placeholder == undefined || placeholder == "") placeholder = "Search ...";
                return '<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="' + placeholder + '"\
                       class="demo-header-searchbox md-text">\
            </md-select-header>';
            }
            function getOptionTemplate(isMultiple, isGroup) {
                var optionTemplate = '<md-option aria-label="Dropdown Selector" ng-value="subItem.ItemID" ng-repeat="subItem in ' + (isGroup ? 'item.SubItems' : 'ItemList') + ' | filter: searchTerm">{{subItem.ItemName}}</md-option>';
                if (isGroup == false) {
                    return (isMultiple ? selectAllTemplate : '') + optionTemplate;
                }
                return (isMultiple ? selectAllTemplate : '') + '<md-optgroup aria-label="Dropdown Selector" class="parentLabel" ng-repeat="item in ItemList">\
                    <div ' + (isMultiple ? 'ng-click="item.toggleSelect() "' : '') + 'style="padding:10px 10px 10px 10px; font-size:1.4em; color:black; border-bottom: 1px solid #ccc;">\
                        {{item.ItemName}}\
                    </div>'
                    + optionTemplate +
                '</md-optgroup>';
            }
            function getMdSelectOptionsTemplate($scope) {
                var returnTemplate = $scope.isMultipleSelected ? 'multiple ' : '';
                returnTemplate += 'ng-required="isRequired" ';
                returnTemplate +='ng-disabled="isDisabled" ';
                return returnTemplate;
            }
            var linker = function ($scope, element, attrs, controllers) {
                //https://github.com/angular/angular/issues/10094 Binding required attribute always applies the required validator
                $scope.isMultipleSelected = "multiple" in attrs ? (attrs.multiple == "false" ? false : true) : false;
                $scope.isGroup = "group" in attrs ? (attrs.group == "false" ? false : true) : false;
                $scope.ngModelCtrl = controllers.ngModelCtrl;
                var ngModelName = attrs.ngModel;

                var completeTemplate = "";
                completeTemplate += styleTemplate;
                //1 begin
                completeTemplate += '<md-select ' + getMdSelectOptionsTemplate($scope) + 'ng-model="ngModel" md-on-open="mdOpen()" md-on-close="mdClose()" ng-change="valChanged()" data-md-container-class="selectdemoSelectHeader" class="md-no-asterisk">';

                completeTemplate += searchTemplate(attrs.placeholder);//2 begin and end
                completeTemplate += getOptionTemplate($scope.isMultipleSelected, $scope.isGroup); // 3 begin and end
                completeTemplate += ' </md-select>';//1 end

                element.html(completeTemplate);
                $compile(element.contents())($scope);
            };

            return {
                restrict: "E",
                require: {
                    ngModelCtrl: '^ngModel'
                },
                scope: { // also uses multiple and group attribute
                    list: "=",
                    ngModel: "<",
                    mdOnClose: "&",
                    ngOption: "<",
                    mdChangeOnClose: "&", // event fire when selector is closes with changed value
                    isDisabled: "=ngDisabled",
                    isRequired: "=ngRequired",
                    placeholder: "@"
                },
                replace: true,
                link: linker, // link is called after controller
                controller: ['$scope', function ($scope) {
                    var defaultValueProperty = $scope.ngOption == undefined || $scope.ngOption.Value == undefined ? 'DivisionID' : $scope.ngOption.Value;
                    var defaultTextProperty = $scope.ngOption == undefined || $scope.ngOption.Text == undefined ? 'DivisionName' : $scope.ngOption.Text;
                    var defaultParentValueProperty = $scope.ngOption == undefined || $scope.ngOption.ParentValue == undefined ? 'ParentDivisionID' : $scope.ngOption.ParentValue;
                    var defaultParentTextProperty = $scope.ngOption == undefined || $scope.ngOption.ParentText == undefined ? 'ParentDivisionName' : $scope.ngOption.ParentText;

                    $scope.ItemList = [];
                    var rawItemList;
                    $scope.$watch('list', function (newValue, OldValue) {
                        formatListForView(newValue);
                    }, true);
                    $scope.toggleSelectAll = function () {
                        valueChanged = true;
                        if ($scope.checkAllChecked == false) {
                            $scope.ngModelCtrl.$setViewValue([]);
                        } else {
                            $scope.ngModelCtrl.$setViewValue(rawItemList.map(function (item) { return item[defaultValueProperty]; }));
                        }
                    };

                    function formatListForView(rawList) {
                        rawList = rawList == null || rawList == undefined ? [] : rawList;
                        rawItemList = rawList;
                        var parentList = [];
                        var idList = [];
                        // if no grouping then just return the modified itemlist
                        if ($scope.isGroup == false) {
                            $scope.ItemList = rawList.map(function (subItem) {
                                return {
                                    ItemID: subItem[defaultValueProperty],
                                    ItemName: subItem[defaultTextProperty]
                                }
                            });
                            return; // below code is for grouping, not needed for group==false
                        }
                        // fill parent
                        rawList.forEach(function (item, index) {
                            if ((item[defaultParentValueProperty] != null || item[defaultParentValueProperty] != 0) && idList.indexOf(item[defaultParentValueProperty]) == -1) {
                                idList.push(item[defaultParentValueProperty]);
                                parentList.push({
                                    ItemID: item[defaultParentValueProperty],
                                    ItemName: item[defaultParentTextProperty]
                                });
                            }
                        });
                        // fill children
                        parentList.forEach(function (item, index) {
                            item.SubItems = rawList.filter(function (subitem) {
                                return subitem[defaultParentValueProperty] == item.ItemID;
                            }).map(function (subItem) {
                                return {
                                    ItemID: subItem[defaultValueProperty],
                                    ItemName: subItem[defaultTextProperty]
                                }
                            });
                            // toggle children from parent
                            item.toggleSelect = function () {
                                var valuesToToggle = item.SubItems.map(function (item) {
                                    return item.ItemID;
                                });
                                valueChanged = true;
                                var alreadySelected = _.intersection(valuesToToggle, $scope.ngModel);
                                if (alreadySelected.length == 0 || (alreadySelected.length > 0 && alreadySelected.length != valuesToToggle.length)) { // select all from the parent
                                    $scope.ngModelCtrl.$setViewValue(_.union(valuesToToggle, $scope.ngModel));
                                } else { // select none from the parent
                                    $scope.ngModelCtrl.$setViewValue(_.difference($scope.ngModel, valuesToToggle));
                                }
                            }
                        });
                        $scope.ItemList = parentList;
                    }
                    var valueChanged = false; // check if value changed for ngChangeOnClose
                    $scope.valChanged = function () {
                        valueChanged = true;
                        // for ng-change
                        $scope.ngModelCtrl.$setViewValue($scope.ngModel);
                    }
                    $scope.mdOpen = function () {
                        valueChanged = false;
                    }
                    $scope.mdClose = function () {
                        if (typeof $scope.mdOnClose === 'function') $scope.mdOnClose();
                        if (valueChanged) {
                            if (typeof $scope.mdChangeOnClose === 'function') $scope.mdChangeOnClose();
                        }
                    }
                }]
            }
        }
    })(); 

My expected result from this is it should work for form validiation.

Upvotes: 1

Views: 182

Answers (0)

Related Questions