Multiple dropzone in one form - javascript

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.

Related

Cascading Dropdown list MVC5 using fluent nhibernate

I have read for 2 days every single question that looks like my problem on here and read multiple pages and tutorials and even watched videos and i just can't understand it or make it work...
What im trying to do is that i have 2 dropdown lists, one is "Departamentos" which you can think of it as a state and "Municipios" which you can think of it as a county. What i need and no matter what i just CAN'T make it work is that when i select a departamento ONLY the municipios from that departamento show up on the dropdown list. Im really a complete noob regarding programming and unfortunately i think i started with something way too big for me, so i' sorry if this is a basic really easy thing to do for you.
The departamento class is :
public virtual int id_departamento { get; set; }
public virtual string descripcion { get; set; }
//i specify Relationship for fluent Nhibernate to municipios since it is a 1-n
public virtual IList<Municipios> Municipios { get; set; }
The municipio class is :
public virtual int id_municipio { get; set; }
public virtual string municipio { get; set; }
public virtual int id_departamento { get; set; }//THis is the FK from Departamento
And heres my main class Sitios where i connect everything to:
This is for the Nhibernate relationships
public virtual Departamentos Departamento { get; set; }
public virtual Municipios Municipios { get; set; }
This is for the lists on that same Sitios class:
public virtual List<Departamentos> Departamentos { get; set; }
public virtual List<Municipios> municipiosLista { get; set; }
Now going to MVC this is the controller i have for the Get create where i populate the lists of Departamento and Municipio to be shown:
using (ISession session = NhibernateHelper.OpenSession())
{
var deptos = session.Query<Departamentos>().ToList();
var munis = session.Query<Municipios>().ToList();
var instanciadelacopia=new Sitios
{
Departamentos = deptos,
municipiosLista = munis
};
return View(instanciadelacopia);
}
And the create view for that specific dropdown part:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Sitios</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<label class="control-label col-md-2"> Departamento </label>
<div class="col-md-10">
#Html.DropDownListFor(model => model.id_departamento, new SelectList(Model.Departamentos, "id_departamento", "descripcion"), "--Select--", new {#id = "depto"})
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2"> Municipio </label>
<div class="col-md-10">
#Html.DropDownListFor(model => model.id_municipio, new SelectList(Model.municipiosLista, "id_municipio", "municipio"), "--Select--", new { #id = "muni" })
</div>
</div>
Everything works fine since it brings me all the values for me to select from the db, where i'm stuck and can't advance is that i need a cascading dropdown for the municipios list that when i select certain departamento ONLY the municipios of THAT selected departamento show up on the list.
So for example i select departamento "atlantida" which it's ID is 1 then only the municipios which have that foreign key from Departamentos = 1 are shown which im guessing Jquery is required.
Would be really good if someone can help me with this, i feel so stressed since i just cant figure out what i need to do. Thanks
All examples i've seen are about using JSON like this one:
http://www.c-sharpcorner.com/uploadfile/4d9083/creating-simple-cascading-dropdownlist-in-mvc-4-using-razor/
but since i already have all the data available on the dropdowns i think i don't need that and instead just a plain jquery function which i cant create.
Solved it with this:
<script type="text/javascript">//Script for Cascading Dropdown
$(function () {
$('#id_departamento').change(function() {
var departmentId = $(this).val() || 0;
$.ajax({
url: '/Sitios/Municipios/',
type: 'POST',
data: { id_departamento: departmentId }, // parametro
dataType: 'json',
success: function (data) {
var options = $('#id_municipio');
$('option', options).remove(); //
options.append($('<option />').val('').text('---'));
$.each(data, function () {
options.append($('<option />').val(this.id).text(this.name));
});
}
});
});
});

How to get collection of IDs, and Selected Dropdown values passed from View's table to Controller?

So I have a checklist/task manager kind of application that basically pulls a bunch of string values from a database using a model, and then pipes it out to a view which uses a expand/collapse patterned table. The only element that an end user modifiers beyond the expand/collapse state is the dopdown list for the project status.
Rather than an update button for each row, the customer wants basically a single button on the bottom of the page which updates all the rows the user changed. So my goal is to try and create said button. As far as the View code snippet, I've tried messing around with Html.BeginForm, but haven't been able to figure out how to also capture the multiple item IDs as well:
#using (Html.BeginForm("UpdateDB", "Checklist", new { id = model.ID })) // model does not exist in this current context
{
<table id="checklistGrid" class="table table-bordered table-striped grid">
<tr>
<th>#Html.DisplayNameFor(model => model.ww)</th>
.... // more table headers
</tr>
#foreach (var group in Model.GroupBy(x => x.ww))
{
<tr class="group-header">
<td colspan="12">
<span class="h2">#group.Key</span>
</td>
</tr>
foreach (var dept in group.GroupBy(x => x.dept))
{
<tr class="dept-header">
<td colspan="12">
<span class="h4">#dept.Key</span>
</td>
</tr>
foreach (var item in dept)
{
<tr class="row-header">
<td> #Html.HiddenFor(modelItem => item.ID)</td>
<td>#Html.DisplayFor(modelItem => item.ww)</td>
.... // more table columns
<td>
#{
List<string> ddl = new List<string> { "Done", "Not Done", "N/A" };
SelectList sl = new SelectList(ddl, item.status);
#Html.DropDownList("newStatus", sl); // Added
}
</td>
<td>#Html.DisplayFor(modelItem => item.applied_by)</td>
<td>
#{
DateTime tmp;
//Check if not null, if not null convert to specified time zone
if (DateTime.TryParse(item.timestamp.ToString(), out tmp))
{
tmp = item.timestamp ?? DateTime.MinValue;
string zoneName = "India Standard Time";
var abbrZoneName = TimeZoneNames.TimeZoneNames.GetAbbreviationsForTimeZone(zoneName, "en-US");
TimeZoneInfo zoneInfo = TimeZoneInfo.FindSystemTimeZoneById(zoneName);
DateTime istTime = TimeZoneInfo.ConvertTimeFromUtc(tmp, zoneInfo);
#Html.Raw(istTime.ToString() + " (" + abbrZoneName.Generic + ")");
}
else
{
#Html.DisplayFor(modelItem => item.timestamp);
}
}
</td>
</tr>
}
}
}
</table>
<p><input type="submit" value="Save Changes" /></p>
}
<script type="text/javascript">
// Hide/Show Dept's Data on Click
$(function () {
$('.dept-header').click(function () {
$(this).nextUntil('.dept-header, .group-header').toggle();
});
});
$(function () {
$('.group-header').click(function () {
var elm = $(this);
if (elm.next().is(":visible")) {
elm.nextUntil('.group-header').hide();
} else {
elm.nextUntil('.group-header', '.dept-header').show();
}
});
});
</script>
I suspect the easier (and computationally quicker way) would to be to use Jquery + Ajax && Json attached to a button onclick at the bottom to send the collection of id and selected dropdown text to an ActionResult in the Controller (to then update the database) and then refresh on the success Ajax callback. However my Jquery/Javascript foo is weak and given the clickable 'header' type rows that expand/collapse the data rows, it's not clear to me how I would efficiently use Jquery to navigate and grab the item.IDand the selected dropdown text from each row, bundle it up, and pipe to the desired ActionResult.
So how do I send both the id and selected dropdown text of all the rows to an arbitrary ActionResult be it with Html.BeginForm or Jquery et al?
EDIT: Model for reference:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace TaskTracker.Models
{
[Table("some_update")]
public class TaskInfo
{
public int ID { get; set; }
[Display(Name = "WW")]
public int ww { get; set; }
[Display(Name = "File Name")]
public string filename { get; set; }
[Display(Name = "Dept")]
public string hsd_unit { get; set; }
[Display(Name = "Owner")]
public string owner { get; set; }
[Display(Name = "Manager")]
public string manager { get; set; }
[Display(Name = "Project")]
public string project { get; set; }
[Display(Name = "Status")]
public string status { get; set; }
[Display(Name = "Last Applied By")]
public string applied_by { get; set; }
[Display(Name = "Last Updated Time Stamp")]
public DateTime? timestamp { get; set; }
}
public class TaskInfoDBContext : DbContext
{
public DbSet<TaskInfo> TaskInfoSet { get; set; }
}
}
You have a number of problems with you view including your form controls have duplicate name attributes meaning you cannot bind to you model when you submit, duplicate id attributes which is invalid html, and your creating a dropdownist with a name that is not even a property of your model. In addition your use of queries in the view code is not good practice. All of this can be solved by using view models and generating your html correctly using for loops (not foreach loops) or a custom EditorTemplate. However, since you have indicated a preference for ajax, you can post the data by making the following changes to the view
Replace the following HtmlHelper methods
#Html.HiddenFor(modelItem => item.ID)
#Html.DropDownList("newStatus", sl)
with
#Html.HiddenFor(modelItem => item.ID, new { id = "", #class = "id" })
#Html.DropDownList("Status", sl, new { id = "", #class = "status" })
This will remove the invalid id attributes and add class names for selection. Then replace the submit button with
<button type="button" id="save">Save Changes</button>
And add the following script
var rows = $('.row-header'); // store all the table rows containing the form controls
$('#save').click(function() {
// Build an array of the values to post back
var data = [];
$.each(rows, function() {
data.push({id: $(this).find('.id').val(), status: $(this).find('.status').val() });
});
$.ajax({
url: '#Url.Action("UpdateDB", "Checklist")',
type: "POST",
data: JSON.stringify({ model: data },
traditional: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
// do something?
}
})
});
Then create a model to accept the values
public class TaskInfoUpdate
{
public int ID { get; set; }
public string Status { get; set; }
}
And modify you POST method to
[HttpPost]
public ActionResult UpdateDB(IEnumerable<TaskInfoUpdate> model)
Side note: Its not clear what you mean by "and then refresh on the success Ajax callback" (what is there to refresh?). I suggest that the method return either return Json(true); if the items were successfully saved, or return Json(null); (or a HttpStatusCodeResult) if not so that you can notify the user as appropriate.

Bind multiple files to array with properties

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.

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.

Find ids of checked checkboxes with jQuery in MVC

I have a Field model, which represents a certain field (Name, Description, ...)
class FieldModel : EntityModel
{
...
public bool ToCopy { get; private set; }
public string Id {get; private set; }
...
}
An Index model, which has a collection of Fields:
class EntityModel
{
...
}
class IndexModel
{
public IEnumerable<EntityModel> Fields { get; private set; }
}
Controller for copy, that should accept ids of fields to copy:
public void CopyFields(string[] fieldsIds)
{
...
}
And I need to select certain fields to copy by checkboxes. So in the vew for Field I added
#Html.CheckBoxFor(x => x.IsSelectedForCopy)
In the view for Index
<button onclick="onCopyClick('#Model');" type="button" class="btn showButton">Copy Fields</button>
And now I need to write a script to select all checked fields and send their Ids to the controller. I have zero experience with Javascript/jQuery, so could someone help me with that?
This should get you started at least ;)
You give jQuery some css selectors and it gives you the objects that match...
$("input :checked").each(function() {
alert($(this).attr("id"));
});
Depending on how you want to send them, you could then append each id to a hidden field on a form like so:
$("input :checked").each(function() {
var tmp = $("#myHiddenField").val();
tmp += " " + $(this).attr("id"));
$("#myHiddenField").val(tmp);
});
$.ajax("TheURLToPostTheDataTo",
{data: [
{idsToSend:$("#myHiddenField").val()}
],
success: function() {
alert("Done");
}
});
Then submit, and on the serverside trim and split by space?

Categories