1

I'm working on customize a input directive which including a label. I tried several days and refer to some articles.
The only problem is that except ng-change, ng-blur and ng-focus, all the other event work. https://jsfiddle.net/luneyq/mw3oz2pr/
Of course I can bind these three event manually myself and they can work as https://jsfiddle.net/luneyq/bp7f3z1o/

But I really don't know why ng-change, ng-blur and ng-focus don't work. Is there any special on these three event? Anyone can help on this?

My codes are as below:

<div ng-app="myApp">
<div ng-controller="MainController">
    <my-input type="number" name="valueNumber1" ng-model="obj.valueNumber1" label="Age" ng-click="log('click')" ng-change="log('change')" ng-blur="log('blur')" ng-focus="log('focus')" ng-mouseleave="log('mouseleave')"></my-input>
    <div id="result"></div>
</div>

The JS:

    var app = angular.module("myApp", []);

app.controller('MainController', function($scope, $window){
    $scope.obj = {valueNumber1: 10};
    $scope.log = function(text) {
        document.getElementById("result").innerHTML = text + ':' + $scope.obj.valueNumber1 + "<br>" + document.getElementById("result").innerHTML;
    };
});

app.directive('myInput', function() {
    return {
        require:  '^ngModel',
        restrict: 'EA',
        scope: {
            ngModel: '=',
            name: '@name',
            label: '@label'
        },
        replace: true,
        transclude: true,
        priority: 10,
        template: '<div>' +
        '<label for="{{ name }}">{{label}}</label>' +
        '<input id="{{ name }}" ng-model="ngModel" />' +
        '</div>',
        compile: function(tElement, tAttrs, transclude) {
            var tInput = tElement.find('input');
            // Move the attributed given to 'custom-input' to the real input field
            angular.forEach(tAttrs, function(value, key) {
                if (key.charAt(0) == '$')
                    return;
                tInput.attr(key, value);
            });
            tElement.replaceWith('<div class="cbay-input-div">' + tElement.html() + '</div>');
            return;
        }
    };
});


Thanks in advance.

Lune
  • 241
  • 1
  • 3
  • 13
  • Seems like a lot of trouble to go through, just to add a label to an input. Why not just do it the old fashioned way? – Shaun Scovil Jan 29 '16 at 03:15
  • In your code `click` and `mouseleave` are applied to the `DIV` in the directive view. `change`, `focus` and `blur` events exist for form elements only. Hence they handlers should be set to the `input` element (not to the `DIV`) – Alexander Elgin Jan 29 '16 at 05:52

1 Answers1

0

The issue is that compilation/transclusion/replace don't work the way you think they work (not sure where you made an incorrect assumption).

What is wrong with your code:

1). You are using incorrect attribute name: you should use tInput.attr(tAttrs.$attr[key], value); instead of tInput.attr(key, value);.

2). All the directives specified at my-input are compiled and linked despite your changes to the tElement in compile function. Proof is here - https://jsfiddle.net/fyuz3auc/3/, take a look at the myTest directive and its output in console: it is still applied to the myInput despite any of your effort.

3). You specified scope for your directive. Thus it has an isolated scope, thus anything you've compiled inside it has no access to log function of MainController, that's why your logs aren't working. Here is the fiddle proving that: https://jsfiddle.net/m5tba2mf/1/ (take a look at $scope.log = $scope.$parent.log in link function returned from compile).

In order to solve the second issue I suggest you to try alternative approach - I am using this at my project, and like it very much. The idea is to use terminal in conjunction with extremely high priority and $compile. Here is the updated fiddle, I think it is pretty straightforward what I do there: https://jsfiddle.net/uoat55sj/1/.

user3707125
  • 3,394
  • 14
  • 23
  • I appreciated your explaination. I still have two more questions: 1) in my original code, I neither used $scope.log = $scope.$parent.log nor default scope(false) : https://jsfiddle.net/luneyq/mw3oz2pr/, but ng-click and some other event work, why? 2) as to the approach you suggested and used in your project which use the default scope(false), I read somewhere mentioned that the default false scope is not recommended in directive. Is there any bad influence using this approach? – Lune Jan 29 '16 at 05:25
  • 1). They worked at the top-level element of your directive, that's what I was trying to show in (2) - remove all of your code from `compile` - they will still work: https://jsfiddle.net/0znsL231/ – user3707125 Jan 29 '16 at 10:13
  • 2). Unless you provide me a link, I can't really answer that - I guess you misunderstood something. In your case it should be completely correct to use inherited scope: https://jsfiddle.net/osrgp29p/ - everything works. You can read some info regarding different types of scopes here: http://stackoverflow.com/questions/14914213/angularjs-when-writing-a-directive-how-do-i-decide-if-a-need-no-new-scope-a – user3707125 Jan 29 '16 at 10:31
  • one more question, why the attribute ngModel is needed as mandatory? In your code: if (!tAttrs.ngModel) { throw new Error("ngModel is required!"); } – Lune Feb 01 '16 at 03:32
  • I guess I wrote it during some intermediate change, disregard that piece. – user3707125 Feb 01 '16 at 04:04