I'm working on an MVC 2 project and I have a model that looks like this:
public string AccountNumber { get; set; }
public IList<Equipment> ShippedEquipmentList { get; set; }
and a view that has a button for adding a new piece of equipment. Clicking the button dynamically adds new textboxes to the view for specifying another piece of equipment. The partial view it renders looks like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<RmaMVC.Models.Entities.Equipment>" %>
<div class="editorRow">
Item: <%: Html.TextBoxFor(x => x.ItemID); %>
Value: <% Html.TextBoxFor(x => x.Description); %>
</div>
My question is: how do I bind this data to the model? When the controller gets called the ShippedEquipmentList comes back as null.
Edit: here is what I have so far. My model looks like this:
public string AccountNumber { get; set; }
public IList<Equipment> ShippedEquipmentList { get; set; }
FormInputs()
{
ShippedEquipmentList = new List<Equipment>();
// adding a single blank piece of equipment so that the length isn't 0
Equipment blank = new Equipment();
ShippedEquipmentList.Add(blank);
}
my main view is this:
<% Html.BeginForm(); %>
<div id="items">
</div>
<%: Ajax.ActionLink("add new", "AddNewEquipment", new AjaxOptions {
UpdateTargetId = "items", InsertionMode = InsertionMode.InsertAfter }) %>
<input type="submit" value="submit" />
<% Html.EndForm(); %>
my controller:
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(FormInputs input)
{
return View(input);
}
public ActionResult AddNewEquipment()
{
return PartialView("~/Views/RMA/EditorTemplates/Equipment.ascx");
}
the partial view that generates the text boxes for the equipment:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<RmaMVC.Models.Entities.Equipment>" %>
<p>
<%: Html.TextBoxFor(x => x.ItemID) %>
<%: Html.TextBoxFor(x => x.Description) %>
<%: Html.TextBoxFor(x => x.Quantity) %>
<%: Html.TextBoxFor(x => x.SerialNumber) %>
</p>
Check this: Model Binding To A List
Related
So basically i have made table query "ProductTable" in my data connections
the query contains 4 rows ID,Name,Quantity and Price
all i want is to fill this table with data
in Table.aspx i have this :
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Father.Models.ProductTable>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Table
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h1>Table</h1>
<% using (Html.BeginForm()) { %>
<%: Html.AntiForgeryToken() %>
<%: Html.ValidationSummary() %>
<fieldset>
<ol>
<li>
<%: Html.LabelFor(z => z.ID) %>
<%: Html.TextBoxFor(z => z.ID) %>
</li>
<li>
<%: Html.LabelFor(z => z.Name) %>
<%: Html.TextBoxFor(z => z.Name) %>
</li>
<li>
<%: Html.LabelFor(z => z.Quantity) %>
<%: Html.TextBoxFor(z => z.Quantity) %>
</li>
<li>
<%: Html.LabelFor(z => z.Price) %>
<%: Html.TextBoxFor(z => z.Price) %>
</li>
</ol>
<input type="submit" value="Enter" />
</fieldset>
<% } %>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="FeaturedContent" runat="server">
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="ScriptsSection" runat="server">
</asp:Content>
In my Tablemodel :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Globalization;
using System.Web.Security;
namespace Father.Models
{
public class ProductTable
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int Quantity { get; set; }
[Required]
public int Price { get; set; }
}
}
in my TableController :
public class TableController : Controller
{
public ActionResult Table()
{
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Table(ProductTable zxc)
{
if (ModelState.IsValid)
{
try
{
}
catch
{
}
}
return View(zxc);
}
}
What to write inside the controller so i can finally put my data into the table
I think you need to spend some more time reading about the MVC paradigm vs. Web Forms. Take a look at Razor as well. You appear to be mixed up as to how everything is supposed to be happening.
You don't need the two methods in your controller.
public ActionResult Table() {
var zxc = new ProductTable();
// Load the data into the object.
return View(zxc);
}
That method in your controller above will pass the ProductTable into the View you have at the top of the question as a Model which will then allow you to populate the data into the Labels and Textboxes that you have set up.
Your HttpPost method that you have there is for saving the data that is changed from the form. You will then validate that everything gets updated properly, and either send them back to another form (if the save was successful), or back to the Table view you have with the model back inside that includes any validation errors or other tips to the user.
I have this model:
public class Package
{
public string CustomerName { get; set; }
public List<Product> Products { get; set; }
public int Id { get; set; }
}
public class Product
{
public int Quantity { get; set; }
public string Name { get; set; }
public int Id { get; set; }
}
I have a Create's view for creating a new Package and one or many Create product's PartialView.
How I can submit the Product informations to the Product's controller without leave the View?
Here is my Product's Partialview code:
<% using (Ajax.BeginForm("Create", "Product", new AjaxOptions())) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Name) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Name) %>
<%: Html.ValidationMessageFor(model => model.Name) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Quantity) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Quantity) %>
<%: Html.ValidationMessageFor(model => model.Quantity) %>
</div>
<%-- <p>
<input type="submit" value="Create" />
</p>--%>
</fieldset>
<% } %>
Thank you
You can use AJAX to solve your problem. The code pode below post 3 values to YourController in YourAction.
jQuery('#YourButton').click(function (event) {
var jqxhr = $.post("YourController/YourAction", {
lastName: $("#tbLastName").val(),
firstName: $("#tbFirstName").val(),
id: $("#id").val()
},
function (data) {
$('#WhereResultAppear').html(data); //can be deleted
})
.success(function () {
$('#InCaseOfSuccess').html(data); //can be deleted
})
.error(function (jqXHR, status, error) {
$('#InCaseOfError').html(data); //can be deleted
})
.complete(function () {
$('#WhenActionIsComplete').html(data); //can be deleted
});
});
Give an id to your form in the partial view. and then use JQuery to send data by serializing the form.
$.post("controller/action", $("#formId").serialize(),
function(data) {
alert("Data Loaded: " + data);
});
And in the controller
Package pakage = new Package();
UpdateModel(pakage );
Although you can write code to serialize the data as the other answers mention, I would consider surrounding the fields in an
#using (Ajax.BeginForm("ActionOnController", new AjaxOptions{}))
{
}
This will do it all for you (as long as you include the necessary javascript e.g. jquery.unobtrusive-ajax.min.js)
Take a look at the AjaxOptions documentation. This allows you to (amongst other things) update another section after posting the values back. You can use this to indicate success
Martin
I have this Model:
public class Package
{
public string CustomerName { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public int Quantity { get; set; }
public string Name { get; set; }
}
When I add the Create's view, the code is:
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.CustomerName) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.CustomerName) %>
<%: Html.ValidationMessageFor(model => model.CustomerName) %>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
How manage the Products list?
Can I get a button or something to create a new product and add it to the Products list?
Thank you
for creating buttn you can also do this use HTML.ActionLink() or Ajax.ActionLink() defined as a button like:
<% Response.Write(Html.ActionLink("Add Product", "Create", new { id = tId, tNum = tNum }, new { #class = "oldVal" })); %>
it'll create a button on your page, and here you can see different attributes (so just check them out you'll find it useful) ... and from both of this which to use is more depends upon your desired action ...
and in your controller:
public ActionResult Create()
{
// do same as mentioned by [gnome][1]
}
here is the example: modal popup, declared as a partial view of page
[partial view using Ajax.ActionLink()]
using (Ajax.BeginForm("Login", "Users", null, new AjaxOptions() { UpdateTargetId = "divLoginPopupContent" }))
{
Response.Write(Html.ValidationSummary(true));
%>
<ul class="chooseQuestion">
<li>
<div class="short">
<%= Html.LabelFor(model => model.LoginEmail)%>
</div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.LoginEmail)%>
<%= Html.ValidationMessageFor(model => model.LoginEmail)%>
</div>
</li><li>
<div class="short">
<%= Html.LabelFor(model => model.LoginPassword)%>
</div>
<div class="editor-field">
<%= Html.PasswordFor(model => model.LoginPassword)%>
<%= Html.ValidationMessageFor(model => model.LoginPassword)%>
</div>
</li><li>
<div class="checkbox">
<%= Html.CheckBoxFor(model => model.Remember)%>
<%= Html.LabelFor(model => model.Remember)%>
</div>
</li><li>
<input type="submit" class="button" value="Login" id="btnLoginSubmit" />
<div id="divlogin_ajaxloading" style="display:none; vertical-align:top; text-align:center;"><img alt="" src="/images/ajax-loader.gif" /></div>
</li>
</ul>
}
your partial view action:
public ActionResult Login(LoginModel model)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.LoginEmail, model.LoginPassword))
{
return Redirect("/MyPage");
}
else
{
ModelState.Clear();
return PartialView("LoginPopup", new LoginModel());
}
}
}
Just typed this up, haven't tested it but the basic idea is to list your products on the customer view with a button to add a new product for a customer.
To list Products for Customer:
<table>
<% foreach(var p in Customer.Products) { %>
<tr>
<td><%: p.Quantity %></td>
<td><%: p.Name %></td>
</tr>
<% } %>
</table>
<p><%: Html.ActionLink("Add Product", "Create", new { controller = 'Products' }, new { id = 'addProduct'}) %>
<div id="dialog"></div>
To add a new Product for a customer you could use jQuery UI to show a dialog; just pass the Customer Id
// assuming you've included jquery-ui
<script type="text/javascript">
$(document).ready(function() {
$('#dialog').dialog({
autoOpen : false,
button : {
'Save' : function() {
$.ajax({
url : $('#addProduct').attr('href'),
type : 'get'
success : function() {
alert('Product added!');
}
});
},
'Cancel' : function() {
$('#dialog').dialog('close');
}
}
});
$('#addProduct').click(function() {
var customerId = $('#CustomerId').val();
$('#dialog').dialog('open');
$('#Customer_Product_Id').val(customerId); // assuming there's a hidden field on the form
});
});
</script>
In your product controller return a partial
public ActionResult Create()
{
List<Product> products = new List<Product>() {
new Product() { Id = 1, Name = "Rice" },
new Product() { Id = 2, Name = 'Corn' }};
ViewData.Add("Products", new SelectList(products, "Id", "Name", ""));
Product product = new Product();
if (Request.IsAjaxRequest())
{
return PartailView("_CreateOrEdit", product)
}
return View("Create", product);
}
_Create view
<%: Html.TextBoxFor(model => model.Quantity) %>
<%: Html.DropDownList("Products") %>
<%: Html.HiddenFieldFor(model => model.Product.Customer.Id) %>
I'm quite new to ASP MVC so maybe this is simple but I'm really stuck cause I've searched the forums with no luck ;)
I've prepared a simple example of my problem. Here's what I'm trying to do. I have the following model that I'm passing to the view:
public class SearchModel
{
public int ItemsFound { get; set; }
public int TotalItems { get; set; }
public SearchFacetModel SearchFacet { get; set; }
}
and the nested class is
public class SearchFacetModel
{
public string SearchText { get; set; }
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
}
The controller looks like this
[HttpGet]
public ActionResult Index()
{
SearchModel model = new SearchModel { SearchFacet = new SearchFacetModel() { SearchText = "test", DateFrom = DateTime.Now }, ItemsFound=1, TotalItems=10 };
return View(model);
}
[HttpPost]
public ActionResult Index(SearchModel model) // Where is model.SearchFacet? Why null?
{
return View(model);
}
The view:
<% using(Html.BeginForm()) { %>
Search<br />
<%=Html.TextBoxFor(m=>m.ItemsFound) %><br />
<%=Html.TextBoxFor(m=>m.TotalItems) %><br />
Search Facet<br />
<% Html.RenderPartial("SearchFacet", Model.SearchFacet); %>
<button type=submit>Submit</button>
<%} %>
The SearchFacet control:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcApplication3.Models.SearchFacetModel>" %>
<%=Html.TextBoxFor(m=>m.DateFrom) %>
<%=Html.TextBoxFor(m=>m.DateTo) %>
<%=Html.TextBoxFor(m=>m.SearchText) %>
Now, I've already put comment near the Index method with HttpPost. I'm getting the SearchModel object but it doesn't have its SearchFacet child object. The child is null.
When I'm changing SearchFacet.ascx to accept whole SearchModel and pass it from main view then it works. But I don't want to pass whole stuff always and everywhere.
I'm surely missing something related to Model Binder but what and how to make it work my way? :)
Any help will be appreciated.
Gacu
The reason it doesn't work is because HTML helpers inside the partial doesn't generate proper names for the input tags. If you look at the generated source code you will see:
<input type="text" name="SearchText" id="SearchText" value="test" />
whereas it should be:
<input type="text" name="SearchFacet.SearchText" id="SearchFacet_SearchText" value="test" />
in order for the default model binder to properly map the objects in the POST action. One way to solve this is to use editor templates:
<% using(Html.BeginForm()) { %>
Search<br />
<%=Html.TextBoxFor(m=>m.ItemsFound) %><br />
<%=Html.TextBoxFor(m=>m.TotalItems) %><br />
Search Facet<br />
<%= Html.EditorFor(x => x.SearchFacet) %>
<button type=submit>Submit</button>
<%} %>
and then inside (~/Views/Home/EditorTemplates/SearchFacetModel.ascx):
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<MvcApplication3.Models.SearchFacetModel>" %>
<%=Html.TextBoxFor(m=>m.DateFrom) %>
<%=Html.TextBoxFor(m=>m.DateTo) %>
<%=Html.TextBoxFor(m=>m.SearchText) %>
Note that the name of the partial should be the same as the type of the property: SearchFacetModel.ascx and located inside the EditorTemplates folder.
Why is my controller receiving an empty model in this case?
Using
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<X.Models.ProductModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<h2>Product</h2>
<% using (Html.BeginForm() {%>
<%: Html.ValidationSummary(true) %>
<div class="editor-label">
Product Name
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Name) %>
<%: Html.ValidationMessageFor(model => model.Name) %>
</div>
<br />
<div class="editor-label">
Short Description
</div>
<div class="editor-field">
<%: Html.TextAreaFor(model => model.ShortDesc) %>
<%: Html.ValidationMessageFor(model => model.ShortDesc) %>
</div>
<br />
<div class="editor-label">
Long Description
</div>
<div class="editor-field">
<%: Html.TextAreaFor(model => model.LongDesc) %>
<%: Html.ValidationMessageFor(model => model.LongDesc) %>
</div>
<p>
<input type="submit" value="Create" />
</p>
<% } %>
</asp:Content>
and the following controller.
using System.Web.Mvc;
using X.Lib.Services;
using X.Models;
namespace X.Admin.Controllers
{
public class ProductController : Controller
{
[HttpGet]
public ActionResult ProductData()
{
return View();
}
[HttpPost]
public ActionResult ProductData(ProductModel NewProduct)
{
//Validate and save
if(ModelState.IsValid)
{
//Save And do stuff.
var ProductServ = new ProductService();
ProductServ.AddProduct(NewProduct);
}
return View();
}
}
}
Model:
public class ProductModel
{
public int ID;
[Required(ErrorMessage = "Name is required")]
public string Name;
public string LongDesc;
public string ShortDesc;
}
EDIT: you need to use properties not variables
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
public string LongDesc { get; set; }
public string ShortDesc { get; set; }
Also...
You are not passing the model back to the view.
return View(NewProduct);
I normally pass a blank model in to the GET action too
ProductModel NewProduct = new ProductModel();
return View(NewProduct);
This way if you wish to set any default values you can do so easily.
Code example in full I've also added try and catch blocks around the adding of the product and given example views you could be returning on success or fail:
[HttpGet]
public ActionResult ProductData()
{
ProductModel NewProduct = new ProductModel();
return View(NewProduct);
}
[HttpPost]
public ActionResult ProductData(ProductModel NewProduct)
{
//Validate and save
if(!ModelState.IsValid)
{
// Return the model back to view
return View(NewProduct);
}
try{
//Save And do stuff.
var ProductServ = new ProductService();
ProductServ.AddProduct(NewProduct);
}
catch(Exception){
return View("Fail");
}
return View("Success");
}