Reputation: 14821
I am developing an Web application using Angular JS and ASP.NET MVC. But I am having a problem with model binding complex type. I am uploading list of files, list of string basically. In the data base, they(list of string and list of file) are the same columns and table because I save the path after file is uploaded. For string I save the same column with file path. For example, a database table has Id(int) and Data(string) columns.
Then I save the string data posted by AngularJS in the Data column, for file path, in the data column as well. But the point is, I need to remember the order. For example, user add a text field and then enter value, then add file field dynamically and then choose file, then add a text field and enter value again. So the order must be [ "text 1 value" , File , "text 2 value" ]. But the problem is we cannot bind the list of data mixed both HttpPostedFileBase for file and string for text value. So what I did was created view models like below.
public class CreateBlogVM
{
[Required]
[MaxLength(250)]
public string Title { get; set; }
[Required]
public ContentModel[] TextContents { get; set; }
[Required]
public ContentFileModel[] Files { get; set; }
}
public class ContentModel
{
public int OrderNo { get; set; }
public string Content { get; set; }
}
public class ContentFileModel
{
public int OrderNo { get; set; }
public HttpPostedFileBase File { get; set; }
}
As you can see in the above, CreateBlogVM will be the ViewModel that I am binding now. That class will have two properties of complex type- TextContents and Files that I explained above what I was doing. So to remember the order, I created a complex type with OrderNo field (Client will pass this value) as you can see above since we cannot bind list of data something like this
[HttpPostedFileBase, String, String, HttpPostedFileBase]
But the problem is when I post data from Angular js, all values are null and not binding the data. Only the "Title" value is binding.
var textContents = new Array();
var photoContents = new Array();
for(var i=0; i<$scope.rows.length; i++)
{
if($scope.rows[i].type=="text")
{
var value = $scope.rows[i].value;
if (value == "" || value == null) {
showAlert("Text field should not be empty", "danger");
return;
}
else {
var content = { OrderNo: i, Content: value }
textContents.push(content)
}
}
else if($scope.rows[i].type=="photo")
{
var file = $scope.rows[i].file;
if(file==null)
{
showAlert("Photo file is required", "danger");
return;
}
else {
var content = { OrderNo: i, File: file };
photoContents.push(file);
}
}
}
var fd = new FormData();
fd.append('Title', $scope.title);
fd.append("TextContents", textContents);
fd.append("Files", photoContents);
$http.post("/Admin/Blog/Create", fd, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
})
.success(function () {
})
.error(function () {
});
Above code is how I submit data to server. When I post data, all values are null and mvc is not binding data. But when I bind values without using complex type like this
public JsonResult Create(HttpPostedFileBase files, String contents, String title)
But if I bind like above, I cannot order the files and string contents. So what is wrong with my code? How can I bind complex data that has list of complex type object properties?
Upvotes: 1
Views: 766
Reputation:
Change the models so that you get a direct relationship between the Content
and the associated File
(the Order
property is unnecessary)
public class CreateBlogVM
{
[Required]
[MaxLength(250)]
public string Title { get; set; }
public List<FileVM> Files { get; set; }
}
public class FileVM
{
[Required]
public string Content { get; set; }
[Required]
public HttpPostedFileBase Image { get; set; }
}
You can only append simple name/value
pairs to FormData
(not arrays of objects). In your loop, append the data using
var fd = new FormData();
fd.append('Title', $scope.title);
for (var i=0; i<$scope.rows.length; i++)
{
....
var content = $scope.rows[i].value;
fd.append('Files[' + i + '].Content', content);
....
var file = $scope.rows[i].file;
fd.append('Files[' + i + '].Image', file);
}
....
so that your generating the names with indexers that relate to your collection property (Files[0].Content
, Files[0].Image
, Files[1].Content
etc)
Then your POST method signature will be
public JsonResult Create(CreateBlogVM model)
Upvotes: 2