ASP.NET MVC Sequence contains no elements - asp.net-mvc

I have the following code in my HomeController:
public ActionResult Edit(int id)
{
var ArticleToEdit = (from m in _db.ArticleSet where m.storyId == id select m).First();
return View(ArticleToEdit);
}
[ValidateInput(false)]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Article ArticleToEdit)
{
var originalArticle = (from m in _db.ArticleSet where m.storyId == ArticleToEdit.storyId select m).First();
if (!ModelState.IsValid)
return View(originalArticle);
_db.ApplyPropertyChanges(originalArticle.EntityKey.EntitySetName, ArticleToEdit);
_db.SaveChanges();
return RedirectToAction("Index");
}
And this is the view for the Edit method:
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="headline">Headline</label>
<%= Html.TextBox("headline") %>
</p>
<p>
<label for="story">Story <span>( HTML Allowed )</span></label>
<%= Html.TextArea("story") %>
</p>
<p>
<label for="image">Image URL</label>
<%= Html.TextBox("image") %>
</p>
<p>
<input type="submit" value="Post" />
</p>
</fieldset>
<% } %>
When I hit the submit button I get the error: Sequence contains no elements on this line: var originalArticle = (from m in _db.ArticleSet where m.storyId == ArticleToEdit.storyId select m).First();
What is the problem? How do I fix it. Thanks

The problem is you have nothing that matches your linq query in _db.ArticleSet. First will throw against an empty collection.
Try FirstOrDefault() if returning null is ok. FirstOrDetault() will return null if nothing matches.

You are not including the ID of the article in your HTML form. If you debug the ArticleToEdit object is probably either null or has zero storyId.
You should include your storyId in the HTML form. You can do it as a hidden field if you don't want the user to see it. For example:
<% using (Html.BeginForm()) {%>
<%= Html.HiddenFor("storyId") %>
...
On a separate note, you should probably switch to using Single instead of First. First indicates that you want the First item in a collection. Single indicates that you should get one and only one result.

You need to be sure of is that when you're editing a record, you need to tell the database what record to edit. It's not enough to have the ID in the querystring unless you specifically tell the Model to use that ID (see my second option), however the easiest way to do that is add the field in your form.
<% using (Html.BeginForm()) {%>
<%= Html.HiddenFor("storyId") %>
<fieldset>
<legend>Fields</legend>
<p>
<label for="headline">Headline</label>
<%= Html.TextBox("headline") %>
</p>
<p>
<label for="story">Story <span>( HTML Allowed )</span></label>
<%= Html.TextArea("story") %>
</p>
<p>
<label for="image">Image URL</label>
<%= Html.TextBox("image") %>
</p>
<p>
<input type="submit" value="Post" />
</p>
</fieldset>
<% } %>
This second option shows how to specify the storyId after the fact by submitting it to the Action via the Querystring. You just have to make sure the form action includes ?storyId=[n]
[ValidateInput(false)]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Article ArticleToEdit, int storyId)
{
ArticleToEdit.storyId = storyId;
if (ModelState.IsValid) {
_db.ApplyPropertyChanges(originalArticle.EntityKey.EntitySetName, ArticleToEdit);
_db.SaveChanges();
return RedirectToAction("Index");
} else {
return View(ArticleToEdit);
}
}
I would also suggest using something like AutoMapper to map ArticleToEdit to ArticleSet so that you don't need to make an additional DB lookup just to grab the original article. Basically if you are able to map ArticleToEdit to the ArticleSet Model, then you can use LINQ to SQL to perform the update without first querying for the storyId.

Related

Form File Upload with other TextBox Inputs + Creating Custom Form Action attribute

I am attempting to create a form where a user is able to enter your typical form values textboxes etc, but also upload a file as part of the form submission. This is my View code it can be seen that the File upload is identified by the MCF id:
<% using (Html.BeginForm("Create", "Problem", FormMethod.Post, new { id = "ProblemForm", enctype = "multipart/form-data" }))
{%>
<p>
<label for="StudentEmail">Student Email (*)</label>
<br />
<%= Html.TextBox("StudentEmail", Model.Problem.StudentEmail, new { size = "30", maxlength=26 })%>
<%= Html.ValidationMessage("StudentEmail", "*") %>
</p>
<p>
<label for="Type">Communication Type (*)</label>
<br />
<%= Html.DropDownList("Type") %>
<%= Html.ValidationMessage("Type", "*") %>
</p>
<p>
<label for="ProblemDateTime">Problem Date (*)</label>
<br />
<%= Html.TextBox("ProblemDateTime", String.Format("{0:d}", Model.Problem.ProblemDateTime), new { maxlength = 10 })%>
<%= Html.ValidationMessage("ProblemDateTime", "*") %>
</p>
<p>
<label for="ProblemCategory">Problem Category (* OR Problem Outline)</label>
<br />
<%= Html.DropDownList("ProblemCategory", null, "Please Select...")%>
<%= Html.ValidationMessage("ProblemCategory", "*")%>
</p>
<p>
<label for="ProblemOutline">Problem Outline (* OR Problem Category)</label>
<br />
<%= Html.TextArea("ProblemOutline", Model.Problem.ProblemOutline, 6, 75, new { maxlength = 255 })%>
<%= Html.ValidationMessage("ProblemOutline", "*") %>
</p>
<p>
<label for="MCF">Mitigating Circumstance Form</label>
<br />
<input id="MCF" type="file" />
<%= Html.ValidationMessage("MCF", "*") %>
</p>
<p>
<label for="MCL">Mitigating Circumstance Level</label>
<br />
<%= Html.DropDownList("MCL") %>
<%= Html.ValidationMessage("MCL", "*") %>
</p>
<p>
<label for="AbsentFrom">Date Absent From</label>
<br />
<%= Html.TextBox("AbsentFrom", String.Format("{0:d}", Model.Problem.AbsentFrom), new { maxlength = 10 })%>
<%= Html.ValidationMessage("AbsentFrom", "*") %>
</p>
<p>
<label for="AbsentUntil">Date Absent Until</label>
<br />
<%= Html.TextBox("AbsentUntil", String.Format("{0:d}", Model.Problem.AbsentUntil), new { maxlength = 10 })%>
<%= Html.ValidationMessage("AbsentUntil", "*") %>
</p>
<p>
<label for="AssessmentID">Assessment Extension</label>
<br />
<%= Html.DropDownList("AssessmentID") %>
<%= Html.ValidationMessage("AssessmentID", "*") %>
<%= Html.TextBox("DateUntil", String.Format("{0:d}", Model.AssessmentExtension.DateUntil), new { maxlength = 16 })%>
<%= Html.ValidationMessage("DateUntil", "*") %>
</p>
<p>
<label for="Details">Assessment Extension Details</label>
<br />
<%= Html.TextArea("Details", Model.AssessmentExtension.Details, 6, 75, new { maxlength = 255 })%>
<%= Html.ValidationMessage("Details", "*") %>
</p>
<p>
<label for="RequestedFollowUp">Requested Follow Up</label>
<br />
<%= Html.TextBox("RequestedFollowUp", String.Format("{0:d}", Model.Problem.RequestedFollowUp), new { maxlength = 16 })%>
<%= Html.ValidationMessage("RequestedFollowUp", "*") %>
</p>
<p>
<label for="StaffEmail">Staff</label>
<br />
<%= Html.ListBox("StaffEmail", Model.StaffEmail, new { #class = "multiselect" })%>
<%= Html.ValidationMessage("StaffEmail", "*")%>
</p>
<p>
<input class="button" type="submit" value="Create Problem" />
</p>
This is my controller code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Problem problem, AssessmentExtension assessmentExtension, Staff staffMember, HttpPostedFileBase file, string[] StaffEmail)
{
if (ModelState.IsValid)
{
try
{
Student student = studentRepository.GetStudent(problem.StudentEmail);
Staff currentUserStaffMember = staffRepository.GetStaffWindowsLogon(User.Identity.Name);
var fileName = Path.Combine(Request.MapPath("~/App_Data"), Path.GetFileName(file.FileName));
file.SaveAs(#"C:\Temp\" + fileName);
if (problem.RequestedFollowUp.HasValue)
{
String meetingName = student.FirstName + " " + student.LastName + " " + "Mitigating Circumstance Meeting";
OutlookAppointment outlookAppointment = new OutlookAppointment(currentUserStaffMember.Email, meetingName, (DateTime)problem.RequestedFollowUp, (DateTime)problem.RequestedFollowUp.Value.AddMinutes(30));
}
problemRepository.Add(problem);
problemRepository.Save();
if (assessmentExtension.DateUntil != null)
{
assessmentExtension.ProblemID = problem.ProblemID;
assessmentExtensionRepository.Add(assessmentExtension);
assessmentExtensionRepository.Save();
}
ProblemPrivacy problemPrivacy = new ProblemPrivacy();
problemPrivacy.ProblemID = problem.ProblemID;
problemPrivacy.StaffEmail = currentUserStaffMember.Email;
problemPrivacyRepository.Add(problemPrivacy);
if (StaffEmail != null)
{
for (int i = 0; i < StaffEmail.Length; i++)
{
ProblemPrivacy probPrivacy = new ProblemPrivacy();
probPrivacy.ProblemID = problem.ProblemID;
probPrivacy.StaffEmail = StaffEmail[i];
problemPrivacyRepository.Add(probPrivacy);
}
}
problemPrivacyRepository.Save();
return RedirectToAction("Details", "Student", new { id = student.Email });
}
catch
{
ModelState.AddRuleViolations(problem.GetRuleViolations());
}
}
return View(new ProblemFormViewModel(problem, assessmentExtension, staffMember));
}
This form was working correctly before I had to switch to using a non-AJAX file upload, this was due to an issue with Flash when enabling Windows Authentication which I need to use.
It appears that when I submit the form the file is not sent and I am unsure as to why? I have also been unsuccessful in finding an example online where a file upload is used in conjunction with other input types.
Another query I have is that for Create, and Edit operations I have used a PartialView for my forms to make my application have higher code reuse. The form action is normally generated by just using:
Html.BeginForm()
And this populates the action depending on which Url is being used Edit or Create. However when populating HTML attributes you have to provide a action and controller value to pass HTML attributes.
using (Html.BeginForm("Create", "Problem", FormMethod.Post, new { id = "ProblemForm", enctype = "multipart/form-data" }))
Is it possible to somehow populate the action and controller value depending on the URL to maintain code reuse? Thinking about it whilst typing this I could set two values in the original controller action request view data and then just populate the value using the viewdata values?
Any help on these two issues would be appreciated, I'm new to asp.net mvc :-)
Thanks,
Jon
ANSWER
Ok guys worked out the issue and its incredibly simple I didn't have the HTML name attribute on the file component of my form:
<input id="MCF" name="MCF" type="file" />
Now this binds to my method signature!
With the first issue, it looks like your action method signature is wrong. Because your fileInput has an ID of MCF, the HttpPostedFileBase parameter should have the same name so that the model binder knows to bind to that action method parameter.
E.g.
public ActionResult Create(Problem problem, AssessmentExtension assessmentExtension, Staff staffMember, HttpPostedFileBase mcf, string[] StaffEmail)
As for the second issue... you could try something like this:
<form method="post" id="ProblemForm" action="<%= Url.Action(ViewContext.RouteData.Values["action"].ToString()) %>" enctype="multipart/form-data">
The current controller will also be in RouteData.Values but if you're after the area, that'll be in RouteData.DataTokens.
HTHs,
Charles
Ok guys worked out the issue and its incredibly simple I didn't have the HTML name attribute on the file component of my form:
<input id="MCFile" name="MCFile" type="file" />
I have changed my method signature to match the name:
public ActionResult Create(Problem problem, AssessmentExtension assessmentExtension, Staff staffMember, HttpPostedFileBase MCFFile, string[] StaffEmail)
Now this binds to my method signature!

Many to Many with LINQ-To-Sql and ASP.NET MVC

I will restrict this to the three tables I am trying to work with Problem, Communications, and ProbComms. The scenario is that a Student may have many Problems concurrently which may affect their studies. Lecturers may have future communications with a student after an initial problem is logged, however as a Student may have multiple Problems the Lecturer may decide that the discussion they had is related to more than one Problem.
Here is a screenshot of the LINQ-To-Sql representation of my DB:
LINQ-To-Sql Screenshot
At the moment in my StudentController I have a StudentFormViewModel Class:
//
//ViewModel Class
public class StudentFormViewModel
{
IProbCommRepository probCommRepository;
// Properties
public Student Student { get; private set; }
public IEnumerable<ProbComm> ProbComm { get; private set; }
//
// Dependency Injection enabled constructors
public StudentFormViewModel(Student student, IEnumerable<ProbComm> probComm)
: this(new ProbCommRepository())
{
this.Student = student;
this.ProbComm = probComm;
}
public StudentFormViewModel(IProbCommRepository pRepository)
{
probCommRepository = pRepository;
}
}
When I go to the Students Detail Page this runs:
public ActionResult Details(string id)
{
StudentFormViewModel viewdata = new StudentFormViewModel(studentRepository.GetStudent(id),
probCommRepository.FindAllProblemComms(id));
if (viewdata == null)
return View("NotFound");
else
return View(viewdata);
}
The GetStudent works fine and returns an instance of the student to output on the page, below the student I output all problems logged against them, but underneath these problems I want to show the communications related to the Problem.
The LINQ I am using for ProbComms is This is located in the Model class ProbCommRepository, and accessed via a IProbCommRepository interface:
public IQueryable<ProbComm> FindAllProblemComms(string studentEmail)
{
return (from p in db.ProbComms
where p.Problem.StudentEmail.Equals(studentEmail)
orderby p.Problem.ProblemDateTime
select p);
}
However for example if I have this data in the ProbComms table:
ProblemID CommunicationID
1 1
1 2
The query returns two rows so I assume I somehow have to groupby Problem or ProblemID but I am not too sure how to do this with the way I have built things as the return type has to be ProbComm for the query as thats what Model class its located in.
When it comes to the view the Details.aspx calls two partial views each passing the relevant view data through, StudentDetails works fine page:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MitigatingCircumstances.Controllers.StudentFormViewModel>" %>
<% Html.RenderPartial("StudentDetails", this.ViewData.Model.Student); %>
<% Html.RenderPartial("StudentProblems", this.ViewData.Model.ProbComm); %>
StudentProblems uses a foreach loop to loop through records in the Model and I am trying another foreach loop to output the communication details:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<MitigatingCircumstances.Models.ProbComm>>" %>
<script type="text/javascript" language="javascript">
$(document).ready(function() {
$("DIV.ContainerPanel > DIV.collapsePanelHeader > DIV.ArrowExpand").toggle(
function() {
$(this).parent().next("div.Content").show("slow");
$(this).attr("class", "ArrowClose");
},
function() {
$(this).parent().next("div.Content").hide("slow");
$(this).attr("class", "ArrowExpand");
});
});
</script>
<div class="studentProblems">
<% var i = 0;
foreach (var item in Model) { %>
<div id="ContainerPanel<%= i = i + 1 %>" class="ContainerPanel">
<div id="header<%= i = i + 1 %>" class="collapsePanelHeader">
<div id="dvHeaderText<%= i = i + 1 %>" class="HeaderContent"><%= Html.Encode(String.Format("{0:dd/MM/yyyy}", item.Problem.ProblemDateTime))%></div>
<div id="dvArrow<%= i = i + 1 %>" class="ArrowExpand"></div>
</div>
<div id="dvContent<%= i = i + 1 %>" class="Content" style="display: none">
<p>
Type: <%= Html.Encode(item.Problem.CommunicationType.TypeName) %>
</p>
<p>
Problem Outline: <%= Html.Encode(item.Problem.ProblemOutline)%>
</p>
<p>
Mitigating Circumstance Form: <%= Html.Encode(item.Problem.MCF)%>
</p>
<p>
Mitigating Circumstance Level: <%= Html.Encode(item.Problem.MitigatingCircumstanceLevel.MCLevel)%>
</p>
<p>
Absent From: <%= Html.Encode(String.Format("{0:g}", item.Problem.AbsentFrom))%>
</p>
<p>
Absent Until: <%= Html.Encode(String.Format("{0:g}", item.Problem.AbsentUntil))%>
</p>
<p>
Requested Follow Up: <%= Html.Encode(String.Format("{0:g}", item.Problem.RequestedFollowUp))%>
</p>
<p>Problem Communications</p>
<% foreach (var comm in Model) { %>
<p>
<% if (item.Problem.ProblemID == comm.ProblemID)
{ %>
<%= Html.Encode(comm.ProblemCommunication.CommunicationOutline)%>
<% } %>
</p>
<% } %>
</div>
</div>
<br />
<% } %>
</div>
The issue is that using the example data before the Model has two records for the same problem as there are two communications for that problem, therefore duplicating the output.
Any help with this would be gratefully appreciated.
Thanks,
Jon
I have recently struggled greatly with many to many relationships in MVC. I've finally gotten mine working. I'd love to help you out, but do not fully understand your problem.
When you say that the query returns 2 rows, I would think that it should since there are 2 communications for that problem. If you want to return problems for a student, you do not need to query the ProbComms table, just the Problem table. Then if you want to return communications for those problems, query the ProbComms table.
For your loop, you need to loop through the problems and then within that loop, loop through the communications specific to that problem. Something very loosely like:
foreach (var p in Model.Student.Problem)
{
<%= Html.Encode(p.ProblemInfoField) %>
...
foreach (var c in p.ProbComms)
{
<%= Html.Encode(c.Communications.CommunicationInfoField) %>
...
}
}
You do not need to select the ProbComms in the controller and send them to the view. They should already be linked. Only the student needs to be sent to the view.

Asp.Net Axaj.BeginForm & UpdateTargetId not working

I have this in HomeController:
public ActionResult Details(string id)
{
var customer = Customers.GetCustomersById(id);
return PartialView("CustomerDetails", customer);
}
And this in Index.aspx:
<div>
<% using (Ajax.BeginForm("Details", new AjaxOptions
{
UpdateTargetId = "customerDetails",
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST"
}))
{ %>
<p>
Customer:
<%=Html.DropDownList("id")%></p>
<p>
<input type="submit" value="Details" /></p>
<% } %>
</div>
<div id="customerDetails">
</div>
And finally in CustomerDetails.ascx I have:
<fieldset>
<legend>Fields</legend>
<p>
Name:
<%= Html.Encode(Model.Name) %>
</p>
<p>
Credit:
<%= Html.Encode(Model.Credit) %>
</p>
<p>
CustomerID:
<%= Html.Encode(Model.CustomerID) %>
</p>
</fieldset>
<p>
<%=Html.ActionLink("Edit", "Edit", new { /* id=Model.PrimaryKey */ }) %> |
<%=Html.ActionLink("Back to List", "Index") %>
</p>
CustomerDetails.ascx was generated by right clicking on the Details-method and choosing "Add View", and selecting partial view and strongly typed view.
I'd want this to update the customer details in "Ajax-manner" inside a div called "customerDetails" inside Index.html. The problem is that after pressing Details-button, a new page is opened where with the correct details. The output page has no master page colors or layouts.
If I debug at Details-method, the contents of customer object is correct.
Any help appreciated!
/pom
The most likely culprit is that you aren't including MicrosoftAjax.js and MicrosoftMvcAjax.js on the page. This causes the javascript to fail because it can't find the necessary functions and the form submits normally instead through Ajax.

ASP.NET MVC Passing Data from View to Controller

I have a view with a grid that contains items added to a workstation. The user can select an item from a drop down list and click an action link which calls a controller that adds that item to the workstation. I can make it work by reading the FormCollection object in the Post action of the controller.
<p>
<% using(Html.BeginForm("AddItem", "Home")) { %>
<label for="ItemID">Item:</label>
<%= Html.DropDownList("ItemID", Model.ItemsList) %>
<%= Html.Hidden("WorkstationID", Model.Workstation.RecordID) %>
<input type="submit" value="Submit" />
<% } %>
</p>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddItem(FormCollection formValue)
{
long workstationId = Convert.ToInt64(formValue["WorkstationID"]);
long itemId = Convert.ToInt64(formValue["ItemID"]);
Workstation workstation = itilRepository.FindWorkstation(workstationId);
Item item = itilRepository.FindItem(itemId);
itilRepository.AddItem(workstation, item);
itilRepository.Save();
return Content("Item added successfully!");
}
What I want to do is be able to submit the two parameters the workstationId and itemId to the controller using Ajax.ActionLink and have the new item that was added get inserted into the grid. I am rendering the grid like this:
<table>
<tr>
<th></th>
<th>
Name
</th>
<th>
Service Tag
</th>
<th>
Serial Number
</th>
</tr>
<% foreach (var item in Model.Items) { %>
<tr>
<td>
<%= Html.ActionLink("Edit", "ItemEdit", new { id = item.RecordID }) %> |
<%= Html.ActionLink("Details", "ItemDetails", new { id = item.RecordID })%>
</td>
<td>
<%= Html.Encode(item.Name) %>
</td>
<td>
<%= Html.Encode(item.ServiceTag) %>
</td>
<td>
<%= Html.Encode(item.SerialNumber) %>
</td>
</tr>
<% } %>
</table>
The problem I have is when I submit using the ActionLink I can't figure out how to pass in the parameters to the controller and how to update the grid without reloading the entire view.
I would really appreciate some help with this or even a link to a tutorials that does something similar.
Thank You!
This is the working version, the one problem is that when the controller returns the partial view that is all that gets rendred the actual page is gone.
<% using (Ajax.BeginForm("AddItem", null,
new AjaxOptions
{
UpdateTargetId = "ResultsGoHere",
InsertionMode = InsertionMode.Replace
},
new { #id = "itemForm" } ))
{ %>
<label for="ItemID">Item:</label>
<%= Html.DropDownList("itemId", Model.ItemsList) %>
<%= Html.Hidden("workstationId", Model.Workstation.RecordID) %>
Submit
<div id="ResultsGoHere">
<% Html.RenderPartial("WorkstationItems", Model.Items); %>
</div>
<% } %>
Not sure what the cause is, the replace was working correctly before but the controller wasn't getting the drop down value. Now the controller is getting the value but the partial view that is returned replaces the entire page.
The Action Method:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddItem(string workstationId, string itemId)
{
long lworkstationId = Convert.ToInt64(workstationId);
long litemId = Convert.ToInt64(itemId);
Workstation workstation = itilRepository.FindWorkstation(lworkstationId);
Item item = itilRepository.FindItem(litemId);
IQueryable<Item> items = itilRepository.AddItem(workstation, item);
itilRepository.Save();
return PartialView("WorkstationItems", items);
}
This is the HTML for the View that does all the work:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<ITILDatabase.Models.WorkstationFormViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Workstation Details
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<link type="text/css" href="/../Content/css/ui-lightness/jquery-ui-1.7.2.custom.css" rel="stylesheet" />
<script type="text/javascript" src="/../Content/js/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="/../Content/js/jquery-ui-1.7.2.custom.min.js"></script>
<h2>
Workstation Details</h2>
<fieldset>
<legend>Fields</legend>
<p>
Record ID:
<%= Html.Encode(Model.Workstation.RecordID) %>
</p>
<p>
Name:
<%= Html.Encode(Model.Workstation.Name) %>
</p>
<p>
Description:
<%= Html.Encode(Model.Workstation.Description) %>
</p>
<p>
Site:
<%= Html.Encode(Model.Workstation.Site.Name) %>
</p>
<p>
Modified By:
<%= Html.Encode(Model.Workstation.ModifiedBy) %>
</p>
<p>
Modified On:
<%= Html.Encode(String.Format("{0:g}", Model.Workstation.ModifiedOn)) %>
</p>
<p>
Created By:
<%= Html.Encode(Model.Workstation.CreatedBy) %>
</p>
<p>
Created On:
<%= Html.Encode(String.Format("{0:g}", Model.Workstation.CreatedOn)) %>
</p>
</fieldset>
<fieldset>
<legend>People</legend>
<% Html.RenderPartial("WorkstationPeople", Model.People); %>
</fieldset>
<fieldset>
<legend>Positions</legend>
<% Html.RenderPartial("WorkstationPositions", Model.Positions); %>
</fieldset>
<fieldset>
<legend>Items</legend>
<% using (Ajax.BeginForm("AddItem", "Home", null,
new AjaxOptions
{
UpdateTargetId = "ResultsGoHere",
InsertionMode = InsertionMode.Replace
},
new { #id = "itemForm" } ))
{ %>
<label for="ItemID">Item:</label>
<%= Html.DropDownList("itemId", Model.ItemsList) %>
<%= Html.Hidden("workstationId", Model.Workstation.RecordID) %>
Submit
<div id="ResultsGoHere">
<% Html.RenderPartial("WorkstationItems", Model.Items); %>
</div>
<% } %>
</fieldset>
<br />
<p>
<%=Html.ActionLink("Edit", "WorkstationEdit", new { id = Model.Workstation.RecordID }) %>
|
<%=Html.ActionLink("Back to List", "Index") %>
</p>
</asp:Content>
What result are you expecting from the AJAX call?
You could use the AjaxHelper object's helper methods instead of the HtmlHelper to render the link. For example, to get new content with an AJAX HttpPOST call and insert it into a <div> with the id set to ResultsGoHere you render the following link:
<%= Ajax.ActionLink("Edit", "ItemEdit",
new {
itemId = item.RecordId,
workstationId = myWorkStationId
},
new AjaxOptions {
HttpMethod="POST",
UpdateTargetId="ResultsGoHere",
InsertionMode = InsertionMode.Replace
}) %>
In your AcionMethod, you can simply test on Request.IsAjaxRequest() to decide what to return:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ItemEdit(string itemId, string workstationId) {
// edit the item and get it back
if (Request.IsAjaxRequest()) {
return PartialView("SingleItem", item);
}
return RedirectToAction("ItemEdit", new { itemId = item.RecordId, workstationId = workstationId });
}
// fallback for get requests
public ActionResult ItemEdit(string itemId, string workstationId)
{
// do stuff and return view
}
This is how you could do it using the Ajax.BeginForm() method instead:
<% using (Ajax.BeginForm("ItemEdit", null, new AjaxOptions
{
UpdateTargetId = "ResultsGoHere",
InsertionMode = InsertionMode.Replace
}, new { #id = "itemForm" } )
{ %>
<p>
<%= Html.DropDownList("itemId") %></p>
<p>
<%= Html.DropDownList("workstationId") %></p>
<p>
Submit
</p>
<% } %>
Please note that the code is in its current state in no way fully functional - the dropdownlists don't get their items from anywhere, there is no <div> to take care of the results from the AJAX request, and the onclick attribute on the link that submits the form requires that jQuery is included (in which case it is way better to give the link an id and add a click() event handler to it from a separate js file...)
EDIT: Oh, and I haven't verified that it is OK to pass a null value to the routeValues parameter. If not, just supply the controller and action names, and you'll be fine.
how can you pass the model from the view to the post create action of the controller using ajax.actionlink?
Here, as of my knowledge, we can pass data from View to Controller in two ways...
Using Formcollection built in keyword like this..
[HttpPost]
public string filter(FormCollection fc)
{
return "welcome to filtering : "+fc[0];
(or)
return "welcome to filtering : "+fc["here id of the control in view"];
}
FormCollection will work only when you click any button inside a form. In other cases it contains only empty data
Using model class
[HttpPost]
public string filter(classname cn)
{
return "welcome to filtering : "+cn.Empid+""+cn.Empname;
}
This is how you will be able to send multiple parameters through actionLink.
For this situation please refer to the code below:
#Html.ActionLink("Link text", "Action Name", null, routeValues: new { pram_serviceLine = Model.ServiceLine_ID, pram_Month = Model.Month, pram_Year = Model.Year, flag = "ROTATION" }
Reply if it works.

Populating a form dynamically based on user input in ASP.Net MVC

My question is similar to Engram's here, but my question goes a bit further. The way i intend it to work is I have a textbox asking how many entries a user is going to make. After they input the number, I need to create that many more textboxes to allow for entries (and then repeat the same process with those textboxes, but baby steps first...) I tried collecting the keys on the post, but it only returns the initial textbox asking for the number of entries. I'm still trying to get a grasp on MVC and the tutorials/videos so far don't delve this deep into it yet. Then again, I know this is probably something I could handle using JQuery, but I'd still be stuck in the same situation.
This is the controller I'm using:
[AcceptVerbsAttribute("POST")]
public ActionResult Create(int tbxNumberOfExercises)
{
ViewData["number"] = tbxNumberOfExercises;
foreach (var key in Request.Form.Keys)
{
string keyString = key.ToString();
if (keyString.StartsWith("tbox_exercise", StringComparison.OrdinalIgnoreCase))
{
string recNum = keyString.Substring(13, keyString.Length - 13);
string approvedKey = Request.Form["tbox_exercise" + recNum];
int number;
int.TryParse(approvedKey, out number);
}
}
return View("Create");
}
And this is my aspx:
<form action="/CreateWorkout/Create" method="post">
Number of Exercises:
<%= Html.TextBox("tbxNumberOfExercises") %>
<br />
<br />
<input type="submit" value="Set Exercise Number" />
</form>
<% if (ViewData["number"] != null)%>
There are this many:<%=Html.Encode(ViewData["number"])%>
<br />
and this line should show up
<% if (ViewData["number"] != null)
{
int max = (int)ViewData["number"];
for (int i = 0; i < max; i++)
{%>
<br />
<br />
<%= Html.TextBox("tbox_exercise" + i) %>
<% }
} %>
<% if (ViewData["s"] != null) %>
<%=Html.Encode(ViewData["s"]) %>
Is there something I'm overlooking, not comprehending, or should I quit while I'm at it because it seems like I'll never get it?
Thanks in advance for any help -- I'm just trying to learn as most I can.
I'd break this up in stages, you'll need to add a "Save" view someplace depending on what you want.
Scott
<form action="/Demo01/Create" method="post">
Number of Exercises:
<%= Html.TextBox("tbxNumberOfExercises") %>
<br />
<br />
<input type="submit" value="Set Exercise Number" />
</form>
<% if (ViewData["number"] != null) {%>
<form action="/Demo01/Save" method="post">
There are this many:<%=Html.Encode(ViewData["number"])%>
<br />
and this line should show up
<% if (ViewData["number"] != null) {
int max = (int)ViewData["number"];
for (int i = 0; i < max; i++) {%>
<br />
<br />
<%= Html.TextBox("tbox_exercise" + i) %>
<% }
} %>
<% if (ViewData["s"] != null) %>
<%=Html.Encode(ViewData["s"]) %>
<input type="submit" value="Save Exercises" />
<% } %>
</form>
And then in your controller something like this:
public class Demo01Controller : Controller {
public ActionResult Create() {
return View();
}
[AcceptVerbsAttribute("POST")]
public ActionResult Create(int tbxNumberOfExercises) {
ViewData["number"] = tbxNumberOfExercises;
return View("Create");
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save() {
foreach (var key in Request.Form.Keys) {
string keyString = key.ToString();
if (keyString.StartsWith("tbox_exercise", StringComparison.OrdinalIgnoreCase)) {
string recNum = keyString.Substring(13, keyString.Length - 13);
string approvedKey = Request.Form["tbox_exercise" + recNum];
int number;
int.TryParse(approvedKey, out number);
}
}
return View(); // return/redirect to wherever you want
}
}
I would consider adding the textboxes client-side via javascript rather than posting back to the server to have the form redrawn, assuming that you can live with javascript as a requirement for using the application. If not, then #Scott's approach should work. As a matter of preference I would probably have the Save method take a FormCollection parameter rather than deal with the Request object directly.
The javascript solution would be to have a single textbox and a button to add another. The user could continue to add textboxes until they have enough.
The problem is that your </form> ending tag needs to come at the end of your view.
Try this modified view:
<form action="/CreateWorkout/Create" method="post">
Number of Exercises:
<%= Html.TextBox("tbxNumberOfExercises") %>
<br />
<br />
<input type="submit" value="Set Exercise Number" />
<% if (ViewData["number"] != null)%>
There are this many:<%=Html.Encode(ViewData["number"])%>
<br />
and this line should show up
<% if (ViewData["number"] != null)
{
int max = (int)ViewData["number"];
for (int i = 0; i < max; i++)
{%>
<br />
<br />
<%= Html.TextBox("tbox_exercise" + i) %>
<% }
} %>
<% if (ViewData["s"] != null) %>
<%=Html.Encode(ViewData["s"]) %>
</form>
I would recommend Scott's approach as far as best practice. This answer is about getting your exact scenario working.
Thanks for the help guys. I realized at 5 this morning that my issue is that the form didn't include the new textboxes / I needed another form. I'll have to seriously look into Javascript and actually modifying the DOM as it would be better to keep it client side.
Thanks so much.

Resources