1

I've written my first custom directive, ng-file-chosen that should assign the file name that has been selected in an <input> element to the binding passed in through ng-model.

In the following snippet, the ng-file-chosen result is bound to model.file and there is a binding below to show the chosen value.

var app = angular.module('app', []);
app.controller('controller', function ($scope) {
    $scope.model = {
        file: "No file selected"
    };
});

var directive = function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        scope: {
            ngModel: '='
        },
        link: function (scope, element, attributes) {
            element.change(function (e) {
                var files = (e.srcElement || e.target).files;
                scope.ngModel = files[0];
            });
        }
    };
};

app.directive('ngFileChosen', directive);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-app="app" ng-controller="controller">
    <input type="file" ng-file-chosen="" ng-model="model.file"/>
    <p>{{model.file}}</p>
</div>

Unfortunately, when selecting a file nothing happens and the binding does not update. I have tried inspecting the generated HTML, and it appears as if the link function isn't running at all, because the input element's full HTML on run is:

<input type="file" ng-file-chosen="" ng-model="model.file" class="ng-pristine ng-valid ng-isolate-scope ng-touched">

What could be causing the directive not to run successfully?

Nick Udell
  • 2,420
  • 5
  • 44
  • 83

1 Answers1

2

There are a few problems with your code.

  1. You do not need to do the isolate binding for ngModel as you are already requiring ngModel in your directive. You don't even need to use isolated scope at all. Generally when you create a attribute directive, you shouldn't use isolated scope.

  2. you need to scope.$apply to propogate your changes to the scope.

    scope.$apply(function() {
        scope.model.file = files[0].name;  
    });
    

There are some other minor fixes that I have done. Here is an updated working plunker for your code.

Abhishek Jain
  • 2,957
  • 23
  • 35