I'm currently working with MVC 1.0 and have never worked with AJAX before and only have limited experience (started a little more than a week ago) with ASP.Net MVC. I'm trying to setup a table (that's built / is in a partial view) that's populated with information from a db that allows a user to quickly add pr remove records into or from the db right from the table. I'm trying to use AJAX to get this done because there is a lot of other information on the rest of the page that I don't want to have to reload. Here's a quick template.
row1: (text box) Add
row2: Name1 Remove
row3: Name2 Remove
So when a user wants to, they can enter a name into the (text box) hit Add and an action in the controller will add the entered name into the database and reload the partial view with the most up to date information.
So far I have been able to get the action method to be called, and add the record into the database. The problem is that it does not reload JUST the partial-view. It instead loads the partial-view as a whole new page and it's content is the only one that displays. (Instead of staying at /Project/Details/5, the page loads /Project/MembersDisplay)
In my master page I have:
<script src="<%= Links.Scripts.jquery_1_3_2_min_js %>" type="text/javascript"></script>
<script src="<%= Links.Scripts.MicrosoftAjax_js %>" type="text/javascript"></script>
<script src="<%= Links.Scripts.MicrosoftMvcAjax_js %>" type="text/javascript"></script>
In the controller I have:
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult AddMember(FormCollection form)
{
MemberRepository repo = new MemberRepository();
Member m = new Member();
m.Id = Convert.ToInt32(form["Id"]);
m.Name = form["name"];
try{
repo.addMember(m);
repo.save();
}
catch{
return View();
}
// Make a new IList of All Members in this current
//IList<Member> mems = (new PRDataContext()).Members_GetMembersById(m.Id).ToList<Member>();
return View("MembersDisplay", members);
}
The Partial view is
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<Application.Models.Member>>" %>
<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoffMvcAjax.js" type="text/javascript"></script>
<table>
<% if (Model.Count() > 0) { %>
<% foreach (var p in ViewData.Model) { %>
<tr>
<td><%= Html.Encode(p.Name) %></td>
<td><%= Ajax.ActionLink("Delete", "AjaxDelete", new { p.Id, p.Name }, new AjaxOptions { Confirm = "Delete?", HttpMethod = "AjaxDelete", UpdateTargetId = "results" })%></td>
</tr>
<% } %>
<% } else { %>
<tr>
<td><b>There are no Members.</b></td>
</tr>
<% } %>
</table>
In the controller, I tried adding if(Request.IsAjaxRequest()==true) the branch never executed. I'm not sure if ajax isn't loading, or what I'm doing wrong.
EDIT - Added missing code
Here's the details portion that uses the partial view:
<table>
<% Ajax.BeginForm("AddMember", "Project", new AjaxOptions { UpdateTargetId = "results", InsertionMode=InsertionMode.Replace}) { %>
<tr>
<td align="left" colspan="2" style="background-color:#93B6E0;"><font style="color:#fff;"><b>Members</b></font></td>
</tr>
<tr>
<td width="80%">
<input type="hidden" name="prjId" value="<%= Html.Encode(Model.Id) %>" /><input type="text" name="name" style="width:120px;" />
</td>
<td width="20%">
<input type="submit" value="Add" />
</td>
</tr>
<% } %>
<tr>
<td colspan="2">
<div id="results"><% Html.RenderPartial(MVC.Members.Views.MembersDisplay, Model.Members.ToList<Member>()); %></div>
</td>
</tr>
Dirk - not sure if the complete code is there. however, from a brief glance, your returning:
return View("MembersDisplay", members);
when in fact, for a partial view, you should be returning something like:
return PartialView("MembersDisplay", members);
hope this helps
jim
If you're intending the submit to fire the Ajax.BeginForm, you'll need to include it in the form with curly braces. Currently your submit is not within the form.
I upgraded to MVC 2.0 as #RailRhoad suggested.
Then I moved the partial view to an area and has to change the path links to the .js files to be more explicit (T4MVC wasn't rendering the correct paths). Instead of
<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
or
<script src="<%= Links.Scripts.MicrosoftAjax_js %>" type="text/javascript"></script>
I had to do
<script src="../../../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>
Also, the action in the controller worked as #jim suggested by
return PartialView(MVC.ProjectDash.Project.Views.MembersDisplay, members);
Also, I removed the "InsertionMode=..." from
<% Ajax.BeginForm("AddMember", "Project", new AjaxOptions { UpdateTargetId = "results", InsertionMode=InsertionMode.Replace}) { %>
The biggest issue was the javascript files were not be linked up and referenced correctly. Once that was fixed, the other things quickly feel into place.
Related
mycontroller.cs
[HttpPost]
public ActionResult Index(int searchtext)
{
var data = (from pm in db.ProductMasters
join shi in db.SuppliersHotelsInfoes
on pm.ProductID equals shi.LocHotelID
where shi.SearchID == searchtext
select pm).ToList().Take(10);
ViewBag.Data = data;
return View();
}
am sending viewBag.Data to view
<% try { %>
<tr><td>
<% foreach(var item in ViewBag.Data){ %>
<img alt="" src="<%item. %>" />
</td><%} %> <% foreach(var item in ViewBag.Data) { %>
<td><%: Html.DisplayFor(modelItem => "")%></td>
<td><%: Html.DisplayFor(modelItem => "")%> </td>
<td><%: Html.DisplayFor(modelItem => "")%></td>
</tr>
<%} }%>
<%catch(Exception ex){ }%>
</table>
Here, how can we take the values from "var item"
I haven't used aspx view syntax for a while but I believe you are missing the equals sign.
You currently have this:
<img alt="" src="<%item. %>" />
instead it should be this
<img alt="" src="<%=item. %>" />
Notice the = before "item"
Take a look here for more info http://weblogs.asp.net/scottgu/archive/2010/04/06/new-lt-gt-syntax-for-html-encoding-output-in-asp-net-4-and-asp-net-mvc-2.aspx
Also consider using a later version of MVC which supports razor syntax which is much cleaner.
Edit:
After your comment I see your issue. You are using the ViewBag which is a dynamic type. Therefore it cannot infer that your ViewBag.Data object is a collection of product masters. You have two options:
Option 1 - Don't use the ViewBag, instead use a strongly typed model.
Option 2 - In your view cast your ViewBag.Data to the List of Product Masters.
For example:
Instead of
<% foreach(var item in ViewBag.Data){ %>
Use this
<% foreach(var item in (List<ProductMaster>)ViewBag.Data){ %>
enter code hereI am trying to upload a file using FileUpload Control. As there are so many complexities within my form, I cannot use Html.BeginForm (of course this works perfectly fine). All I want to do is with Input id call the function to send the postedfile to the controller. I searched in the web, but my need is this. I DO NOT WANT TO SUBMIT THE WHOLE FORM. All I want to do is upload file and comeback to my form to complete the rest of the fields. Any type of code snippet would be appreciated. Thanks
Here is the sample of code what I have
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
ViewReportFiles
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm([ActionName], [ControllerName], FormMethod.Post, new { target = "_blank" }))
{%>
Here I have few fields to process.
Along with this I have fileUpload control
<table>
<tr>
<td valign="bottom">
<input type="file" id="document" name="document" accept="text/xml, text/csv, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" size="76" />
</td>
<td valign="bottom">
<div Id="UploadFile"> <input type="image" alt="upload file" src="<%= ResolveUrl("~/media/form_upload_btn.png") %>" name="image" /> </div>
</td>
</tr>
</table>
<% }>
My javascript is as follows
<script type="text/javascript" language="javascript">
$("#UploadFile").click(function () {
if ($("#document").val() == '') {
// checking for selected file
alert('Please select a document.');
return false;
}
$.ajaxFileUpload({
**url: '<%= Url.Action("actionName", "Controller") %>',**
data: { val: 'aaa' },
secureuri: false,
fileElementId: 'document',
dataType: 'xml',
success: function (data) {
}
});
});
</script>
Here the problem is it's not getting into that URL. it goes to my HTTPPost of the page.
My controller code is like this....
public void UploadAccessDataFile(){
foreach (string uploadFile in Request.Files)
{
}
}
Please help.
I am trying to upload a file using FileUpload Control
Don't try this to do this. Read the following blog post which explains how to upload files in an ASP.NET MVC application.
I have the following partial view NewsSummary.ascx used to display a list of news article summaries:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<AkwiMemorial.Models.Article>>" %>
<table>
<% foreach (var item in Model as IEnumerable<AkwiMemorial.Models.Article>)
{ %>
<tr>
<td>
<strong>
<%= Html.Encode(String.Format("{0:MMMM dd yyyy}", item.DateCreated)) %>
</strong>
</td>
<td>
<%= Html.Encode(item.Abstract) %>
</td>
<td>
<a class="link1"> <%= Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ })%></a>
</td>
</tr>
<% } %>
</table>
My HomeController fetches articles to be displayed and creates an entry in the ViewData dictionary of Index.aspx before it is rendered. Partial view is then rendered within Index.aspx using Html.RenderPartial with the lists of news articles passed as its model as follows:
<% Html.RenderPartial("NewsSummary", ViewData["news"]); %>
Now, I already have a NewsController that fetches a news article by a given Id. With the code as as, clicking on the Details link of each article summary in the NewsSummary partial view invokes an action in my HomeController called Details. How do I tie this action to the Details method in my NewsController. I looked into Html.RenderAction but could not determine how it will work for me in this scenario.
Any ideas or am I approaching this the wrong way?
You should specify the controller as a parameter to Html.ActionLink like this:
<%= Html.ActionLink("Details", "Details", "News", new { /* id=item.PrimaryKey */ }, null) %>
I have a view that is strongly typed:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MPKwithMVC.Models.SmartFormViewModel>" %>
Works great to generate the view, but when I post, I have an ActionResult defined:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Next(MPKwithMVC.Models.SmartFormViewModel model)
{ .. }
Which I would imagine get hit when my next button is clicked (it works if I change the argument to a FormsCollection). I instead get a message saying "No parameterless constructor defined for this object".
What am I doing wrong?
My SmartFormsViewModel is:
[Serializable]
public class SmartFormViewModel
{
public List<Question> Questions { get; set; }
public List<Answer> Answers { get; set; }
public SmartFormViewModel(List<Question> questions, List<Answer> answers)
{
this.Questions = questions;
this.Answers = answers;
}
public SmartFormViewModel()
{
}
}
And here is the View:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MPKwithMVC.Models.SmartFormViewModel>" %>
<%# Import Namespace="MPKwithMVC.Models" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
SmartForms
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Questionaire</h2>
<% using (Html.BeginForm("Next", "SmartForms"))
{ %>
<div style="float: left; margin-right: 2em;">
<% Html.RenderPartial("NavigationPanel", Model); %>
</div>
<div>
<table>
<%
foreach (Question question in (Model.Questions))
{ %>
<tr>
<td>
<div style="text-align: right; width: 20em;">
<%= Html.Encode(question.QuestionText)%>
</div>
</td>
<td>
<div style="float: left;">
<% if (question.QuestionType == 1)
{ %>
<%= Html.TextBoxFor(model => model.Answers[(int)question.QuestionID - 1].AnswerValue) %>
<% } %>
<% if (question.QuestionType == 2)
{ %>
<%= Html.RadioButton("yn" + question.QuestionID, "Yes", false)%>Yes
<%= Html.RadioButton("yn" + question.QuestionID, "No", true)%>No
<% } %>
</div>
<% if (question.Required == true)
{ %>
<div style="color: Red; float: right; margin-left: 3px;">
*</div>
<% } %>
</td>
</tr>
<%
} %>
<tr>
<td>
<%
if (ViewData["errorMsg"] != null)
{%>
<div style="color:Red;">
<%= Html.Encode(ViewData["errorMsg"].ToString()) %>
</div>
<% } %>
</td>
<td>
<div style="margin-top: 1em;">
<button name="button" value="next">Next</button>
</div>
</td>
</tr>
</table>
</div>
<% } %>
</asp:Content>
Have a look at the input names that are being generated within your HTML. I think you have an issue with the naming of your controls thus the default model binding is failing since you mentioned that the using FormCollection works correctly. I am making this assumption since I don't know what your Questions and Answers classes look like
<%= Html.TextBoxFor(model =>
model.Answers[(int)question.QuestionID - 1].AnswerValue) %>
Won't this render something similar to the following; which if I'm not mistaken will not bind to your model. The same applies for the RadioButtons.
<input type="text" name="Answers[0].AnswerValue" id="Answers_0__AnswerValue" value="somevalues"/>
The RadioButton helper should be
<%= Html.RadioButton("Questions[" + question.QuestionID + "].ID", "Yes", false)%> // you now get a list of questions
<input type="radio" name="Questions[1].ID" id="Questions_1__ID" value="No" checked="checked"/>
There are some ways you can try to resolve this:
Correctly name your input controls so that they match you ViewModel. (This includes posting back all the required fields for you model - I think that that default values are used when the model binding occurs if its not posted)
Create a custom model binder
You may need to tell the Binder what the Prefix of the input fields are. ([Bind] attribute) to specifically include or exclude form fields.
Create a new Model containing the values you expect to post back
I think that overall that your approach needs to slightly change. From the info provided, your Q&As are closely related. Depending on the question type your 'answer' is either boolean or freetext. At the moment your are not posting a List<Questions> back to the server. Answers, yes, but they are not I don't think that it is recognised as List<Answers>.
Haacked has a post which I think is related to your issue and this SO question further indicates that it may still applly to ASP-MVC-2.
As the error message suggest, your SmartFormViewModel class needs to contain a parameterless constructor.
This is a great example of what you would like to do
I would also suggest you use the strongly typed helpers from Html extensions to generate your form fields.
e.g.
Html.HiddenFor(x => x.SomeField)
Html.TextBoxFor(x => x.SomeEditableField)
Make sure that your <form>'s action points to your Next method.
I'm using ASP.NET MVC 2 and am struggling with saving child entities. I have an existing Invoice entity (which I create on a separate form) and then I have a LogHours view that I'd like to use to save InvoiceLog's, which are child entities of Invoice. Here's the view:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TothSolutions.Data.Invoice>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Log Hours
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="HeadContent" runat="server">
<script type="text/javascript">
$(document).ready(function () {
$("#InvoiceLogs_0__Description").focus();
});
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Log Hours</h2>
<% using (Html.BeginForm("SaveHours", "Invoices")) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<table>
<tr>
<th>Date</th>
<th>Description</th>
<th>Hours</th>
</tr>
<%
int index = 0;
foreach (var log in Model.InvoiceLogs) {
%>
<tr>
<td><%: log.LogDate.ToShortDateString() %></td>
<td><%: Html.TextBox("InvoiceLogs[" + index + "].Description")%></td>
<td><%: Html.TextBox("InvoiceLogs[" + index + "].Hours")%></td>
<td>Hours</td>
</tr>
<%
index++;
}
%>
</table>
<p>
<%: Html.Hidden("InvoiceID") %>
<%: Html.Hidden("CreateDate") %>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
And here's the controller code:
//GET: /Secure/Invoices/LogHours/
public ActionResult LogHours(int id)
{
var invoice = DataContext.InvoiceData.Get(id);
if (invoice == null)
{
throw new Exception("Invoice not found with id: " + id);
}
return View(invoice);
}
//POST: /Secure/Invoices/SaveHours/
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveHours([Bind(Exclude = "InvoiceLogs")]Invoice invoice)
{
TryUpdateModel(invoice.InvoiceLogs, "InvoiceLogs");
invoice.UpdateDate = DateTime.Now;
invoice.DeveloperID = DeveloperID;
//attaching existing invoice.
DataContext.InvoiceData.Attach(invoice);
//save changes.
DataContext.SaveChanges();
//redirect to invoice list.
return RedirectToAction("Index");
}
And the data access code:
public static void Attach(Invoice invoice)
{
var i = new Invoice { InvoiceID = invoice.InvoiceID };
db.Invoices.Attach(i);
db.Invoices.ApplyCurrentValues(invoice);
}
In the SaveHours action, it properly sets the values of the InvoiceLog entities after I call TryUpdateModel but when it does SaveChanges it doesn't update the database with the new values. Also, if you manually update the values of the InvoiceLog entries in the database and then go to this page it doesn't populate the textboxes so it's clearly not binding correctly.
Thanks,
Justin
Got it working, I needed to populate the InvoiceLogID and InvoiceID in hidden fields so they'd get populated in the EntityCollection.