MVC Model Validation on a dynamic form? - c#

I have the following Model :
public class FileModel
{
public int Id { get; set; }
[Required(ErrorMessage = "Required")]
[StringLength(100, ErrorMessage = "Max is 100, Min is 3", MinimumLength = 3)]
public string Name { get; set; }
public string Path { get; set; }
[Required(ErrorMessage = "Required")]
public string FileTypeId { get; set; }
public DateTime RegistrationDate { get; set; }
}
the following is my view :
#using (Html.BeginForm("Index", "FileManagement", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<table class="content-table" style="min-width: 600px; border-spacing: 15px;">
<tr>
<td colspan="4" class="table-header">New File
<div class="add-icon">+</div>
</td>
</tr>
<tr>
<td>Name: </td>
<td>#Html.TextBoxFor(q => q.NewFile.Name, new { maxlength = "100", id = "NewFile_Name1", name = "NewFile.Name1" })
<br />#Html.ValidationMessageFor(q => q.NewFile.Name)
</td>
<td>
<input type="file" id="FileUploadField1" /></td>
<td style="width: 16px; text-align: center;"> </td>
</tr>
<tr>
<td colspan="4" style="text-align: center;">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
<script type="text/javascript">
$('.content-table .add-icon').click(function () {
var lastFileField = $('.content-table input[type="file"]').last();
var lastId = lastFileField.attr('id').replace(/\D*/g, '');
lastId = parseInt(lastId) + 1;
var newFields = '<tr>' +
'<td>Name : </td>' +
'<td><input data-val="true" data-val-length="Max chars is 100, Min chars is 3" data-val-length-max="100" data-val-length-min="3" data-val-required="Required" id="NewFile_Name' + lastId + '" name="NewFile.Name' + lastId + '" type="text" value="" /><br /><span class="field-validation-valid" data-valmsg-for="NewFile.Name' + lastId + '" data-valmsg-replace="true"></span></td>' +
'<td><input type="file" id="FileUploadField' + lastId + '"/></td>' +
'<td style="text-align:right;"><div class="delete-icon"></div></td>' +
'</tr>';
var lastTr = $(lastFileField).parents('tr:first')[0];
$(lastTr).after(newFields);
});
$('.content-table .delete-icon').live('click', function () {
$(this).parents('tr:first').remove();
});
</script>
}
As you can see, We have a form for uploading one or more files. So, I've added an + button for users that they can add a file field to form.
Users must enter the name of the file and select a file for uploading. But MVC client validator just validate the first inputs that added with Razor.
How can I force MVC validator to validate all fields at the client side and server side.
Another question is:
How can we handle getting the field values at a MVC Controller.
Thanks

This great blog will help you understand how the default model binder will bind lists and arrays. Just make a ViewModel for your page that looks somewhat like this:
public class FileUploadViewModel
{
public List<FileModel> lFileModels { get; set; }
}
Then in your "+" jQuery function, make sure the generated input names are something like lFileModels.[0].Title (or it might be lFileModels[0].Title, just click that link and you'll figure it out)
Then to get that info in the controller, it's just like any other form!
[HttpPost]
public ActionResult Index(FileUploadViewModel viewModel)
{
}
Edit
Another link for MVC file uploading
Edit 2
After reading your code once again, I now realise that the validation problem is due to adding unobtrusive validations after the library was loaded. You have to re-parse the validators.
$("form").data("validator", null);
$.validator.unobtrusive.parse($("form"));
The binding applies to server-side validation and the 2nd part of your question.
Edit 3
For adding fields, instead of doing it straight in JS, you should Ajax load the section of your form with the file fields. When you click on the add button, it request a partial view of the file fields with, in it's post data, a list of the current items. The partial view then returns a rendered view with an extra field. It's just an idea. I haven't tried or even seen that idea. I just though about it and figured I could share it.

Related

asp.net mvc getting data from textbox on view back to stored procedure to edit record

I am new to ASP.NET but have done 20 years of desktop dev. I have a Customer class with fields CustId, CustName, CustNotes.
I have a view called CustView that has some input boxes that are to be pre-populated with customer details from a previous view using the CustID.
I can show the customer details in the text boxes but I cannot get the user edited textboxes (ie the user changes the name of a customer) back to a stored procedure in an Action.
I use a DB class called Cust1DBHandle to call the stored procedure. I have set up 3 buttons and 3 actions in a bid to get any of them to work, either by passing variables or using RequestString but nothing works.
Question #1: how can I pass the new text values back as either global variables, variables in the action or using a datamodel?
Question #2: in the CustViewDBHandle, I populate a list of the results. Is that the correct thing to do if it’s only for one row of data?
Question #3: when pressing a SAVE button that executes a stored procedure, do you have to have a return in the action in the controller?
Thanks.
Cust.cs model
public partial class Cust
{
[DisplayName("Cust ID")]
public Int32 CustID { get; set; }
[DisplayName("Customer Name")]
// [Required(ErrorMessage = "This field is required")]
public string CustName { get; set; }
[DisplayName("Customer Notes")]
public string CustNotes { get; set; }
public string ErrorMessageCust { get; set; }
}
CustView.cshtml:
#model IEnumerable<App22.Models.Cust>
#{
ViewBag.Title = "CustView";
}
<h2>#ViewBag.Title.</h2>
<h3>#ViewBag.Message</h3>
<header>
</header>
<meta name="viewport" content="width=device-width" />
<title>CustViewy</title>
<html>
<head>
</head>
<style>
th, td {
padding: 5px;
}
</style>
<body>
<p>
</p>
<p>CUSTOMER DETAILS ARE HERE</p>
<form name="1" method="post">
<fieldset>
<legend>Headline Details</legend>
<table>
#foreach (var item in Model)
{
<tr>
<td>
<label for="CustID">CustID:</label>
</td>
<td>
<input type="text" name="1CustID" value="#Html.DisplayFor(modelItem =>
item.CustID)" />
</td>
</tr>
<tr>
<td>
<label for="CustName">CustName:</label>
</td>
<td>
<input type="text" name="2CustName" value="#Html.DisplayFor(modelItem
=> item.CustName)" />
</td>
</tr>
<td>
<label for="CustNotes">Cust Notes:</label>
</td>
<td>
<input type="text" name="3CustNotes" value="#Html.DisplayFor(modelItem =>
item.CustNotes)" />
</td>
<tr>
<td></td>
<td>
<input type="submit" name="action:Save1" value="Save" />
<button style="background-color:red" type="button" name="tree0"
class="btn btn-primary" onclick="location.href='#Url.Action("SaveCust0","Cust1")'">
SAVE0 »
</button>
<button style="background-color:blue" type="button" name="tree1"
class="btn btn-primary" onclick="location.href='#Url.Action("SaveCust1","Cust1",new { CustyIDSave = item.CustID , CustyNameSave = item.CustName })'">
SAVE1 »
</button>
<button style="background-color:green" type="button" name="tree2" class="btn btn-primary" onclick="location.href='#Url.Action("SaveCust2","Cust1")'">
SAVE2 »
</button>
</td>
<td>
</td>
</tr>
}
</table>
</fieldset>
</form>
</body>
</html>
Cust1Controller.cs:
public class Cust1Controller : Controller
{
public ActionResult SaveCust0()
{
string message = "";
message = Request.Form["CustName"].ToString();
return Content(message);
CustViewDBHandle dbhandle = new CustViewDBHandle();
ModelState.Clear();
dbhandle.SaveCust(Convert.ToInt32(Request.Form["CustID"]),
Request.Form["CustName"].ToString());
}
public ActionResult SaveCust1(int CustyIDSave, string CustyNameSave)
{
CustViewDBHandle dbhandle = new CustViewDBHandle();
ModelState.Clear();
dbhandle.SaveCust(CustyIDSave, CustyNameSave);
return null;
}
[HttpPost]
public ActionResult SaveCust2(int CustyIDSave, string CustyNameSave)
{
CustViewDBHandle dbhandle = new CustViewDBHandle();
ModelState.Clear();
dbhandle.SaveCust(CustyIDSave, CustyNameSave);
return null;
}
// GET: Cust1
public ActionResult Index()
{
Cust1DBHandle dbhandle = new Cust1DBHandle();
ModelState.Clear();
return View("~/Views/Cust/Cust1.cshtml",dbhandle.GetCust(""));
// return View("~/Views/Cust/Cust1.cshtml"); //This works
}
[HttpGet]
public ActionResult Reload(string tree)
{
//tree = "Breaker2";
Cust1DBHandle dbhandle = new Cust1DBHandle();
ModelState.Clear();
return View("~/Views/Cust/Cust1.cshtml", dbhandle.GetCust(tree));
//Cust1DBHandle dbhandle = new Cust1DBHandle();
//ModelState.Clear();
//return View("~/Views/Cust/Cust1.cshtml", dbhandle.GetCust(SearchBy));
// return View("~/Views/Cust/Cust1.cshtml"); //This works
}
public ActionResult ViewCust(int CustyIDView)
{
//tree = "Breaker2";
CustViewDBHandle dbhandle = new CustViewDBHandle();
ModelState.Clear();
return View("~/Views/Cust/CustView.cshtml", dbhandle.GetCust(CustyIDView));
//Cust1DBHandle dbhandle = new Cust1DBHandle();
//ModelState.Clear();
//return View("~/Views/Cust/Cust1.cshtml", dbhandle.GetCust(SearchBy));
// return View("~/Views/Cust/Cust1.cshtml"); //This works
}
}
CustViewDBHandle.cs:
public class CustViewDBHandle
{
// ********** VIEW CUSTOMER DETAILS ********************
public List<Cust> GetCust(int CustyID)
{
GlobalVar.connection();
List<Cust> CustyChosen = new List<Cust>();
SqlCommand cmd = new SqlCommand("psv_CustView1", GlobalVar.con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#CustyID", CustyID);
SqlDataAdapter sd = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
GlobalVar.con.Open();
sd.Fill(dt);
GlobalVar.con.Close();
foreach (DataRow dr in dt.Rows)
{
CustyChosen.Add(
new Cust
{
CustID = Convert.ToInt32(dr["CustID"]),
CustName = Convert.ToString(dr["CustName"]),
CustNotes = Convert.ToString(dr["CustNotes"]),
});
GlobalVar.GlobCustName1 = Convert.ToString(dr["CustName"]); //This method uses
Global Var to get data to pass to form. Can pass anything that way.
}
return CustyChosen;
}
public int SaveCust(int CustyID, string CustyName)
{
GlobalVar.connection();
SqlCommand cmd = new SqlCommand("psu_CustSave1", GlobalVar.con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#CustyID", CustyID);
cmd.Parameters.AddWithValue("#CustyName", CustyName);
GlobalVar.con.Open();
cmd.ExecuteNonQuery();
//SqlDataAdapter sd = new SqlDataAdapter(cmd);
//DataTable dt = new DataTable();
//sd.Fill(dt);
GlobalVar.con.Close();
return 1;
}
}
There is a lot to parse here, but what strikes me as the main part of the problem you have noted is:
a) You methods on the controller are not all decorated with HttpPost attributes.
b) The action on the form that you render will be looking for a POST endpoint with a name that matches the get
That said, you're going to find it hard to get answers for how to make this work, given that this is very non-idiomatic asp.net MVC code.
I would strongly suggest working through a few tutorials, as web dev is considerably different to windows dev, with a different set of challenges. Hopefully your experience will let you skim through that quickly.

How to increment a value within a database column by button click

As part of the project that I'm working on, users can create a post and then other users are able to click on a "like" or "dislike" button.
The code below is the Post.cs class responsible for adding the tables to the database.
public class Post
{
//The post ID
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int postId { get; set; }
// Foreign key to customer
public string Id { get; set; }
public string Email { get; set; }
public string postTitle { get; set; }
public string postBody { get; set; }
public string postDepartment { get; set; }
public string postCategory { get; set; }
public bool postAnonymous { get; set; }
public int postLikes { get; set; }
public int postDislikes { get; set; }
public DateTime postDate { get; set; }
}
The following code is the back-end C# code that's linked to either buttons.
protected void btnLike_Click(object sender, EventArgs e)
{
}
protected void btnDislike_Click(object sender, EventArgs e)
{
}
I am trying to get the buttons to increase the integer value by +1 on the database per each click and the users should only be able to click on either one without being able to like // dislike more than once.
How would I go about trying to do this successfully using the asp.net webforms.
<------------------EDIT------------------------->
protected void btnLike_Click(object sender, EventArgs e)
{
var postIDforLike = // add logic to get the id of the post to increment the likes
using (var _dbContext = new ApplicationDbContext())
{
var addLikeSql = "update post set postLikes = postLikes + 1 where postID = #id";
var paramID = new SqlParameter("#id", postIDforLike);
_dbContext.Database.ExecuteSqlCommand(addLikeSql, paramID);
}
}
<--------------------------EDIT2----------------------->
<asp:Button ID="btnLike" class="btn btn-primary" runat="server" Text="👍 Like" Width="99.99px" OnClick="btnLike_Click" />&nbsp <asp:Button ID="btnDislike" Width="99.99px" class="btn btn-primary" runat="server" Text="Dislike 👎" OnClick="btnDislike_Click" />
</div>
<br />
<%--------------------------------------
Inserting Comment Information
--------------------------------------%>
<div class="col-md-12">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title text-center">Add Comment</h3>
</div>
<div class="panel-body">
<fieldset>
<table class="nav-justified">
<tr>
<td class="modal-sm" style="width: 237px; height: 21px;">
<label for="commentBody" class="col-lg-2 control-label">Comment:</label></td>
<td style="width: 434px; height: 21px;">
<asp:TextBox Width="400px" style="resize:none;" class="form-control" ID="commentBody" runat="server" placeholder="Body" TextMode="MultiLine"></asp:TextBox>
</td>
<td style="height: 21px">
<asp:RequiredFieldValidator controltovalidate="commentBody" ID="commentBodyValidator" runat="server" ErrorMessage="*Comment is required" ForeColor="Red"></asp:RequiredFieldValidator>
</td>
</tr>
</table>
<br />
<table class="nav-justified">
<tr>
<td style="height: 21px; width: 511px">
<label for="commentAnonymous" class="col-lg-2 control-label" style="left: 0px; top: 0px; width: 538px">Would you like this comment to be submitted anonymously?:</label></td>
<td style="height: 21px; width: 104px">
<asp:RadioButtonList ID="commentAnonymous" runat="server" BorderStyle="None" CellPadding="0" CellSpacing="0">
<asp:ListItem Value="1" Text="Yes">Yes</asp:ListItem>
<asp:ListItem Value="0" Text="No">No</asp:ListItem>
</asp:RadioButtonList>
</td>
<td style="height: 21px"><asp:RequiredFieldValidator controltovalidate="commentAnonymous" ID="commentAnonymousValidator" runat="server" ErrorMessage="*Please select an option" ForeColor="Red"></asp:RequiredFieldValidator>
</td>
</tr>
</table>
<br />
<table class="nav-justified">
<tr>
<td class="modal-sm" style="width: 408px"> </td>
<td>
<button type="reset" class="btn btn-default">Cancel</button>
<asp:Button class="btn btn-default" ID="commentSubmitBtn" runat="server" autopostback="false" onclick="AddComment" Text="Submit" />
</td>
<td> </td>
</tr>
</table>
</fieldset>
<hr>
<table class="display" id="commentsTable">
<thead>
<tr>
<th>Comment</th>
<th>User</th>
<th>Date</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
I assume that you use Entity Framework (based on the attributes used in the post class).
The easiest way to do that is to load the entity, increment the value and then call SaveChanges. But this is very inefficient and also dangerous, if you don't use a locking mechanism.
What you are probably looking for is a
update post set postLikes = postLikes + 1 where postID = #id
command. This would already be more efficient, because you do not load the whole post every time before updating the Likes value. You can execute Sql Commands like this using Database.ExecuteSqlCommand of your context.
Another possible solution would be to change your DB Model by adding a Like and Dislike Table where you add a record for every like/dislike. To get the current count of likes, you have to count the number of records associated to the post. This adds overhead, but has the advantage that you don't create a bottleneck because the database has to lock your post record every time you want to update the like field.
//You need to have a new table that handle user-post-like
// UserLike Table which have UserId int PostId int Like or dislike so you dont need Likes and Dislikes attributes on entity!
//Suppose that, this operation can be done by user logged in.
protected void btnLike_Click(object sender, EventArgs e)
{
//you need to get postId while button clicked...
DBContext db = new DbContext();
var user = Session["loggedUser"] as User ;
var didUserLike = db.Post.Where(p=>p.PostId == postId).Select(x=>x.UserId == user.UserId).FirstOrDefault());
if(didUserLike.Count() > 0){
//Operation like enable button disable button!
} //and if you want to do it for dislike..yes you can
}
Sorry i don't have studio or code here i cant control this.But i think you understand structure. Have a good day.
Edited(Added):
and in view you dont need to show from attribute of your enetiyt because this can be calculated with query. This is not right for database design.
and you can do it like ;
public int likeCountForPost(int id){
var postLikes = db.PostLikes.Where(x=>x.PostId == id && x.PostLike==1).Count();
return postLikes;
}
You also can do it by changing just 1 to 2 for return count of dislikes.

Ajax.BeginForm Not working correctly in FireFox (Only submits Post once)

I am using Ajax.Begin form with a partial view to replace contents of defined target.
like so,
Partial View:
#model string
<fieldset style="width:916px">
<legend class="SearchFiledSet">AMN</legend>
<table>
<tr valign="top">
<td>Notes:&nbsp</td>
<td>#Html.TextArea("Notes", #Model, 5, 30, new { disabled = "disabled", style = "background: #FFF; border: 1px solid #000;" })</td>
#using (Ajax.BeginForm("AMN", new AjaxOptions { UpdateTargetId = "AMN",
Url = Url.Action("AMN"),
OnBegin = "OnBegin",
OnFailure = "OnFailure",
OnSuccess = "OnSuccess",
OnComplete = "OnComplete"}))
{
<td style="padding-left: 30px">P N: &nbsp #Html.TextBox("PN", "", new { size = "15" })
#if(string.IsNullOrEmpty(#Model))
{
<br />
<font color="red">No matching pn was found in the system.</font>
}
</td>
<td style="padding-left: 60px">
<button type = "submit">Search</button>
</td>
}
</tr>
</table>
</fieldset>
Controller:
public PartialViewResult AMN(string PN = null)
{
IPS p=null;
string remarks= " ";
if (PN != null)
{
p = _pS.GetPPN.Trim());
remarks = p != null ? p.Remarks : remarks;
}
return PartialView(((object)remarks));
}
Main View:
<div id="AMN" style="margin-left: 180px">
#Html.Action("AMN")
</div>
The Ajax calls work fine in IE of course, but in Firefox it hits the break point on the controller and correctly posts during the first submit but then nothing will happen after each consecutive submit. Not even the break point will get hit. I have seen a few other posts of people complaining of this same issue a few years ago but none of them had a resolution. Has anyone experienced this issue and found a resolution or have any recommendations of what can be the issue?
There is another Html.BeginForm on the main page that I link my partial view to, but my partial view is outside that form, and I also tried removing the other form and just leaving the ajax one with no luck.
I am using jquery-1.7.2
I think I now understand what is happening based off of general research on the topic rather than directing it to Ajax.BeginForm method. I wanted to basically mimic a the concept of a panel, and be able to just plug in the full form and replace the panel (partial view) with updated data on the ajax call. Well I am not that experienced with ajax or javascript, but it seems that when I rewrite the html the object on the dom is getting replaced too so all focus is lost, hence it worked on one post but not twice.
This was confusing mostly because it worked the way I originally thought it would on Internet Explorer but not Firefox. So in order to make it cross-browser compatible I just used JSON to send back the data to be changed and then registered a function to the OnSuccess call, which will just change the html necessary rather than rebuilding the partial. I wanted to handle the Ajax mostly with the Asp.net MVC framework libraries to keep the code cleaner but I guess this isn't likely to happen unless I abstract out the form contents from the partial.
Here is the changes made for anyone else who runs into this issue:
Controller:
[HttpGet]
public PartialViewResult AMN()
{
string remarks = " ";
return PartialView(((object)remarks));
}
[HttpPost]
public JsonResult AMN(string PN = null)
{
IPS p=null;
string remarks= " ";
if (PN != null)
{
p = _pS.GetP(PN.Trim());
remarks = p != null ? p.Remarks : null;
}
return Json(remarks);
}
PartialView:
#model string
<fieldset style="width:916px">
<legend class="SearchFiledSet">AMN</legend>
<table>
<tr valign="top">
<td>Notes:&nbsp</td>
<td>#Html.TextArea("Notes", #Model, 5, 30, new { disabled = "disabled", style = "background: #FFF; border: 1px solid #000;" })</td>
#using (Ajax.BeginForm("AMN", "PS", null, new AjaxOptions {OnSuccess = "processData" }, new { id = "AMNForm" }))
{
<td style="padding-left: 30px">PN: &nbsp #Html.TextBox("PN", "", new { size = "15" })
#if(string.IsNullOrEmpty(#Model))
{
<br />
<font color="red">No matching pn was found in the system.</font>
}
</td>
<td style="padding-left: 60px">
<button type = "submit">Search</button>
</td>
}
</tr>
</table>
</fieldset>
<s.. type="text/java..">
function processData(data) {
$("#Notes").val(data);
if (!data[0])
alert("data is null")
else
alert("data is not null")
}
</..>

Post List<Model> from form in MVC

I have a need to be able to post back an unknown number of this model class:
public class UserRegisterModel
{
public string UserName { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public string RepName { get; set; }
public string ContactNumber { get; set; }
}
in a List<UserRegisterModel> to a controller.
I need my view to have a form that can send back multiple instances of the model to the controller, so for example if my user wants to add 3 Reps, he can make 3 reps and give them each details and usernames and passwords, and send them all back at once in a List.
I have this starting point for my view code:
#model List<PicsWebApp.Models.UserRegisterModel>
#{
ViewBag.Title = "NewRep";
}
<h2>NewRep</h2>
#using (Html.BeginForm("NewRep", "Admin", FormMethod.Post, new { id = "fieldform", #class = "form" }))
{
}
Can anybody show me the correct way of accomplishing this? I know I need javascript in order to dynamically add more form elements if the user clicks "add new rep" so this is mainly about the C#.
you need to make sure the properties' ID's conform to the standard set (I used the link from Felipe's comment when I was doing this). I did this before, where a table row would get added when the user click a button. I found the best way is to add an entry before the page gets rendered, then taking the HTML for that, and using it in your jquery.
Here's a function I've used:
$("#nameAdd").click(function (e) {
var index = $("#nameTable tr").length - 1;
e.preventDefault();
var newItem = $("<tr> \
<td> \
<span id='Names_" + index + "__FullName' /> \
</td> \
<td> \
<input class='table-editor-small update-name' id='Names_" + index + "__FirstName' name='Names[" + index + "].FirstName' type='text' value='' /> \
</td> \
<td> \
<input class='table-editor-small update-name' id='Names_" + index + "__MiddleName' name='Names[" + index + "].MiddleName' type='text' value='' /> \
</td> \
<td> \
<input class='table-editor-small update-name' data-val='true' data-val-required='Person Last Name Required' id='Names_" + index + "__LastName' name='Names[" + index + "].LastName' type='text' value='' /> \
</td> \
<td class='center'> \
<input data-group='nameRadios' id='Names_" + index + "__IsPrimary' name='Names[" + index + "].IsPrimary' type='radio' value='True' /> \
</td> \
<td class='center'> \
<input data-val='true' data-val-required='The Delete? field is required.' id='Names_" + index + "__ToDelete' name='Names[" + index + "].ToDelete' type='checkbox' value='true' /> \
<input name='Names[" + index + "].ToDelete' type='hidden' value='false' /> \
</td> \
</tr>");
$("#nameTable").append(newItem);
}
again, the HTML to add is straight from an item that had been rendered by the view, i just swapped in the index

Using jQuery to post back to Controller

In my web page, I have a series of tables that basically just contain rows of information. Each of these is given an id in a for loop and I'm trying to reference them from outside that. I added classes to both the table and a 'Save Changes' button.
Essentially, my goal is for the user to be able to drag and drop rows around, thereby changing the order. Then they can click the 'Save Changes' button and this will post back to the server with the relevant information.
I am having trouble matching up the button to the relevant table and thereby submitting the id's of each row back to the server in an array. I have written the code to be able to be able to get the ids from each of the tables and their current order, but I don't know how to assign this to an array from within the button click jQuery.
Here is the View:
#foreach (var collection in Model.Collections)
{
<h2>#collection.Season</h2>
#Html.ActionLink("Delete Collection", "DeleteCollection", new { controller = "Edit", brand = collection.Brand.Name, season = collection.Season })
#Html.ActionLink("Edit Collection", "EditCollection", new { controller = "Edit", brand = collection.Brand.Name, season = collection.Season })
#Html.ActionLink("Add Image", "CreateImages", new { controller = "Edit", season = collection.Season })
<p>
To change the ordering of images, drag and drop to your desired position and then click the Save Changes button on the appropriate collection.
</p>
<table class="table-collection" id="table-#collection.Id">
<tr class="nodrop nodrag">
<th>
Id
</th>
<th>
Description
</th>
<th>
Image
</th>
<th>
Options
</th>
</tr>
#foreach (var image in collection.Images)
{
<tr id="#collection.Id-#image.Id">
<td class="dragHandle showDragHandle">
#image.Id
</td>
<td>
#image.Description
</td>
<td>
<img src="#Url.Content("~/" + image.Location)" alt="#image.Description" />
</td>
<td>
<ul>
<li>
#Html.ActionLink("Edit", "EditImage", new { controller = "Edit", brand = image.Collection.Brand.Name,
season = image.Collection.Season, imageId = #image.Id } )
</li>
<li>
#Html.ActionLink("Delete", "DeleteImage", new
{
controller = "Edit",
brand = image.Collection.Brand.Name,
season = image.Collection.Season,
imageId = #image.Id
})
</li>
</ul>
</td>
</tr>
}
</table>
<p>
<input type="submit" value="Save Changes" class="save-order" id="saveTable-#collection.Id"/>
</p>
}
Here is the jQuery so far:
$(document).ready(function () {
$(".table-collection").tableDnD();
$(".save-order").click(function (e) {
e.preventDefault();
$.ajax({ url: window.location.href,
type: 'POST',
data: { ids: $("--ASSIGN ARRAY HERE--"
});
The jQuery to iterate through each row is essentially this:
function(table, row) {
var rows = table.tBodies[0].rows;
var debugStr = "Row dropped was "+row.id+". New order: ";
for (var i=0; i<rows.length; i++) {
debugStr += rows[i].id+" ";
}
I see you are using input type submit which is exclusively used to postback forms. What you need to do is wrap every table up in a form with something like this:
#using(Html.BeginForm("Action", "Controller", new{ collectionId = collection.Id }))
{
<input type="submit" value="Save Changes" class="save-order" />
}
Note that this will cause a 'post-back' of the form to Action, Controller. Specify the collection id inside the route values to identify the specific collection.
Do note, you need to add input type hidden with the id's value otherwise the ids' won't get serialised - all you have to specify is the name attribute
<td class="dragHandle showDragHandle">
<input type="hidden" name="ids" value="#(image.Id)" />
#image.Id
</td>
Then you can intercept the call then do it via ajax with:
$(".save-order").click(function(e) {
e.preventDefault();
var form = $(this).closest('form');
if(form.validate()) {
$.post(form.attr('action'), form.serialize(), function() {
alert('The new image order has been saved.');
});
}
return false;
});
The accepting controller action method will probably have this signature
public ActionResult Action(int collectionId, int[] ids)
{
//Do stuff here
return Request.IsAjaxRequest() ? null : View();
}
Now it should support graceful degradation if javascript is disabled (does a normal form submit, otherwise does it via ajax)
Hope this helps :)
You can grab all of the IDs with something like this:
var IDs = [];
$("#mydiv").find("span").each(function(){ IDs.push(this.id); });
In your scenerio, do something like this:
$(document).ready(function ()
{
$(".table-collection").tableDnD();
$(".save-order").click(function (e)
{
var IDs = [];
$("#yourtable").find("draggable-tr-class").each(function(){ IDs.push(this.id); });
e.preventDefault();
$.ajax(
{
url: window.location.href,
type: 'POST',
data: { ids: IDs }
);
}
})
i have been create demo in jsfiddle using json
http://jsfiddle.net/viyancs/4ffb3/11/
if you use like that demo in your server must be get parameter `JSONFile' after that parse this json for what do you want.actually the demo not same with your case but i think you can use this by your logic.

Categories