Nicholas Pappas
Nicholas Pappas

Reputation: 10624

Scope Variable Not Being Set Outside of Directive

I am attempting to create an AngularJS directive that sends a file name, from an <input type="file"> element, to a file upload factory. The work is based off the following blog post:

http://odetocode.com/blogs/scott/archive/2013/07/05/a-file-input-directive-for-angularjs.aspx

I have my HTML element defined:

<div file-input="file" on-change="readFile()"></div>

With the associated directive set up:

myApp.directive('fileInput', function ($parse)
{
    return {
        restrict: "A",
        template: "<input type='file' />",
        replace: true,
        link: function (scope, element, attrs) {
            var model = $parse(attrs.fileInput);
            var onChange = $parse(attrs.onChange);

            element.bind('change', function () {
                model.assign(scope, element[0].files[0]);
                console.log(element[0].files[0]);  // correctly references selected file
                scope.$apply();
                console.log(model(scope));  // correctly references selected file
                onChange(scope);
            });
        }
    };
});

When I select a file from the element the change event is fired and both my console.log calls print out a reference to the file that I selected. But attempting to print out $scope.file in my controller does not reflect the file selection:

$scope.file = "nothing";

$scope.readFile = function()
{
    $scope.progress = 0;

    console.log($scope.file);  // print "nothing"

    fileReaderFactory.readAsDataUrl($scope.file, $scope)
        .then(function (result) {
            $scope.imageSrc = result;
        });
};

What am I missing that does not allow the controller to properly set and retain the value sent from the directive?

UPDATE

I've made the following updates, but my scope variable is still not being updated. My change function is called, but it doesn't notice any new value given to myModel.

HTML:

<file-input my-model="fileRef" my-change="readFile()"></file-input>

Directive (input tag updated to text for testing purposes):

myApp.directive('fileInput', function ($parse)
{
    return {
        restrict: "AE",
        template: "<input type='text' />",
        replace: true,
        scope: {
            myModel: "=",
            myChange: "&"
        },
        link: function (scope, element, attrs) {
            element.bind('change', function () {
                scope.myChange();
            });
        }
    };
});

Controller:

$scope.fileRef = "nothing";

$scope.readFile = function()
{
    $scope.progress = 0;

    console.log($scope.fileRef);  // prints "nothing"

    fileReaderFactory.readAsDataUrl($scope.fileRef, $scope)
        .then(function (result) {
            $scope.imageSrc = result;
        });
};

Upvotes: 0

Views: 970

Answers (2)

Louis
Louis

Reputation: 1035

try adding file to the link function. I had the same issue and that's what fixed it for me.

As near as I can tell the link acts as the controller (scope) for the template.

However, the one thing that confuses me is that when I added a property to $scope from within a $scope method in the controller the new property is seen by the template. When added outside the $scope method (still in the controller) it is not.

Upvotes: 1

NicolasMoise
NicolasMoise

Reputation: 7279

In your directive object add scope: {file-input: '='} to have two way binding between your directive's scope and it's parent scope.

Upvotes: 1

Related Questions