0

I have a problem where by a form within my ng-controller doesnt seem to change the properties in the controller that I thought it would. After some reading around it seems I wasn't fully aware of prototypal inheritance, but thanks to the internet and SO I updated my code as such. But unfortunately its still not working and I cannot figure out why.

Here is my HTML

<div ng-app="licenceApp" ng-controller="licenceController">    
    <div class="hover panel" ng-class="{switch : licenceFormVisible}">
        <div class="highlightedSection nosidepad clearfix back">  
            <div class="highlightedSubSection" ng-class="{fullsize : uploadModel.active}" ng-show="!Applying && !FinishedWithErrors && !offlineActivationScreenVisible">
                <h2>Licence File</h2>
                Upload and apply a valid licence file{{uploadModel.active}}<br /><br />
                ...
                <form id="hiddenUploadForm" name="hiddenUploadForm" target="hiddenUploadFormFileTarget" action="/settings/uploadILP" method="post" enctype="multipart/form-data" style="display: none;">
                    <input id="hiddenUploadFormFile" name="file" type="file" ng-model="uploadModel.uploadFileName" onchange="angular.element(this).scope().uploadFileChanged()" />
                    <iframe id="hiddenUploadFormFileTarget" name="hiddenUploadFormFileTarget" iframe-onload="uploadFileFinished()"></iframe>
                </form>
            </div>
        </div>
    </div>

ViewModel

angular.module('licenceApp.controllers', [])
    .controller('licenceController', ['$scope', 'licenceAPIservice', '$filter', '$timeout', function ($scope, licenceAPIservice, $filter, $timeout) {
        $scope.uploadModel = {
            active: false,
            uploadFileName: "",
            uploading: false
        };

        $scope.uploadFileChanged = function () {
            $scope.uploadModel.active = true;
            $scope.uploadModel.uploading = true;

            $('#hiddenUploadForm').submit();
        }
        ...

So when I change uploadModel.active in a function it shows the correct value through a console.log but the display doesnt mimic the new value! Am I still subject to prototypal inheritance here? Note that uploadFileChanged is hit when the input file control is changed.

Community
  • 1
  • 1
Chris
  • 26,744
  • 48
  • 193
  • 345

1 Answers1

1

onchange is a javascript event outside angular so you would need to call $apply to notify angular of the changes in the scope. Fortunately there is a angular directive that does that for you (ng-change).

<input id="hiddenUploadFormFile" 
       name="file" type="file" 
       ng-model="uploadModel.uploadFileName"
       ng-change="uploadFileChanged()" />

EDIT:

ngModel doesn't work with input type=file (issue), hence ngChange won't work as it required ngModel to work.

The right way to approach it then would be to call $apply inside you uploadFileChanged function.

HTML:

<input id="hiddenUploadFormFile" 
       name="file" type="file" 
       onchange="angular.element(this).scope().uploadFileChanged()"/>

JS:

    $scope.uploadFileChanged = function () {
        $scope.$apply(function() {
          $scope.uploadModel.active = true;
          $scope.uploadModel.uploading = true;
          $('#hiddenUploadForm').submit();
        });
    }

If you plan to use input type file, it might be work to create a simple directive that handles the change event without the need to access the element scope that way.

Wawy
  • 6,259
  • 2
  • 23
  • 23
  • Yes I just double checked and it doesnt work with inputs.. In this case do I just need to use $apply within my function? – Chris Nov 18 '14 at 09:10
  • ngChange only requires ngModel to work, so if the input field has ngModel it should work fine. – Wawy Nov 18 '14 at 09:11
  • Thanks. If I add that code I get an error on the apply line which is: `Uncaught TypeError: undefined is not a function ` – Chris Nov 18 '14 at 09:25
  • It was `$apply` :D - ok so thats great and it works, thankyou. But can I ask why? Why do I need to apply when in other functions I dont? It must have found the scope as the function was executed? – Chris Nov 18 '14 at 09:27
  • Because of the way two-way data binding works, when you change a property in $scope that a it's being used in your template angular needs to be informed when that property change in some way and the way that it uses is called $digest which is a recursive function that travels accross all scopes checking for equality checks in the fields that it's supposed to keep in sync with the template. – Wawy Nov 18 '14 at 09:30
  • Why do I not need to do this under normal execution? – Chris Nov 18 '14 at 09:30
  • Because you are inside angular context, meaning an angular directive/controller/service all does methods are called within $digest or they trigger a $digest. – Wawy Nov 18 '14 at 12:24