1

Got an odd problem with angular. I can get this to work with ng-click but I need to use onchange for other reasons.

My code seems to be working fine for other buttons that can be pressed to trigger the same code but when it comes to my upload button it decides not to update the ng-class even though scope.slideMenu is actually logged out as true.

I would also like to close the menu when the user has finished choosing an image/file if someone could help with that as well.

Heres the code:

HTML

                <div class="app-slide-menu"
                     ng-class="{'app-menu-active': slideMenu}">

                    <form class="app-set-image-file-form">
                        <label class="app-file-input"
                               title="Upload Image">
                            <i class="icon-enter"></i>
                            <input type="file"
                                   onchange="angular.element(this).scope().setImageFile(this)"
                                   accept="image/*">
                        </label>
                    </form>
                </div>

Now for the JS:

        scope.setImageFile = function (element) {
        var reader = new FileReader();

        // Converts the image to a data URL.
        reader.readAsDataURL(element.files[0]);
        scope.slideMenuToggle();

        /**
         * When chosen image is selected it triggers the onload function to pass the image to the whiteboardService.
         * @param event - Saves the event of the input field to get the images data URL.
         */
        reader.onload = function (event) {
            fabric.Image.fromURL(event.target.result, function (img) {
                whiteboardService.uploadImageToCanvas(img);
            });

            // Resets the form that wraps round the file input to allow the
            // user to add more than one of the same file.
            // NOTE: This will break other inputs if you put them inside this form
            $('.app-set-image-file-form').trigger('reset');
        };
    };

And the simple toggle JS:

    scope.slideMenuToggle = function () {
        scope.slideMenu = !scope.slideMenu;
    };
Max Lynn
  • 1,738
  • 6
  • 22
  • 35
  • 3
    Are you sure you don't want to use `ng-change` ? – IanGabes Mar 10 '16 at 17:33
  • ngModel doesn't work with input type file and ngChange ALWAYS requires ngModel. – Tarun Dugar Mar 10 '16 at 17:45
  • Have you tried triggering a $digest cycle after your changes? Angualr doesn't know anything has changed since you need to use the onchange handler instead of ng-change. You can trigger using $timeout or $scope.$apply – Michael Biggs Mar 10 '16 at 18:18
  • FYI this explains why we can't use Ng-change http://stackoverflow.com/questions/17922557/angularjs-how-to-check-for-changes-in-file-input-fields – Max Lynn Mar 11 '16 at 11:17

3 Answers3

1

Use ngChange. onChange is still the JavaScript onChange it was before using Angular. When you use onChange, Angular doesn't know things have changed.

This is one of the rare cases calling $scope.$apply() would be needed (when using onChange)

JanS
  • 2,065
  • 3
  • 27
  • 29
  • Oddly Scope.apply doesn't work. If I put this in the function that is being called on change. scope.slideMenu = true; scope.$apply(); am I doing this wrong. Please note that my scope doesn't need the $ as the developer before us thought it would of been a good idea...... – Max Lynn Mar 11 '16 at 10:10
  • Update you can use scope.apply() but oddly you have to write it after everything else – Max Lynn Mar 11 '16 at 11:56
0

I think your best option would be to create an angular directive for a custom onchange, something like this:

app.directive('customOnChange', function() {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var onChangeHandler = scope.$eval(attrs.customOnChange);
            element.bind('change', onChangeHandler);
        }
    };
});

Then you can use it like this on your input: <input type="file" custom-on-change="setImageFile" accept="image/*">

Finally in your controller, you can do this:

$scope.setImageFile = function (element) {
    var reader = new FileReader();

    // Converts the image to a data URL.
    reader.readAsDataURL(element.files[0]);
    scope.slideMenuToggle();

    /**
     * When chosen image is selected it triggers the onload function to pass the image to the whiteboardService.
     * @param event - Saves the event of the input field to get the images data URL.
     */
    reader.onload = function (event) {
        fabric.Image.fromURL(event.target.result, function (img) {
            whiteboardService.uploadImageToCanvas(img);
        });

        // Resets the form that wraps round the file input to allow the
        // user to add more than one of the same file.
        // NOTE: This will break other inputs if you put them inside this form
        $('.app-set-image-file-form').trigger('reset');
    };
};

Hope this can help.

Johannes Jander
  • 4,974
  • 2
  • 31
  • 46
Sergio Fandino
  • 435
  • 3
  • 7
0

Ok guys,

Sorry to say that your suggestions didn't help me but I managed to come to a solution that fixed my bug but I don't really understand why so please let me know if you can let me know.

I've basically added a class of 'js-input-file' to the input tag and then put this code below to trigger a scope reset.

        var inputScope = angular.element('.js-input-file').scope();

        inputScope.$apply();

This fixes my problem but I just can't get round my head why putting scope.$apply(); on its own doesn't fix the problem but the above does. Let me know your thoughts.

Max Lynn
  • 1,738
  • 6
  • 22
  • 35