I am currently using the EntityFramework to bind my ASP.NET MVC project to a MySQL database and one of my entities, Product, has an Images property containing a collection of ProductImages. I have built a form to allow the user to modify a given Product and this form includes fields for editing all of the images associated to that Product as well. After reading Phil Haack's and Dan Miser's posts on the matter I have a decent idea of what needs to happen, but I can't seem to make it work for some reason...
Here is my Product form:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<KryptonCMS.Models.Product>" %>
<%# Import Namespace="KryptonCMS.Core" %>
<%# Import Namespace="KryptonCMS.Models.ViewModels" %>
<% using (Html.BeginForm())
{%>
<ul class="gallery">
<%
var index = 0;
foreach (var image in Model.ImageList.OrderBy(p => p.Order))
{
%>
<li>
<% Html.RenderPartial("ProductImageForm", image, new ViewDataDictionary(ViewData) { { "index", index } }); %>
</li>
<%
index++;
}
%>
</ul>
<p>
<input type="submit" name="btnSave" value="Save" />
<input type="submit" name="btnCancel" value="Cancel" />
</p>
<% } %>
And here is the definition for ProductImageForm:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<KryptonCMS.Models.ProductImage>" %>
<%# Import Namespace="KryptonCMS.Core" %>
<div>
<%
var fieldPrefix = string.Format("images[{0}]", ViewData["index"]); %>
<%=Html.Hidden(fieldPrefix + "ID", Model.ID) %>
<img src="<%=UtilityManager.GetProductImagePath(Model.Product.ID, Model.FileName, true) %>"
alt="" /><br />
<label for="Description">
Description:</label>
<%=Html.TextBox(fieldPrefix + "Description", Model.Description) %><br />
<label for="Order">
Order:</label>
<%=Html.TextBox(fieldPrefix + "Order", Model.Order)%><br />
</div>
And finally my ProductsController actions:
public ActionResult Edit(int id)
{
var product = productsRepository.GetProduct(id);
if (product == null)
return View("NotFound", new MasterViewModel());
// else
return View(ContentViewModel.Create(product));
}
[AcceptVerbs(HttpVerbs.Post), ValidateInput(false)]
public ActionResult Edit(int id, FormCollection formCollection)
{
var product = productsRepository.GetProduct(id);
if (formCollection["btnSave"] != null)
{
if (TryUpdateModel(product) && TryUpdateModel(product.Images, "images"))
{
productsRepository.Save();
return RedirectToAction("Details", new { id = product.ID });
}
return View(ContentViewModel.Create(product));
}
// else
return RedirectToAction("Details", new { id = product.ID });
}
The HTML output for a single ProductImageForm looks like this:
<div>
<input id="images[0]ID" name="images[0]ID" type="hidden" value="1" />
<img src="/Content/ProductGallery/3/thumbs/car1.jpg"
alt="" /><br />
<label for="Description">
Description:</label>
<input id="images[0]Description" name="images[0]Description" type="text" value="FAST CAR" /><br />
<label for="Order">
Order:</label>
<input id="images[0]Order" name="images[0]Order" type="text" value="1" /><br />
</div>
I have tried all sorts of methods of reorganizing my form including taking the Image collection out of the Product form and placing it in its own (which I really don't want to do), but nothing is working. Is there something blatatently wrong with my approach here?
You are missing dots in inputs' names:
<%= Html.Hidden(fieldPrefix + ".ID", Model.ID) %>
<%= Html.TextBox(fieldPrefix + ".Description", Model.Description) %>
<%= Html.TextBox(fieldPrefix + ".Order", Model.Order) %>
Check this blog post: http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
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){ %>
I'm new to ASP.net MVC and I am struggling to make this work at the moment. I have a controller method called Add, it looks like this:
public ActionResult Add()
{
// check user is authenticated
if (Request.IsAuthenticated)
{
return View();
}
return RedirectToAction("Index", "Home");
}
//
// POST: /Home/Add
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Add(string title, string description, string priority, string color, FormCollection collection)
{
if (ModelState.IsValid)
{
// create instance of todo object
todo obj = new todo();
try
{
// gather fields
obj.priority = Convert.ToInt32(priority);
obj.color = Convert.ToInt32(color);
obj.title = title;
obj.description = description;
todosDataContext objLinq = new todosDataContext();
// get the users id, convert to string and store it
var userid = Membership.GetUser().ProviderUserKey;
obj.userid = userid.ToString();
// save
objLinq.todos.InsertOnSubmit(obj);
objLinq.SubmitChanges();
return RedirectToAction("Index", "Home");
}
catch
{
return View(obj);
}
}
return RedirectToAction("Index", "Home");
}
If data is sent via POST to the method, it should add the data to the database. That is working fine and everything is added correctly. However, the RedirectToAction is not firing, and the application gets stuck at /Home/Add, when it should redirect to /Home/Index. The view loads however, so it shows /Home/Index but the URL says /Home/Add.
Here is a copy of the partial view that contains the form:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<todo_moble_oauth.Models.todo>" %>
<% using (Html.BeginForm()) { %>
<%: Html.AntiForgeryToken() %>
<%: Html.ValidationSummary(true) %>
<fieldset>
<h3>Title:</h3>
<div class="editor-field">
<input type="text" name="title" />
</div>
<h3>Description:</h3>
<div class="editor-field">
<input type="text" name="description" />
</div>
<h3>Priority:</h3>
<div class="editor-field">
<select name="priority">
<option value="1">Low</option>
<option value="2">Medium</option>
<option value="3">High</option>
</select>
</div>
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<h3>Color:</h3>
<input type="radio" name="color" id="radio-choice-1" value="0" checked="checked" />
<label for="radio-choice-1">None</label>
<input type="radio" name="color" id="radio-choice-2" value="1" />
<label for="radio-choice-2">Red</label>
<input type="radio" name="color" id="radio-choice-3" value="2" />
<label for="radio-choice-3">Blue</label>
<input type="radio" name="color" id="radio-choice-4" value="3" />
<label for="radio-choice-4">Yellow</label>
</fieldset>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
So data is being sent to the database and stored, however the redirect is broken.
Turns out it is an issue with jQuery mobile, this threads solution resolved the issue for me:
jQuery Mobile/MVC: Getting the browser URL to change with RedirectToAction
OK, I'm sorry if the tile of the question was unclear, and if you understand what I mean, please don't hesitate to help me think of a better one.
Anyway, I have a <input type="submit"> element for my form, and I want it to return the same URL as the URL of the page the element is on.
Currently, if I click the button, it takes me from /Calculate/Quadratic to /Calculate/QuadraticForm
In my controller for this view, I have the following code:
[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Quadratic()
{
ViewData["Root1"] = "";
ViewData["Root2"] = "";
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Quadratic(QuadCalc boss)
{
ViewData["Root1"] = x1;
ViewData["Root2"] = x2;
return View();
}
And here is the markup and code for my Quadratic view page, which includes the form which includes the submit button I've been referring to:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Quadratic</h2>
<% using(Html.BeginForm("QuadraticForm", "Calculate")) %>
<% { %>
<div>
a: <%= Html.TextBox("quadraticAValue") %>
<br />
b: <%= Html.TextBox("quadraticBValue") %>
<br />
c: <%= Html.TextBox("quadraticCValue") %>
<br />
<input type="submit" id="quadraticSubmitButton" value="Calculate!" />
<br />
<p><%= ViewData["Root1"] %></p>
<p><%= ViewData["Root2"] %></p>
</div>
<% } %>
</asp:Content>
Therefore, all I really want is to have the submit button return the same page, but the HTTP post will aid the application in passing new ViewData. Unless I'm interpreting this all wrong.
The problem is in your BeginForm method that calls the QuadraticForm action
<% using(Html.BeginForm("QuadraticForm", "Calculate")) %>
If you want to give an ID to the form you should use
<% using (Html.BeginForm("Quadratic", "Calculate", FormMethod.Post, new { id = "QuadraticForm" })) { %>
If you dont mind about the ID and want to just return to the same action just use
<% using(Html.BeginForm() %>
The parameters are:
The action name
The controller Name
The form method (get/post)
The form attributes
Given the following view model and action using the DefaultModelBinder, it seems to ignore the dictionary, but bind all other properties correctly. Am I missing something here? Looking at the MVC source code this seems legit.
Thanks
public class SomeViewModel
{
public SomeViewModel()
{
SomeDictionary = new Dictionary<string, object>();
}
public string SomeString { get; set; }
public IDictionary<string, object> SomeDictionary { get; set; }
}
[HttpPost]
public ActionResult MyAction(SomeViewModel someViewModel)
{
//someViewModel.SomeString binds correctly
//someViewModel.SomeDictionary is null
}
<%# Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<SomeViewModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content runat="server" ID="Content2" ContentPlaceHolderID="MainContent">
<% using (Html.BeginForm("MyAction", "MyController")) {%>
<%= Html.EditorFor(m => m.SomeString) %>
<%= Html.EditorFor(m => m.SomeDictionary["somevalue"]) %>
<input type="submit" value="Go" />
<%} %>
</asp:Content>
And for reference, the HTML output is:
<input class="text-box single-line" id="SomeString" name="SomeString" type="text" value="" />
<input class="text-box single-line" id="Somedictionary_somevalue_" name="SomeDictionary[somevalue]" type="text" value="" />
EDIT: The above will not work as pointed out below, however I prefer this layout and the following quick hack works for my needs, call this just after posting...
someViewModel.SomeDictionary = (from object key in Request.Form.Keys
where key.ToString().StartsWith("SomeDictionary[")
select new
{
Key = key.ToString().Replace("SomeDictionary[", string.Empty).Replace("]", string.Empty),
Value = (object)Request.Form[key.ToString()]
}).ToDictionary(arg => arg.Key, arg1 => arg1.Value);
It needs some tidying up ofcourse :)
You may take a look at this post to see how dictionaries should be binded. I am afraid that using strongly typed EditorFor helpers you won't be able to achieve this and you will have to generate the fields manually.
I want to retain data in the view controls like drop down list, radio button, checkbox, textbox while displaying same view again with validation fail message. Data is in the drop down list bind using the ViewData. Check box data bind using viewdata. User enter values in the textbox and for checkbox control.
When view is displayed with the validation messages controls are reset and for drop down list data needs to be passed in the viewData before displaying view. Please let me know any solution to this.
Thanks & Regards,
Tsara.
Find the required code below
Please find below View . There is some data in the drop down list and also in the check box. For the first time page render it is passed in the ViewData.
<div id="RegisterLogin">
<label id="Label1"> Project:</label>
<%= Html.Label( Convert.ToString(ViewData["SelectedProject"])) %>
<%= Html.DropDownList("ProjectCode", (SelectList)ViewData["ddlProject"], "--Select--", new { id = "ddlProject" , title = "Select Project" })%>
<br />
<br />
<label id="Label2">
Select User:</label>
<%= Html.DropDownList("ddlUsers", (SelectList)ViewData["ddlUsers"], "--Select User--", new { id = "ddlUsers", title = "Select User" })%>
<br />
<br />
<label id="spacing">
Username:</label>
<input type="text" name="txtUserName" title="Enter username" id="txtUserName" />
<%= Html.ValidationMessage("username") %>
<br />
<br />
<label id="leftalign">
Password :</label>
<input type="password" name="txtPassword" title="Enter your password" id="txtPassword" />
<%= Html.ValidationMessage("password") %>
<br />
<br />
Confirm Password :
<input type="password" name="txtConfirmPassword" title="Confirm your password" id="txtConfirmPassword" />
<%= Html.ValidationMessage("confirmPassword") %>
<br />
<br />
<label id="space" >Email :</label>
<input type="text" name="txtEmailId" title="Enter your email id" id="txtEmailId" />
<%= Html.ValidationMessage("email") %>
<br />
<br />
<label id="rolealign"> Assign Role : </label>
<div class="listPermissions">
<%= Html.ValidationMessage("role") %>
<% Dictionary<string, string> lstRoles = ViewData["ListRoles"] as Dictionary<string, string>; %>
<% if (lstRoles != null)
{
int iCounter = 1;
foreach (KeyValuePair<String,String> item in lstRoles)
{ %>
<%= Html.CheckBoxCustom(Convert.ToString(iCounter+1), "chkRole", item.Value,"") %>
<%= Html.Encode(item.Key) %><br />
<%}
}%>
</div>
<br />
<input type="image" src="../../Content/save.jpg" value="Save" alt="Save" style="padding-left:250px;"/>
<%--<img src="../../Content/save.jpg" alt="Save" id="imageGO" />--%>
<img src="../../Content/reset.jpg" onclick="document.forms[0].reset()" alt="Reset"/>
<%--<img src="../../Content/reset.jpg" alt="Reset" onclick="return btnCancel_onclick()" />--%>
</div>
In postback function call validation is done as follows
public ActionResult Register(string txtUserName, string txtPassword, string txtConfirmPassword, string txtEmailId, FormCollection frmCollection)
{
string strValue = frmCollection.Get("chkRole");
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
if (ValidateRegistration(txtUserName, txtEmailId, txtPassword, txtConfirmPassword, frmCollection))
{
if (strValue == "")
{
ModelState.AddModelError("role", "Please select the role");
}
else
{
// Attempt to register the user
MembershipCreateStatus createStatus = MembershipService.CreateUser(txtUserName, txtPassword, txtEmailId);
if (createStatus == MembershipCreateStatus.Success)
{
FormsAuth.SignIn(txtUserName, false /* createPersistentCookie */);
// Create an empty Profile for the newly created user
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("_FORM", ErrorCodeToString(createStatus));
}
}
}
//IDictionary<string, ValueProviderResult> valueProvider = frmCollection.ToValueProvider();
//Dictionary<string, string> valueProvider = frmCollection.AllKeys.ToDictionary(k => k, v => frmCollection[v]);
//foreach (string i in valueProvider.Keys)
//{
// ModelState.SetModelValue(i, valueProvider[i]);
//}
//Get list of projects
ProjectOperations objProjectOperations = new ProjectOperations();
List<Project> lstProjects = objProjectOperations.GetAll();
//Logging://Verbose:
logEntry = new LogEntry("List of project added into view data", "AzureTableLogs", 0, 0, System.Diagnostics.TraceEventType.Verbose, "", null);
Logger.Write(logEntry);
ViewData["ddlProject"] = new SelectList(lstProjects, "ProjectCode", "ProjectName");
MembershipUserCollection lstUser = Membership.GetAllUsers();
ViewData["ddlUsers"] = new SelectList(lstUser, "UserName", "UserName");
//MembershipUserCollection lstUser= Membership.GetAllUsers();
// If we got this far, something failed, redisplay form
return View();
}
Every time before displaying view need to have values in the view data.
For resolve this i tried solution i got like
IDictionary valueProvider = frmCollection.ToValueProvider();
but it is not supported in mvc 2.0.
If you need more details please let me know.