0

I have come across a situation where I need to change the input type of an input box based on the selected value of a dropdown. In other words, if the user selects 'String' from the dropdown then the type of the input should be 'text', etc. I decided to create a directive cuz I'm still learning Angular but also so I don't copy blocks of code all over the place (I need this more than once).

This it the contents of my attempt:

(function () {
    "use strict";

    angular
        .module('app')
        .directive('dynamicTypeInput', dynamicTypeInput);

    function dynamicTypeInput() {
        return {
            replace: true,
            restrict: 'AE',
            require: ['ngModel', '^form'],
            scope: {type: '=', placeholder: '@'},
            templateUrl: 'app/common/dynamic-type-input/dynamic-type-input.tpl.html',
            link : function(scope, element, attrs, ngModel){

                //Watch for changes to ngModel, update scope.inputValue
                scope.$watch(function(){
                    return ngModel[0].$modelValue;
                }, function (value){
                    scope.inputValue = value;
                });

                //Watch inputValue, update the model
                scope.$watch('inputValue', function(value){
                    ngModel[0].$setViewValue(value);
                });

                //Setup ng-change
                if (attrs.ngChange) {
                    ngModel[0].$viewChangeListeners.push(function () {
                        scope.$eval(attrs.ngChange);
                    });
                }
            }
        };
    }
})();

Note: The template is simply an ng-switch that selects the appropriate input box based on the value of scope.type and the inputs all bind to scope.inputValue.

I used the answer from this SO question to help me add the ability to add an ng-change attribute and fire correctly. According to that answer I needed to remove the ngModel from the isolated scope, I'm not sure why this is required but, if someone could explain it I'd be grateful.

Removing ngModel from the isolated scope made it more difficult to instantiate directive with an initial value or have the directive update when the model is changed in the main controller so now I watch ngModel[0].$modelValue and update the local value if it changes.

While the directive works and does what I expect it do but it all seems a bit convoluted and inefficient, is there no way I can achieve what I want in a more simple manner?

JonoCoetzee
  • 969
  • 2
  • 15
  • 30

1 Answers1

0

Using the second answer to the SO question referenced I fixed the problem of needing to remove ngModel from the isolated scope in order to get ngChange to work. This simplified the directive and I can use two-way binding as is.

Final directive:

(function () {
    "use strict";

    angular
        .module('app')
        .directive('dynamicTypeInput', dynamicTypeInput);

    dynamicTypeInput.$inject = ['$timeout'];

    function dynamicTypeInput($timeout) {
        return {
            replace: true,
            restrict: 'AE',
            require: ['ngModel', '^form'],
            scope: {ngModel: '=', type: '=', placeholder: '@', ngChange: "&"},
            templateUrl: 'app/common/dynamic-type-input/dynamic-type-input.tpl.html',
            link: function (scope, element, attrs, ngModel) {

                scope.$watch('ngModel', function () {
                    $timeout(scope.ngChange);
                });

            }
        };
    }
})();
Community
  • 1
  • 1
JonoCoetzee
  • 969
  • 2
  • 15
  • 30