Bind multiple files to array with properties - javascript

I have a basic HTML form with <input type="file" multiple> inside. For each chosen file I create a description.
Now I want to bind them to PostedPhotoViewModel[] PostedPhotos;:
public abstract class PostedPhotoViewModel
{
public string Description { get; set; }
public HttpPostedFileBase File { get; set; }
}
I don't know how to prepare my input to do such a thing. Is it possible? Or do I have to do some tricks to achieve my target?
#Html.TextBoxFor(m => m.PostedPhotos, new { #name = "PostedPhotos", type = "file", multiple="multiple" })
I tried to force it in such a way, but didn't work:
myForm.submit(function(e) {
myInput.files = $.map(myInput.files, function(element) {
return {File: element, Description: "Test description"}
});
return true;
});
It's basic ASP.NET MVC 5 project.

I would replace this:
#Html.TextBoxFor(m => m.PostedPhotos, new { #name = "PostedPhotos", type = "file", multiple="multiple" })
With just:
<input type="file" name="files" multiple="multiple" />
Then in the controller do something like:
[HttpPost]
public ActionResult Index(IEnumerable<HttpPostedFileBase> files)
I think the nested view model list binding with a textbox property is making it far more complicated than it is.

Related

Using C# MVC multiple dynamic models in View

I have a View with several form that I'm using for searching and displaying the results as partial View in like SearchByNumber, SearchByVehicle, etc.
I'm trying to load view and execute search for different forms by posting link with querystring like www.example.com/Search?number=101010 from different view.
For the first form, SearchByNumber I only have one parameter, string number and i'm returning view with dynamic Model and its working like it should, but I only manage to make search for this form.
Here is my controller:
public ActionResult Index(string number)
{
return View(model: number);
}
and in the View I have:
<form id="searchbynumberform">
Search By Any Number:
<div class="input-group input-group-sm">
<input type="text" class="form-control" name="number" id="number" value="#Model">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" name="numbersearch" id="numbersearch" disabled>
Search
</button>
</span>
</div>
</form>
My Question is, if anyone can help me, How to perform search let's say on the second form where I have int type and string name parameters?
Thank You in advance...
At the moment your Model is only the search string that was entered, which seems rather incomplete. It would make a lot more sense if the Model also contained the actual search results, which after all is what the user wants to see. And then you can also add the other search properties.
The MVC approach for this is to create a (View)Model class, somewhere in your project, something like this:
public class SearchModel
{
public string Number { get; set; }
public int? Type { get; set; }
public string Name { get; set; }
public List<SearchResult> SearchResults { get; set; }
}
And then use it e.g. like this:
public ActionResult Index(string number)
{
var model = new SearchModel
{
Number = number,
SearchResults = GetByNumber(number)
};
return View(model);
}
public ActionResult IndexOther(int type, int name)
{
var model = new SearchModel
{
Type = type,
Name = name,
SearchResults = GetByTypeAndName(type, name)
};
return View(model);
}
And in your Index.cshtml:
#model SearchModel
#* You can now use Model.Number, Model.Type, Model.Name and Model.SearchResults. *#

Multiple dropzone in one form

I'm using dropzone in one single page. In fact, user can add dynamically one object that contains DropZone for instance one City can have N houses and for each house, I let the user send files trough DropZone.
The problem is that I can't bind the uploaded files to the ASP model. At the moment it doesn't even reach the controller.
Here is the HTML it generates:
<div class="house0">
<div class="dropzone dz-clickable" id="houseDropzone0">
<div class="dz-default dz-message" data-dz-message="" style="display: block;">
<span>Drop files here to upload</span>
</div>
</div>
</div>
<div class="house1">
<div class="dropzone dz-clickable" id="houseDropzone1">
<div class="dz-default dz-message" data-dz-message="" style="display: block;">
<span>Drop files here to upload</span>
</div>
</div>
</div>
Here is the Javascript I've done:
//Foreach houses, create a dropzone element and stock it in the table
var dropzones = [];
var housesList= #Html.Raw(Json.Encode(Model.housesList));
for (var i = 0; i < housesList.length; i++) {
//create the dropzone for the house
var currentHouse = housesList[i];
dropzones.push(createHouseDropzoneForId(currentHouse ,i));
}
//Instanciate each dropzone
function createActionDropzoneForId(id) {
return new Dropzone("#actionDropzone" + id,
{
url: "/houseUrl/" + id,
paramName: 'houseList[' + id+ '].files',
autoProcessQueue: false
});
}
//Handle the submit event to process the files alongside the data
$("input[type=submit]").on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var form = $(this).closest('form');
if (form.valid() == true) {
var dropzones = dropzones;
dropzones.forEach(function (element) {
if (element.getQueuedFiles().length > 0) {
element.processQueue();
} else {
element.uploadFiles([]); //send empty
}
})
}
});
Here is the model that should be binded (in my ASP controller):
CITY Class:
public class City
{
public List<Houses> housesList { get; set; }
// Other properties as postal code, name, etc
}
HOUSE Class:
public class House
{
public HttpPostedFileBase[] files { get; set; }
// Other properties as color, name, etc
}
One way to fix this is to ensure your razor view contains the #using(Html.BeginForm) directive that will bind the Dropzone elements to the model.
I noticed in your definition of the Dropzone element you are using:
...
paramName: 'houseList[' + id+ '].files',
...
This should be the cause of the problem as your model currently expects Dropzones with this configuration:
...
paramName: 'files',
...
To fix this I suggest you augment your model to support multiple dropzones by defining the following properties in the model:
public HttpPostedFileBase[] houseList-1-files { get; set; }
public HttpPostedFileBase[] houseList-2-files { get; set; }
public HttpPostedFileBase[] houseList-3-files { get; set; }
Also modify the dropzone definition to:
...
paramName: 'houseList-' + id+ '-files',
...
Then you can modify the received HttpPostedFileBase objects to fit into your usage of:
public List<Houses> housesList { get; set; }
by instantiating new House objects.

Set path of selected file to model in view

I am trying to set the full path of the selected file into the model in the view.
Controller FileController:
public async Task<IActionResult> Create(CreateFileViewModel model)
{
if (ModelState.IsValid)
{
var file = new File
{
Path = model.Path
};
_context.Add(file);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(model);
}
Model CreateFileViewModel:
public class CreateFileViewModel
{
public string Path { get; set; }
}
Model File:
public class File
{
public int Id { get; set; }
public string Path { get; set; }
}
ViewForm Create:
<form asp-action="Create">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Path" class="control-label"></label>
<input asp-for="Path" id="selectedFile" type="file" />
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
Script in Create:
<script>
document.getElementById('selectedFile').onmouseout = function () {
#Model.Path=this.value;
};
</script>
But
#Model.Path=this.value;
this not working. Ofc I cannot convert between razor and javascript variables. But I don't know another way how to set full path of selected file into the model variable.
This
<input asp-for="Path" id="selectedFile" type="file" />
set into model variable just file name, without a path.
#Model.Path is server side i.e. c# code, while this.value is java script code which is client side code, so the server side code will get executed when view is rendered while your js code with execute on particular event in your html.
What you need is to update the hidden value via javascript and it will post back in controller with updated value and will also work in html with the updated value.
Your hidden field will be rendered with id Path, so you can write :
document.GetElementById("Model").value = this.value;
or if you have jquery library included in your application, then you can make use of that as well:
$("#Path").val(this.value);
This way when the model will get posted back to controller, the Path property will have the updated value which would be the path you have assigned via js code.
Hope it helps!

Order dropdown list in ASP.Net mvc 5 application

I have a dropdown and I want it to appear in a particular order where dropdown option header should come before footer. but I am not able to do so.
Helper
public static readonly string HEADER_ID = "-1000";
public static readonly string FOOTER_ID = "-1001";
CSHTML
<select id="simTextEditorSelection" onchange="ShowTextEditorBasedOnSelection();" style="float:right;">
#foreach (PageInfoMV anItemForEditor in Model.ItemContents)
{
<option value="#anItemForEditor.ItemId">#anItemForEditor.ItemDisplayText</option>
}
UI
P.S: I don't want to change the enum values of Header and footer. Please guide me.
My Attempt:
#foreach (PageInfoMV anItemForEditor in Model.ItemContents.OrderByDescending(x=>x.Id))
But it created some other issues. So, I want to avoid it.
Assume you have this class:
public class MyClass
{
public int Id { get; set; }
public int Name { get; set; }
}
In the Action you can create a SelectList from any list as followed:
public ActionResult Create()
{
var list = db.MyClass.ToList(); //or an other way to get the list of items
//you can add som more items to the list:
list.Add(new MyClass { Id=-1000, Name="Some Name" });
ViewBag.Selectlist = new SelectList(list.OrderByDescending(x=>x.Id), "Id", "Name");
return View();
}
In the View:
#{
var optionList = ViewBag.Selectlist as SelectList;
}
#Html.DropDownListFor(model => model.someProperty, optionlist, "- Select an option -")
Change your cshtml to this and you should have your wish
<select id="simTextEditorSelection" onchange="ShowTextEditorBasedOnSelection();" style="float:right;">
#foreach (PageInfoMV anItemForEditor in Model.ItemContents.OrderByDescending())
{
<option value="#anItemForEditor.ItemId">#anItemForEditor.ItemDisplayText</option>
}

Return a Javascript array to action

Here's my problem:
i have a JS array that i want to pass it to an action and then render that action's View.
this is my array and i fill it Using Jquery like so :
var factorlist = [];
factorlist.push({ ID: data.ID, Name: data.Name, number: data.number, SingelPrice: data.SingelPrice, TotalPrice: data.TotalPrice })
(data come from an AJAX Call)
then i put a hidden input element in my page to put my array in it and send it with a submit.
here is hidden input and submit button :
<input type="submit" id="input" name="input" />
<input type="hidden" id="list" name="list"/>
This is how i send it :
$('form').submit(function (event) {
$('#list').val(JSON.stringify(factorlist));
});
and this is the action that i'm sending the array to :
public ActionResult PrePayment(List<Order> list)
{
return View(list);
}
and my order class :
public class Order
{
public int ID { get; set; }
public string Name { get; set; }
public int number { get; set; }
public float SingelPrice { get; set; }
public float TotalPrice { get; set; }
}
**Now the thing is i get and empty list in action not null...what is the problem and is there any other way to do this? **
The default MVC data binding will not work for you in this case, you have to deserialize your values manually:
var js = new JavaScriptSerializer();
list = (Order[])js.Deserialize(Request.Form["list"], typeof(Order[]));
To avoid doing this every time you can register a Custom Model Binding for that type too, check an example in http://www.codeproject.com/Articles/605595/ASP-NET-MVC-Custom-Model-Binder
You should use Json instead of JS array. More details http://msdn.microsoft.com/en-us/library/system.web.mvc.jsonresult(v=vs.118).aspx
if i understand you right, you want to post your js array from form to controller action, so basing on this article there is a solution:
$('form').submit(function (event) {
$(factorlist).each(function (i, el) {
$('form').append($('<input />', {
type: 'hidden',
name: 'list[' + i + '].ID',
value: el.ID
}));
// and so on for each your of objects property
});
});
More correct way is to do it using ajax and redirect on success callback.

Categories