angularjsarraysangularjs-directiveangularjs-modelangularjs-bindings

Angular 1 dynamic form object


I'm using Angular 1 and creating a dynamic form. It works by looping through some objects and rendering dynamically binded input fields like:

<div class="quest-form form-group" ng-repeat="task in tasks">   
    <div ng-if="task.Class == 'TaskText'" ng-class="'task ' + task.Class">          
        <input ng-model="questForm.Task[task.ID].Value" ng-name="task_{{task.ID}}" ng-required="task.Required == 1" type="text" class="form-control" placeholder="{{task.Title}}"  />
    </div>
    ...
    ...
</div>

I also have a upload field in the loop:

<div ng-if="task.Class == 'TaskUpload'" ng-class="'task ' + task.Class">
    <input class="btn btn-primary upload-btn" ngf-max-size="10MB" type="file" ng-model="upload" ngf-multiple="false" ngf-select="uploadFile(upload, task.ID, $invalidFiles)" />
    <input class="" ng-model="questForm.Task[task.ID].FileUploadID" ng-required="task.Required == 1" ng-name="task_{{task.ID}}" type="text" />
</div>

When the file uploaded event is called I'm trying to set the value of the hidden field which is ng-model="questForm.Task[task.ID].FileUploadID" like this:

$scope.uploadFile = function(file,taskID) {
    file.upload = Upload.upload({
        url: assetsURL+'/home/UploadFile',
        data: {file: file}
    });
    file.upload.then(function (response) {
        $scope.questForm.Task[taskID].FileUploadID = response.data; // THIS MESSES UP
    }, function (response) {
        ...
    });
};

I get the following error, it's like $scope.questForm.Task[128] does not exist even though the hidden input looks correct and is binded to the $scope.questForm.Task[128].

angular.js:14362 TypeError: Cannot read property '128' of undefined
at file.upload.then.$scope.errorMsg (http://localhost/carl-mygps-app/js/controllers/quest-details-controller.js:120:26)
at processQueue (http://localhost/carl-mygps-app/bower_components/angular/angular.js:16689:37)
at http://localhost/carl-mygps-app/bower_components/angular/angular.js:16733:27
at Scope.$eval (http://localhost/carl-mygps-app/bower_components/angular/angular.js:18017:28)
at Scope.$digest (http://localhost/carl-mygps-app/bower_components/angular/angular.js:17827:31)
at Scope.$apply (http://localhost/carl-mygps-app/bower_components/angular/angular.js:18125:24)
at done (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12233:47)
at completeRequest (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12459:7)
at XMLHttpRequest.requestLoaded (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12387:9) Possibly unhandled rejection: {}

I have tried defining blank objects in the scope like:

$scope.questForm = [];
$scope.questForm.Task = {};

But I should not need to because they are created in the template? confused. Thanks all.


Solution

  • Actually nope. Having your template compiled does not mean all the ng-models are initialized. While ng-model is smart enough to create all the intermediate objects, if they don't exist, it doesn't do so until $viewValue is changed. In your case if you upload a file without editing any other input first, $viewValue for inputs has never changed, and thus you have to initialize questForm, questForm.Task, and questForm.Task[taksID] yourself.

    if (!$scope.questForm) {
      $scope.questForm = {};
    }
    if (!$scope.questForm.Task) {
      $scope.questForm.Task = {};
    }
    if (!$scope.questForm.Task[taskID]) {
      $scope.questForm.Task[taskID] = {};
    }
    $scope.questForm.Task[taskID].FileUploadID = response.data;
    

    Or you can initialize questForm and questForm.Task at the beginning. And only check if questForm.Task[taskID] exists before initializing it.