FX_Sektor
FX_Sektor

Reputation: 1460

To change IFormFile to IList<IFormFile> and sent $http.post request. AngularJS, ASP.NET Core

My example works only with one file. I need an array of files. I use angularJS 1.7 and asp.net-core 2.0

HTML

 <div class="post">
    <form>
        <textarea ng-model="Headline" name="Headline" rows="2" cols="63" class="form-control"></textarea>

        <textarea ng-model="BodyText" name="BodyText" rows="4" cols="63" class="form-control"></textarea>

        <input type = "file" file-model = "myFile" multiple/>

        <input id="Submit" class="btn btn-default" ng-click="createPost()" type="submit" value="Пост" />
    </form>
</div>

Method creatPost in AgularJs controller

 $scope.createPost = function () {
        var model = {
            Headline: $scope.Headline,
            BodyText: $scope.BodyText,
            ImgPost: $scope.myFile
        }
        $http({
                method: 'POST',
                url: '/api/Blog/create-post/',
                headers: {
                    'Content-Type': undefined
                },
                data: model,
                transformRequest: function (data, headersGetter) {
                    var formData = new FormData();
                    angular.forEach(data, function (value, key) {
                        formData.append(key, value);
                    });

                    return formData;
                }
            })
            .then(function onSuccess(response) {
                if (response.data = "Ok") {
                    $window.location.reload();
                }
            })


            .catch(function onError(response) {
                console.log("Error")
            });
    }

Model in C#

[ScaffoldColumn(false)]
    public int Id { get; set; }

    [ScaffoldColumn(false)]
    public string User { get; set; }

    [Required(ErrorMessage = "")]
    [StringLength(100, MinimumLength = 1, ErrorMessage = "")]
    public string Headline { get; set; }

    [Required(ErrorMessage = "")]
    [StringLength(1000, MinimumLength = 1, ErrorMessage = "")]
    public string BodyText { get; set; }

    [ScaffoldColumn(false)]
    public DateTime? Date_Time { get; set; }

    public IFormFile ImgPost { get; set; }

And a method signature

    [HttpPost]
    [Route("create-post")]
    public async Task<object> PostSaveOnFile(PostViewModel model)
    {

How I need to change my model in # public IFormFile ImgPost { get; set; } And make changes on my angularJS controller and I shoud be able to sent an array of files. Thanks.

As I understood the problem in my derective of fileReader

(function (angular) {
    angular.module('app').directive('fileModel', ['$parse', function ($parse) {
      return {
         restrict: 'A',
         link: function(scope, element, attrs) {
            var model = $parse(attrs.fileModel);
            var modelSetter = model.assign;

            element.bind('change', function() {
               scope.$apply(function() {
                  modelSetter(scope, element[0].files[0]);
               });
            });
         }
      };
   }]);
})(window.angular);

If I will change in my C# model to IList or IEnumerable it will be only the first file. I did nor understand how to change my derective to array

Upvotes: 1

Views: 952

Answers (2)

Alexander
Alexander

Reputation: 9642

At first you need to update fileModel directive to set an array of files instead of single file. You can use this answer but the resulting array will have FileList type and I suggest you to convert it to Array because it will be more flexible in conjunction with other changes in code

app.directive('fileModel', ['$parse', function ($parse) {
  return {
     restrict: 'A',
     link: function(scope, element, attrs) {
        var model = $parse(attrs.fileModel);
        var modelSetter = model.assign;

        element.bind('change', function() {
           scope.$apply(function() {
              modelSetter(scope, Array.from(element[0].files));
           });
        });
     }
  };
}]);

In order to let ASP.NET Core properly bind an Array (or List) you need to add all array values with the same key to FormData. For instance, if you have

public List<string> Strings { get; set; }

you will need to append the values with Strings key

formData.append("Strings", "value1");
formData.append("Strings", "value2");
formData.append("Strings", "value3");

So you need to update transformRequest function to properly add an array of files to FormData

function (data, headersGetter) {
    var formData = new FormData();
    angular.forEach(data, function (value, key) {
        //you could check if value is FileList
        //but your model can hold array of primitive values like ["val1", "val2", "val3"]
        //this single check for Array covers all cases
        //and you don't need to have different check for FileList
        if (value instanceof Array) {
            for (var i = 0; i < value.length; i++) {
                formData.append(key, value[i]);
            }
            return;
        }

        formData.append(key, value);
    });

    return formData;
}

Upvotes: 2

georgeawg
georgeawg

Reputation: 48968

I did nor understand how to change my derective to array

app.directive('fileModel', ['$parse', function ($parse) {
  return {
     restrict: 'A',
     link: function(scope, element, attrs) {
        var model = $parse(attrs.fileModel);
        var modelSetter = model.assign;

        element.bind('change', function() {
           scope.$apply(function() {
              ̶m̶o̶d̶e̶l̶S̶e̶t̶t̶e̶r̶(̶s̶c̶o̶p̶e̶,̶ ̶e̶l̶e̶m̶e̶n̶t̶[̶0̶]̶.̶f̶i̶l̶e̶s̶[̶0̶]̶)̶;̶
              modelSetter(scope, element[0].files);
           });
        });
     }
  };
}]);

Change the modelSetter call to return an array.

Upvotes: 1

Related Questions