kishor10d
kishor10d

Reputation: 553

angularjs - ng-file-upload not binding model to dynamically created HTML form

I want to upload an image file from dynamically generated HTML form using ng-repeat. I am using ng-file-upload module to upload the single image file (https://github.com/danialfarid/ng-file-upload). When I am uploading file from static HTML its working fine. But when I try to upload the file from dynamically generated HTML then it wont work as expected. The file is not uploading and also giving error in firefox console as follows :

Error: Argument 2 of FormData.append is not an object.

The form is successfully submitted if we assign ng-model of file control set to null. for example; if

<input name='img' type='file' value='' ng-model='data.imageFile' 'ngf-select' accept='image/*' />

and

$scope.data.imageFile = null;

then other parameters will submitted by HTTP service and store to database normally but file will not going to upload.

Is any way to assign file object to the input[type=file] in this case of dynamically generated HTML??

The code PLUNKER is created here

http://plnkr.co/edit/S7hnVJnuDjWUMQ6PYewk?p=preview

Upvotes: 0

Views: 1858

Answers (1)

Yogi
Yogi

Reputation: 450

Yes there is a way to assign input type=file to dynamically generated html. Not just dynamically generated when the page load, but also when adding new input type=file via angular. I just did this and it worked!!! and I'm so excited I post all the tricks here. All I'm asking in return is please Vote up when you get it working in your solution. Both the question and the Answer are at 0 point now, but I can prove that this is a working solution.

     <input type="file" class="form form-control" placeholder="Section Image" file-model2="fileUploadFile2[getImageIndex(c.ChapterNbr, $index)][$index]" />

Notice that have two dimension array and this input=file goes inside ng-repeat within ng-repeat, dynamically added when user presses the +Add button.

on the angular side, in getImageIndex:

        var chIndex = 0;
        var sIndex = 0;

        $scope.getImageIndex = function (chNbr, sectionNbr) {
            for (var i = 0; i < $scope.chapters.length; i++) {
                if ($scope.chapters[i].ChapterNbr == chNbr) {
                    chIndex = i;
                    sIndex = sectionNbr;
                    return i;
                };
            };
        };

This is purely to get the indexes (first and second dimension, specific to my setup). I use directive posted somewhere in StackOverflow that I'm thankful for, to actually get the file bytes and info, and it goes like this:

       .directive('fileModel2', ['$parse', function ($parse) {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                element.bind('change', function (e) {
                    $parse(attrs.fileModel2)
                    .assign(scope, element[0].files[0]);
                    scope.$apply();
                    scope.getFile2(scope.$eval(attrs.indexNumber));
                });
            }
        };
    }])
    .factory('fileReaderFactory', function ($q, $log) {
        return {
            onLoad: function (reader, deferred, scope) {
                return function () {
                    scope.$apply(function () {
                        deferred.resolve(reader.result);
                    });
                };
            },
            onError: function (reader, deferred, scope) {
                return function () {
                    scope.$apply(function () {
                        deferred.reject(reader.result);
                    });
                };
            },
            onProgress: function (reader, scope) {
                return function (event) {
                    scope.$broadcast("fileProgress",
                        {
                            total: event.total,
                            loaded: event.loaded
                        });
                };
            },
            getReader: function (deferred, scope) {
                var reader = new FileReader();
                reader.onload = this.onLoad(reader, deferred, scope);
                reader.onerror = this.onError(reader, deferred, scope);
                reader.onprogress = this.onProgress(reader, scope);
                return reader;
            },

            readAsDataURL: function (file, scope) {
                var deferred = $q.defer();

                var reader = this.getReader(deferred, scope);
                reader.readAsDataURL(file);

                return deferred.promise;
            }
        }
    }
    );

The directive triggers getFile2, which executes the Filereader to read the bytes fore previewing the image. Finally, to preview the image:

         $scope.getFile2 = function () {
            console.log($scope.fileUploadFile2[chIndex][sIndex]);
            if ($scope.fileUploadFile2[chIndex][sIndex]) {
                fileReaderFactory.readAsDataURL($scope.fileUploadFile2[chIndex][sIndex], $scope)
                    .then(function (result) {
                        $scope.chapters[chIndex].Sections[sIndex].sectionImgPreview = result;
                    });
            }
        };

and here is the html to preview the image:

     <img ng-if="s.sectionImgPreview" class="img-responsive" ng-src="{{s.sectionImgPreview}}" alt="" onerror="this.src='@Url.Content("~/Content/Images/ToyApp.png")';" />

At this point, $scope.fileUploadFile2[chIndex][sIndex] is ready to post to the back end, which, in my case, is a C# Controller that accept the entire JSON containing Course Chapters and sections, image binaries and videos, text and html, into a complex class that will in turn store the information into the database schema.

Upvotes: 2

Related Questions