1

I copied a custom directive that watches for changes on form file inputs.

angular.module('customDirective', [])
.directive('ngFileInputChange', function() {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            var onChangeHandler = scope.$eval(attrs.ngFileInputChange);
            element.bind('change', onChangeHandler);
        }
    };
});

Then I use it in the template like so:

<input ng-model="vm.image" ng-file-input-change="vm.base64Test" ...>

And this works fine.

However, this doesn't work:

<input ng-model="vm.image" ng-file-input-change="vm.base64Test()" ...>

Note the () at the end of base64Test(). With a generic ng-click, the parenthesis are fine, or even required (how else would I pass arguments to the function?) but with the custom directive, parenthesis cause an error.

I only have a vague understanding of what scope.$eval(attrs.ngFileInputChange) is actually doing, so maybe I could make some change in that?

EDIT:

I should also add that I need access to some scope variables. For example, if I didn't have to use the custom directive, I would write something like this in my controller:

vm.base64Test(associatedData){
    console.log(associatedData);    
}

And then:

<input ng-change("vm.base64Test(vm.associatedData)">

But ng-change doesn't watch file input contents and the custom directive doesn't allow arguments (other than event), so I'm stuck.

2 Answers 2

1

Firstly, if you're planning on calling a method from a directive's API (so to speak), you need to identify it as a callback:

scope: {
    'onChange': '&ngFileInputChange'
}

This simply means you're defined/exposing a way to declare a callback when the scope.onChange gets triggered. It also means you don't need to $parse or $eval since the isolate scope can handle that for you.

Next you can trigger that callback much like you're already doing:

element.bind('change', function(evt){
    $scope.$apply(function(){
        var payload = { $data:<whatever-you-pull-from-your-file-change-evt> }
        $scope.onChange(payload)
    }) 
});

One thing to note is that I'm calling the callback within the $scope.$apply. Because you're listening on non-angular events (e.g. onChange), you need to notify angular a change has occurred outside of the $digest cycle.

Where you've declared your directive, in your actual callback (probably defined on your view controller) you can pass the payload to the method like so:

<input ng-file-input-change="vm.doSomething($data)">
Sign up to request clarification or add additional context in comments.

Comments

0

When passing a function to a directive you usually need to pass the function pointer, so the directive can call the function later with arbitrary arguments.

In this case, your directive is evaluating the attribute to a function pointer (that's what the $eval is doing), and then binding it as a change handler to the element the directive is attached to. You don't need (or want) to have parenthesis.

There's nothing wrong with your code as it is currently. I have a directive which does the exact same thing, it works great! (and when your function vm.base64Test is called you will get the standard arguments for a change event on an input.) So you can have vm.base64Test be:

function(event) {
  console.log(event);
  // your other code here
  // event.target will give you the input element the code was called on
  // event.target.files will give you an array of the files selected by the user
}

2 Comments

Thanks for the answer. I essentially did what you have here, but I'm still running into problems. I expanded my question to explain.
where are you defining vm.base64test? It sounds like an architecture issue to me, you should define the fileChange event handling function in the context of a controller that has access to the $scope where the directive is used so you can access the functions that way. I can make a plunkr if this is not clear.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.