Why is my model's data being cleared at POST - c#
I'm sorry if this ends up simple or stupid question, I'm very new to MVC and Razor, being dropped in deep end with no formal training on anything current...
I don't understand why the data in my model is available in my first view is not available in my second view.
Controller initiates an instance of model using code
Product _Product = new Product();
then the first view is called after a bunch of initial data queries have ran. The view is called using
return View("Attributes", _Product)
to call the view passing it the _Product instance of model.
My first view does many things but relevant to this question it shows a table using foreach row on dataset filled by SQL on controller stored on model.
#foreach(DataRow row in Model.attList.Tables[0].Rows)
The view has multiple buttons, such as add, delete, edit... Each have their own action assigned such as
<input type="submit" class="btn btn-primary" name="action:btnDelete" value="Delete" />
If the user presses one of the buttons on that view that triggers HTTPPOST. One of them calls a new view but if I pass my model to that view again, the data is now blank? I am passing the model to the view, and accepting it in HTTPPOST and passing it to the next view (or so I thought).
I'm using very generic beginning to form
#using (Html.BeginForm()) {
The code in controller for this button is very basic
[HttpPost]
[MultipleButton(Name = "action", Argument = "btnEditValues")]
public ActionResult btnEditValues(Product _Product)
{
return View("btnEditValues", _Product);
}
But then in that 2nd view if I try to re-display the same table, it now says my Model.attList is empty. I added quick script of console.log(#Model.attList); and when I click the button it is showing the 2nd view but the console is logging a blank value (not NULL, just blank)...
I'm sure I'm just missing something simple - or perhaps I'm trying to do something I can't ???
The following was copied/pasted, without edit, into this question from an errantly posted "answer" which was intended to be an edit on the question. The answer has been flagged and should be removed.
Per David's Request.
Here is complete M, V, V, and C (didn't include other 2 views they don't use Model and both work)...
!!! NOTE !!!: This code is being used somewhere everyone has SQL manager and Admin rights to SQL server. As such SQL parameters were not used. This code would be vulnerable to SQL injection if used on open Website.
Model
namespace P21.Rules.Visual.Areas.KCDA_ItemMaint_Attributes.Models
{
public class Product
{
// Variables used between views
public int RowSelected { get; set; }
// Declare datasets to use as list
public DataSet attList { get; set; }
public DataSet lowList { get; set; }
}
}
Controller
namespace P21.Rules.Visual.Areas.KCDA_ItemMaint_Attributes.Controllers
{
#region Multiple Buttons
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MultipleButtonAttribute : ActionNameSelectorAttribute
{
public string Name { get; set; }
public string Argument { get; set; }
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
var isValidName = false;
var keyValue = string.Format("{0}:{1}", Name, Argument);
var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);
if (value != null)
{
controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
isValidName = true;
}
return isValidName;
}
}
#endregion
public class KCDA_ItemMaint_AttributesController : BaseRuleController
{
#region public variables
// public dataset for loading SQL values
DataSet attload = new DataSet();
DataSet lowload = new DataSet();
#endregion
#region Main Action
// GET: KCDA_ItemMaint_Attributes/KCDA_ItemMaint_Attributes
public ActionResult Attributes()
{
if (Data.Fields["item_id"].FieldValue == string.Empty)
{
// Report error and prevent form pop-up if no product group has been selected
Rule.RuleResult.Success = false;
Rule.RuleResult.Message = "You must first select an Item before listing Attributes";
return RedirectToAction("Close", "Initialize", new { area = "" });
}
else
{
try
{
// Create container and setup Group values
Product _Product = new Product();
//Get Attributes for selected item
LoadAttributes();
_Product.attList = attload.Copy();
//Get ECom Server Side Attribute for selected item
LoadLower();
_Product.lowList = lowload.Copy();
return View("Attributes", _Product);
}
catch (Exception ex)
{
//catch the error and send it to the Error view with the HandleErrorInfo
return View("Error", new HandleErrorInfo(ex, "KCDA_ItemMaint_Attributes", "Attributes"));
}
}
}
#endregion
#region Buttons
[HttpPost]
[MultipleButton(Name = "action", Argument = "btnDelete")]
public ActionResult btnDelete(Product _Product)
{
// create SQL command to delete the
string sqlDelete = "UPDATE BL_ProductAttribute SET DeleteFlag = '1' WHERE [KEY] = '" + _Product.RowSelected + "'";
// Run the sqlDELETE command
SqlCommand cmdDelete = new SqlCommand(sqlDelete, P21SqlConnection);
cmdDelete.ExecuteNonQuery();
SqlDataAdapter daDelete = new SqlDataAdapter(cmdDelete);
return View("btnDelete", _Product);
}
[HttpPost]
[MultipleButton(Name = "action", Argument = "btnAdd")]
public ActionResult btnAdd(Product _Product)
{
// Retrieve selected/loaded drop-down values
string ddGroup = Request["ApplyGroup"];
string ddName = Request["AttributeName"];
string ddValue = Request["AttributeValue"];
if (ddValue == "")
{
ViewBag.msg = "No Value Selected";
}
else
{
// default duplicate count to 0
int duplicate = 0;
// create SQL command to check for duplicate attribute
string sqlDuplicate = "SELECT COUNT(1) FROM BL_ProductAttribute " +
"WHERE SKU = '" + Data.Fields["item_id"].FieldValue + "' " +
"AND AttributeGroupName = '" + ddGroup + "'";
// Run the sqlDuplicate command
SqlCommand cmdDuplicate = new SqlCommand(sqlDuplicate, P21SqlConnection);
cmdDuplicate.CommandType = CommandType.Text;
SqlDataAdapter daDuplicate = new SqlDataAdapter(cmdDuplicate);
// Create dataset from duplicate check
DataTable dupcheck = new DataTable();
daDuplicate.Fill(dupcheck);
// Set count if exists
duplicate = int.Parse(dupcheck.Rows[0][0].ToString());
// if exists update/undelete otherwise insert
if (duplicate > 0)
{
// create SQL command to update the attribute
string sqlAdd = "UPDATE BL_ProductAttribute " +
"SET BL_ProductAttribute.Value = '" + ddValue.Replace("'", "''") + "', " +
"BL_ProductAttribute.AttributeTitle = '" + ddName + "', " +
"BL_ProductAttribute.DeleteFlag = 0, " +
"BL_ProductAttribute.ProductID = '" + Data.Fields["product_group_id"].FieldValue + "' " +
"FROM BL_ProductAttribute " +
"WHERE SKU = '" + Data.Fields["item_id"].FieldValue + "' AND AttributeGroupName = '" + ddGroup + "' ";
// Run the sqlAdd command
SqlCommand cmdAdd = new SqlCommand(sqlAdd, P21SqlConnection);
cmdAdd.ExecuteNonQuery();
SqlDataAdapter daAdd = new SqlDataAdapter(cmdAdd);
ViewBag.msg = "Record Updated";
}
else
{
// If adding determine next key value for unique ID
string newKey = string.Empty;
// create SQL command to get next KEY value for insert reset current maxkey
string sqlMax2 = "SELECT max([key])+1 FROM BL_AttributeEnumValue";
// Run the sqlMax command
SqlCommand cmdKey2 = new SqlCommand(sqlMax2, P21SqlConnection);
cmdKey2.CommandType = CommandType.Text;
SqlDataAdapter daKey2 = new SqlDataAdapter(cmdKey2);
// Create dataset from newKey check and assign to newKey
DataTable KeyCheck2 = new DataTable();
daKey2.Fill(KeyCheck2);
newKey = KeyCheck2.Rows[0][0].ToString();
// create SQL command to update the attribute
string sqlAdd = "INSERT INTO BL_ProductAttribute ([Key], ProductId, SKU, AttributeTitle, " +
"isSKUlevel, isRequired, isDefault, Value, AttributeGroupName, DeleteFlag) " +
"VALUES('" + newKey + "', '" + Data.Fields["product_group_id"].FieldValue + "', '" +
Data.Fields["item_id"].FieldValue + "', '" + ddName + "', 1, 1, 1, '" +
ddValue.Replace("'", "''") + "', '" + ddGroup + "', 0)";
// Run the sqlAdd command
SqlCommand cmdAdd = new SqlCommand(sqlAdd, P21SqlConnection);
cmdAdd.ExecuteNonQuery();
SqlDataAdapter daAdd = new SqlDataAdapter(cmdAdd);
ViewBag.msg = "Record Added";
}
}
return View("btnAdd", _Product);
}
[HttpPost]
[MultipleButton(Name = "action", Argument = "btnEditValues")]
public ActionResult btnEditValues(Product _Product)
{
return View("btnEditValues", _Product);
}
#endregion
#region SQL Loads
private void LoadAttributes()
{
// Define SQL select command
string sqlAttributes = "SELECT * FROM BL_ProductAttribute " +
"WHERE SKU = '" + Data.Fields["item_id"].FieldValue + "' AND DeleteFlag = '0' " +
" AND AttributeGroupName in ('SKU_Color', 'SKU_SelectableAttribute_1', 'SKU_SelectableAttribute_2')";
// Set SQL command type to text and run it
SqlCommand cmdlist = new SqlCommand(sqlAttributes, P21SqlConnection);
cmdlist.CommandType = CommandType.Text;
SqlDataAdapter dalist = new SqlDataAdapter(cmdlist);
// Load results from SQL into DataSet
dalist.Fill(attload);
}
private void LoadLower()
{
string DBconn = "vsldb1";
// Define SQL select command
string sqllist = "SELECT [Key], ProductID, SKU, AttributeTitle, isSKUlevel, isRequired, isDefault, " +
"\"Value\" = Case " +
"when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '0') then '0 - Warehouse Regular Item' " +
"when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '1') then '1 - Not on Website/Pending' " +
"when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '2') then '2 - RFQ' " +
"when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '3') then '3 - Limited Quote' " +
"when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '4') then '4 - Discontinued/Obsolete' " +
"when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '5') then '5 - Specials' " +
"when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '6') then '6 - Direct Ship' " +
"when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '7') then '7 - Offline' " +
"else value end, AttributeGroupName, UpdateFlag FROM OPENDATASOURCE('SQLOLEDB','Data Source=" + DBconn + ";user " +
"id=sa;password=KCDAAdmin').KCDA.dbo.KCDA_ProductAttribute PA" +
" WHERE PA.SKU = '" + Data.Fields["item_id"].FieldValue + "' AND PA.AttributeGroupName not in " +
"('SKU_Color', 'SKU_SelectableAttribute_1', 'SKU_SelectableAttribute_2')";
// Set SQL command type to text and run it
SqlCommand cmdlist = new SqlCommand(sqllist, P21SqlConnection);
cmdlist.CommandType = CommandType.Text;
SqlDataAdapter dalist = new SqlDataAdapter(cmdlist);
// Load results from SQL into DataSet
dalist.Fill(lowload);
}
#endregion
#region Drop Downs
[HttpPost]
public ActionResult GetAttributeNames(string selectedOption)
{
// Define variables for JSON query to use
JsonResult result = new JsonResult();
List<string> attNames = new List<string>();
List<string> attValues = new List<string>();
if (selectedOption != null)
{
// SQL to get attribute name for the selected attribute group for this product group
string sql = "SELECT Title FROM BL_Attribute (NOLOCK) WHERE BL_Attribute.DeleteFlag = '0' AND BL_Attribute.AttributeGroupName = '" + selectedOption + "'" +
" AND BL_Attribute.Title in (select AD.AttributeTitle from BL_AttributeDept AD where AD.product_group_id = '" + Data.Fields["product_group_id"].FieldValue.Substring(0, 2) +"')";
using (SqlCommand selectAttNames = new SqlCommand(sql, P21SqlConnection))
{
using (SqlDataReader reader = selectAttNames.ExecuteReader())
{
while (reader.Read())
{
attNames.Add(reader["Title"].ToString());
}
}
}
// SQL to get list of current available values for this attribute type
string sql2 = "SELECT Value FROM BL_AttributeEnumValue (NOLOCK) WHERE DeleteFlag = '0' and AttributeTitle = '" +
attNames[0] + "' ORDER BY Value";
using (SqlCommand selectAttValues = new SqlCommand(sql2, P21SqlConnection))
{
using (SqlDataReader reader2 = selectAttValues.ExecuteReader())
{
while (reader2.Read())
{
attValues.Add(reader2["Value"].ToString());
}
}
}
// define return object
var retObj = new
{
retNames = attNames,
retValues = attValues
};
return Json(retObj, JsonRequestBehavior.AllowGet);
}
return Json(new { Success = "false" });
}
#endregion
#region Edit Values
#endregion
#region Close Rule
[HttpPost]
public ActionResult Return()
{
Rule.RuleResult.Success = true;
//IMPORTANT - This is what returns the Visual Rule control back to the server
//DO NOT REMOVE
return RedirectToAction("Close", "Initialize", new { area = "" });
}
#endregion
}
}
Attributes View
#using P21.Rules.Visual.Areas.KCDA_ItemMaint_Attributes.Models
#using System.Data
#model Product
#{
ViewBag.Title = "Attributes";
Layout = "~/Views/Shared/_VisualRuleLayout.cshtml";
var listAttGroups = new List<SelectListItem>
{
new SelectListItem { Text = "SKU_Color", Value = "SKU_Color"},
new SelectListItem { Text = "SKU Select Att 1", Value = "SKU_SelectableAttribute_1"},
new SelectListItem { Text = "SKU Select Att 2", Value = "SKU_SelectableAttribute_2"}
};
}
#section scripts{
<script>
$(function () {
$("#ApplyGroup").change(function () {
var option = $(this).val();
//Clear and activate 2nd and 3rd drop down
$("#AttributeName").empty();
$("#AttributeValue").empty();
$("#AttributeName").prop('disabled', false);
$("#AttributeValue").prop('disabled', false);
var url = "GetAttributeNames?selectedOption=" + option;
$.post(url, function (retObj) {
$.each(retObj.retNames, function (i, attName) {
$("#AttributeName").append($('<option></option>').val(attName).html(attName));
});
$.each(retObj.retValues, function (i, attValue) {
$("#AttributeValue").append($('<option></option>').val(attValue).html(attValue));
});
});
});
});
</script>
}
#using (Html.BeginForm())
{
<div class="container">
<div class="row">
<label class="col-md-3 control-label" for="ApplyGroup" id="lblApplyGroup">Attribute Group</label>
<label class="col-md-3 control-label" for="AttributeName" id="lblAttributeName">Attribute Name</label>
<label class="col-md-2 control-label" for="AttributeValue" id="lblAttributeValue">Attribute Value</label>
<div class="col-md-2">
<input type="submit" class="btn btn-primary" name="action:btnEditValues" value="Edit Values" />
</div>
</div>
<div class="row" style="padding-top:5px">
<div class="col-md-3">
#Html.DropDownList("ApplyGroup", listAttGroups, "Select Group", new { #id = "ApplyGroup", #class = "form-control" })
</div>
<div class="col-md-3">
#Html.DropDownList("AttributeName", new List<SelectListItem>(), new { #id = "AttributeName", #class = "form-control", disabled = "disabled" })
</div>
<div class="col-md-3">
#Html.DropDownList("AttributeValue", new List<SelectListItem>(), new { #id = "AttributeValue", #class = "form-control", disabled = "disabled" })
</div>
<div class="col-md-3">
<span class="pull-right">
<input type="submit" class="btn btn-primary" name="action:btnAdd" value="Add\Update" />
</span>
</div>
</div>
<div class="row" style="padding-top:20px">
<div class="col-md-10">
</div>
<div class="col-md-2">
<span class="pull-right">
<input type="submit" class="btn btn-primary" name="action:btnDelete" value="Delete" />
</span>
</div>
</div>
<div class="row" style="padding-top:10px">
<div class="col-md-12">
<table id="attTable" style="width:100%" cellpadding="0" cellspacing="0" border="1" class="row">
<tbody>
<tr style="background-color: #F0F8FF;">
<th></th>
<th>Dept</th>
<th>Item #</th>
<th>Attribute Name</th>
<th>SKU Level</th>
<th>Required</th>
<th>Default</th>
<th>Attribute Value</th>
<th>Attribute Group</th>
</tr>
#foreach (DataRow row in Model.attList.Tables[0].Rows)
{
<tr class="selectable-row">
<td>#Html.RadioButtonFor(m => Model.RowSelected, row[0])</td>
<td>#row[1]</td>
<td>#row[2]</td>
<td>#row[3]</td>
<td>#row[4]</td>
<td>#row[5]</td>
<td>#row[6]</td>
<td>#row[7]</td>
<td>#row[8]</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<div class="row" style="padding-top:50px">
<div class="col-md-12">
<table id="lowTable" style="width:100%" cellpadding="0" cellspacing="0" border="1" class="row">
<tbody>
<tr style="background-color: #F0F8FF;">
<th>Dept</th>
<th>Item #</th>
<th>Attribute Name</th>
<th>SKU Level</th>
<th>Required</th>
<th>Default</th>
<th>Attribute Value</th>
<th>Attribute Group</th>
</tr>
#foreach (DataRow row in Model.lowList.Tables[0].Rows)
{
<tr class="selectable-row">
<td>#row[1]</td>
<td>#row[2]</td>
<td>#row[3]</td>
<td>#row[4]</td>
<td>#row[5]</td>
<td>#row[6]</td>
<td>#row[7]</td>
<td>#row[8]</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
btnEditValues View
#using P21.Rules.Visual.Areas.KCDA_ItemMaint_Attributes.Models
#using System.Data
#model Product
#{
ViewBag.Title = "btnEditValues";
Layout = "~/Views/Shared/_VisualRuleLayout.cshtml";
var listAttGroups = new List<SelectListItem>
{
new SelectListItem { Text = "SKU_Color", Value = "SKU_Color"},
new SelectListItem { Text = "SKU Select Att 1", Value = "SKU_SelectableAttribute_1"},
new SelectListItem { Text = "SKU Select Att 2", Value = "SKU_SelectableAttribute_2"}
};
}
#section scripts{
<script>
console.log(#Model.attList);
</script>
}
<p align="right"><button type="button" class="btn btn-primary" onclick="location.href='#Url.Action("Attributes", "KCDA_ItemMaint_Attributes")'">X</button></p>
<center><h2>Edit Item Attribute Availble Values</h2></center>
#using (Html.BeginForm())
{
<div class="container">
<div class="row">
<label class="col-md-4 control-label" for="EditApplyGroup" id="lblEApplyGroup">Attribute Group</label>
<label class="col-md-4 control-label" for="EditAttributeName" id="lblEAttributeName">Attribute Name</label>
</div>
<div class="row" style="padding-top:5px">
<div class="col-md-4">
#Html.DropDownList("EditApplyGroup", listAttGroups, "Select Group", new { #id = "EditApplyGroup", #class = "form-control" })
</div>
<div class="col-md-4">
#Html.DropDownList("EditAttributeName", new List<SelectListItem>(), "Select Name", new { #id = "EditAttributeName", #class = "form-control", disabled = "disabled" })
</div>
</div>
<div class="row" style="padding-top:10px">
<div class="col-md-12">
<table id="attEditTable" style="width:100%" cellpadding="0" cellspacing="0" border="1" class="row">
<tbody>
<tr style="background-color: #F0F8FF;">
<th></th>
<th>Attribute Value</th>
<th>Item Count</th>
</tr>
</tbody>
</table>
</div>
</div>
</div>
}
If your ultimate question is, "how can I post this model with all of it's data?",
public class Product
{
// Variables used between views
public int RowSelected { get; set; }
// Declare datasets to use as list
public DataSet attList { get; set; }
public DataSet lowList { get; set; }
}
then the only real answer is "with a LOT of work." The answer that follows is probably mostly opinionated, so not really a great SO answer, but reflects my experience.
Basically, this controller action:
[HttpPost]
[MultipleButton(Name = "action", Argument = "btnEditValues")]
public ActionResult btnEditValues(Product _Product)
{
// ... other code
return View("btnEditValues", _Product);
}
expects the model binder to be able to construct an instance of the Product class. To do so, it needs to have every public property be present as a form value in the HTTP request that was made to this controller action. Those form values come from what are called successful controls, which is basically any form element (<input />, <select>, or <textarea>, generally) which has a name (the name="propertynamehere" attribute). If the field's not present, it's value doesn't exist.
Given how complex the DataSet class is, I would not recommend attempting to build enough form fields to successfully get your data back from the model binder. The only thing you really should care about is RowSelected - that lets you fetch the data you need from the database. Given that an SQL connection and query are generally pretty fast, it's probably a better user experience, as well, since posting enough data to repopulate a DataSet requires A LOT of form values (you might even run into a issue with exceeding the maximum allowed number of fields, although that is configurable in ASP.NET). If that makes sense and sounds feasible, I can elaborate on how to refactor at least this action to match how MVC is intended to work.
As a note, if the goal is to take a user to an edit page, that makes more sense as a GET request; loading an edit screen is an idempotent action, which matches the semantics of a GET. POST is normally meant to be used to update values, which you're not doing there.
You might find this useful, as it describes the recommended pattern to follow (PRG, or POST-Redirect-GET): https://en.wikipedia.org/wiki/Post/Redirect/Get
Of course, if the material you're learning from is on the older side, ASP.NET (via WebForms) used POST to transfer something called ViewState, which was/is used to give the illusion of state within a web application - the idea was "Windows Forms for the Web", but it adds a lot of overhead that you really don't want in a modern aplication. Given your background, it (WebForms) might be a better fit, though, since it lets you focus on an event-driven development model.
The form doesn't have any data for constructing the model.
Take a step back and consider the HTTP requests being made. An HTML <form>, when submitted, requests the action URL (as a POST request in this case) and includes whatever form values are present in that form. This likely does not include disabled form elements. (I could be mistaken on that.) And it definitely does not include the HTML of the form.
(You've indicated that you didn't come from a WebForms background, but coincidentally enough this is a very common mistake for people who did.)
With the exception of the buttons, the only form elements I'm seeing are three drop down lists (ApplyGroup, AttributeName, AttributeValue) as well as the radio buttons being emitted in the loop, called RowSelected.
But the model needs more values to be created:
public int RowSelected { get; set; }
// Declare datasets to use as list
public DataSet attList { get; set; }
public DataSet lowList { get; set; }
The model has RowSelected, and you can debug to confirm if that value is being correctly populated. (Your question indicates only that you're trying to debug the presence of the DataSet properties, not the RowSelected property.)
But including all data from two DataSet properties would be much more complex. And, perhaps more to the point, would be entirely unnecessary. Take a step back and consider that you'd be filling the page with data that you don't expect the user to edit and then posting all of that data back to the server that it just came from.
In your action you can get that RowSelected property in a variety of ways. As a method argument, as a model property, or simply as you currently get form values in your other action methods:
string rowSelected = Request["RowSelected"];
You can use int.TryParse() to convert that to an integer value if needed. Though the SQL code you're using is concatenating strings so you may not need to anyway.
(Side note: It's good that you're already aware that SQL injection is a bad thing. Even though in your particular case it's not necessarily a security problem, please be aware that SQL injection is also a very common source of bugs. It's worth learning the correct way of doing things.)
If you can get the fully-populated model from the database using an identifier, that would be ideal. It's certainly easier to just pass around the identifier than it is to pass around an entire complex model, especially if you're not expecting the user to edit that model on that page.
All in all, it looks like you're simply expecting the entire model to be posted to the server when really all you're posting is the identifier for the model. Which, fortunately enough, is all you actually need. Just use that identifier to fetch the intended record.
Related
How to disable #html.dropdownlist option label in .NET?
View Markup : #Html.DropDownListFor(model => model.entityType, ViewBag.entityType as SelectList, "Select Type", new { #class = "custom-select"}) This is the dropdown list in .NET and "select Type" is the option label. My expectation : I want to disable "Select Type" option label. how can I do this ? Thanks in advance who's help me. Regards: PHioNiX
My expectation : I want to disable "Select Type" option label. how can I do this ? Well,it can be implemented using couple of ways. However, the most easiest and efficient way is, we would check SelectList by its class name in your scenario it would be #class = "custom-select" and set the property as we expect here, disable to be more specific. Just like below: $('.custom-select option:contains("Select Type")').attr("disabled", "disabled"); Note: Here, .custom-select is your class name, we are checking for Select Type and finally setting the attribute disabled. We are done. Why its efficient, because looping always cost a lot. We are not using loop here which always has Big 0 impact. Complete Solution: Model: public class EntityTypeModel { public string? entityType { get; set; } } Controller: public IActionResult Create() { List<SelectListItem> entityTypeList = new List<SelectListItem>(); entityTypeList.Add(new SelectListItem { Text = "Entity-Type-C#", Value = "Entity-Type-C#" }); entityTypeList.Add(new SelectListItem { Text = "Entity-Type-SQL", Value = "Entity-Type-SQL" }); entityTypeList.Add(new SelectListItem { Text = "Entity-Type-Asp.net core", Value = "Entity-Type-Asp.net core" }); ViewBag.entityType = entityTypeList; return View(); } View: #model DotNet6MVCWebApp.Models.EntityTypeModel #{ ViewData["Title"] = "Create"; } <h4>Entity Type Dropdown</h4> <hr /> <div class="row"> <div class="col-md-4"> <div class="form-group"> <label asp-for="entityType" class="control-label"></label> #Html.DropDownListFor(model => model.entityType, #ViewBag.entityType as SelectList, "Select Type", new { #class = "custom-select"}) <span asp-validation-for="entityType" class="text-danger"></span> </div> </div> </div> #section scripts { <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script> <script> $(document).ready(function () { $('.custom-select option:contains("Select Type")').attr("disabled", "disabled"); }); </script> } Output: Update: Another thing you can do is: You can set your "Select Type" text from the backend like this way: entityTypeList.Add(new SelectListItem { Text = "Select Type", Value = "Select Type" }); and then you can check if the the user has selected Select Type in that case you can update ModelState with the error message as below: if (EntityTypeModel.entityType.Contains("Select Type")) { ModelState.AddModelError("", "Entity Type should not be Select Type"); }
Text change event for textbox not updating assigned value
I am building a shopping portal where I need to accept quantity of products and pass it to an action named order As you can see in picture above, I added a textbox for accepting quantity for each of the product, then I built an actionlink using following code #Html.ActionLink("Order Now", "OrderNow", "ShoppingCart", new { id = item.prod_id, qty = #quantity }, new { style = "color: white;" }) to get quantity I added new int quantity attribute to view, as int quantity = 1; but how to update this quantity variable when users change the text in qty textbox. Below is my View code: #Html.TextBox("qty","", new { id=#item.prod_name, placeholder="Qty", style="width:20px; height:15px; font-size:small;" }) <script type="text/javascript"> $('##item.prod_name').change(function () { } }); </script> #Html.ActionLink("Order Now", "OrderNow", "ShoppingCart", new { id = item.prod_id, qty = #quantity }, new { style = "color: white;" }) Here is my controller action method public ActionResult OrderNow(int id, int qty) { if (Session["cart"] == null) { List<Item> cart = new List<Item>(); cart.Add(new Item(p.FirstOrDefault(), qty)); Session["cart"] = cart; return RedirectToAction("ViewCart", new { ids = p.FirstOrDefault().prod_sub_cat_id }); } else { List<Item> cart = (List<Item>)Session["cart"]; cart[index].quantity = qty; Session["cart"] = cart; return RedirectToAction("ViewCart", new { ids = p.FirstOrDefault().prod_sub_cat_id }); } }
You don't really want a link to a GET method here. Your modifying data (and would not want this to be added to the browser history) so you should be posting the data. For each product, add a form element with a textbox for the quantity and a submit button for the 'Order Now` action (style it to look like a link if you want) #using (Html.BeginForm("OrderNow", "ShoppingCart", new { id = item.prod_id }) { <input type="text" class="???" name="qty" placeholder="Qty" /> <input type="submit" value="Order Now" class="???" /> } Side notes: Add class names and use css rather than including inline styles such as style="width:20px; height:15px; font-size:small;" You could also use #Html.TextBox("qty", new { id = "", #class="???", placeholder = "Qty"), but note the id = "" which removes the id attribute to prevent invalid html due to duplicates
Posting all selected rows using Grid.Mvc
I have successfully implemended a new Index view to my application will will render all outstanding emails to a gridview which features a checkbox for each row. My intention now is to send an email which will include details of each record selected to a designated recipient. Once a record has been selected and the details mailed for that record, I will flag that row as 'Emailed' and filter all emailed rows out in my controller before passing the list to the view. I am a little confused as to how I make my submit button capture all selected records that have been checked and post the entire page back to my controller where I can do my email related stuff. Here's what I have so far, I don't think I have wired up the submit button correctly to the gridview: #using GridMvc.Html #Html.Grid(Model).Columns(columns => { columns.Add() .Encoded(false) .Sanitized(false) .Titled("Send Email?") .SetWidth(30) .RenderValueAs(o => Html.CheckBox("checked", false)); columns.Add(a => a.Id).Titled("Id").SetWidth(110); columns.Add(a => a.AddressLineOneOld).Titled("Old Address") .RenderValueAs(a => a.AddressLineOneOld + " " + a.AddressLineTwoOld + " " + a.AddressLineThreeOld + " " + a.AddressLineFiveOld); columns.Add(a => a.PostcodeOld).Titled("Old Postcode").Sortable(true); columns.Add(a => a.AddressLineOneNew).Titled("New Address") .RenderValueAs(a => a.AddressLineOneNew + " " + a.AddressLineTwoNew + " " + a.AddressLineThreeNew + " " + a.AddressLineFiveNew); columns.Add(a => a.PostcodeNew).Titled("New Postcode").Sortable(true); columns.Add(a => a.Emailed).Sortable(true); }).WithPaging(20) <br /> <div style="float:right;"> #using (Html.BeginForm("Index", "EmailManager", FormMethod.Post, new { name = "frm", id = "frm" })) { <input id="btnSendEmailForSelected" type="submit" name="submitButton" value="Email Selected" class="btn btn-default" /> } </div> How do I post all selected rows back to the controller?
it this line Html.CheckBox("checked", false) add a unique name add the value for the checkbox to the Id or the unique key you have as below Html.CheckBox("checked", false, new {name = "selectedAddressesIds", value = "UNIQUE_KEY_HERE"}) and in the action method try the following, you must write your action method to have FormCollection var selectedIds = form.GetValues("selectedAddressesIds"); if (selectedIds != null) { foreach (var id in selectedIds) { // do what you want with the selected id. } }
How to perform a simple multiple selection in a table ASP.NET MVC4
Here is what my view looks like: #model Affiliate <div class="box paint color_16"> <div class="title"> <h4><i class="icon-tasks"></i><span>#Model.CompanyName's Commissions</span> </h4> </div> <div class="content top "> <div class="subtitle"> #Html.ActionLink("Void", "DeleteInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" }) #Html.ActionLink("Create Invoice", "CreateInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" }) #Html.ActionLink("Pay", "PayInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" }) </div> <table class="table table-striped table-hover"> <tr> <h3>Commissions</h3> </tr> <tr> <td></td> <td>Amount</td> <td>Status</td> <td>Action</td> </tr> #foreach (var item in Model.Commissions) { <tr> #if (item.Status == ViewBag.PaymentStatus || ViewBag.PaymentStatus == "All") { <td>#Html.CheckBox("commId", new { value = item.Id })</td> <td>#Html.DisplayFor(x => item.PayoutAmount)</td> <td>#Html.DisplayFor(x => item.Status)</td> } </tr> } </table> </div> What I want to be able to do is when I hit an actionlink on the top, grab all the items from the table that are checked, and pass that list of id's to the controller logic. I am assuming a viewmodel may be the solution, something like this: public Affiliate affilite { get; set; } public List<int> selectedItems { get; set; } etc. But how to I get the selected Items into that VM selectedItems container?
Based off your comments, you don't seem to be looking for the most "correct" answer, but rather just a quick and dirty "how would I do this" answer. If you just want to pass the list, you could setup your controller action like this: public ActionResult MyAction(int[] id) { ... } Or, you seem to indicate it is strongly typed to a view model with a property that contains a List (I would shorten the name of the property, you'll see why in a second). In javascript, the easiest thing to do would be to use jQuery to bind a click event on your hyperlink that gets the list of items that are checked and appends that to the query string. $("#myLink").click(function() { var url = "site.com/action?"; var ids = $(".table").find("input:checked"); ids.each(function() { url += "id=" + $(this).val() + "&" }); window.location = url; }); Basically, you want to create one long query string with the action parameter's name repeated over and over, which identifies an array. It looks something like this (id is for int[] id in MyAction): id=15&id=20&id=25&id=30&.... And then once the query string is built, redirect the user to that url. MVC should then be able to bind that to an array and you're all set. That's basically the idea, anyway; the syntax and the javascript I wrote could be way off so don't copy my code and expect it to work as is - I wrote that off the top of my head. If your action is bound to a viewmodel, then you need to set the parameter in the query string to the name of the property of your model: selectedids=1&selectedids=2&selectedids=3... Or, if the array is a property of an object, which is a property of the model... model.selectedids=1&model.selectedids=2&model.selectedids=3... You'll just need to play around with it some.
Use html checks inside form tag ( you could use helpers too) and post the model to a post action. MVC will serialize the model automatically
DropDownList Binding to ActionResult Create Method MVC 4 VS2012
I'm a brand new user here - but I've been searching for a couple of hours now to solve following problem: I've got 2 Entities - Category and Item. Each Item should belong to a Category - therefore I would like to have a DropDownList which shows all existing Categories when Creating a new Item. So far my code shows the DropDownList with all the Categories, but when I select a Category and Submit the form (POST) the value for Category is always null. This naturally causes ModelState.IsValid to be false, because Category isn't nullable. How can I get the User-Selected-Value into my Create(POST) method? I've got a Controller with following Methods to Create a new Item: // GET Method public ActionResult Create() { ViewBag.Category = new SelectList(db.CategorySet, "Id", "CategoryName"); return View(); } [HttpPost] public ActionResult Create(Item item) { if (ModelState.IsValid) { db.ItemSet.Add(item); db.SaveChanges(); return RedirectToAction("Index"); } return View(item); } And this is the DropDownList in my View (Create.cshtml): <div class="editor-field"> #Html.DropDownList("Category", (IEnumerable<SelectListItem>) ViewBag.Categories, "--Select Category--") </div>
Finally I ended up with a custom view model - that way I got it working... For those of you who don't know what a custom view model is: You create a new class which contains all the values you need to create your new object, in my example a class which contains a SelectList (property) of available Categories, an integer value (property) for SelectedCategoryId and the Item (property) you want to create. In your cshtml file you add this class as #model ....CustomCreateItemModel and use it in your DropDownList
If your Item has a CategoryId property: public class Item { public int CategoryId {get;set;] } You will need to name your DropDownList to "CategoryId" so that the ModelBinder will be able to bind the value correctly Or use the strongly typed helper: Html.DropDownListFor(x=>x.CategoryId...)
Thanks Armen. I had the same issue with my dropdown list being populated OK from the database but the OrganisationID (in my case) not making it to the database when a new record was created (in my case only 0 was always captured) - until I just changed the name of the ViewBag to be identical to the value in the dropdown (i.e. both OrganisationID) - as you had helpfully pointed out - and now it works! For what it's worth, for anyone else going through the frustration that "Desperate coder" and I went through when our naming wasn't consistent to enable binding, here's what I have used to get a dropdown list working (sorry - NOT using the Entity Framework, but the principle should still be clear and easy to adapt if you are using the EF): But the key takeaway is identical naming to enable binding. Thanks again Armen! MODEL public class Organisation_Names { public DataSet GetOrg_Names() { SqlConnection cn = new SqlConnection(#"Data Source=XXXXXXXXX;User ID=XXXXXXXXX;Password=XXXXXXXXXXX;Initial Catalog=XXXXXXXXXXXX"); SqlCommand cmd = new SqlCommand("sp_GetOrg_Names", cn); cn.Open(); cmd.CommandType = CommandType.StoredProcedure; cmd.ExecuteNonQuery(); DataSet ds = new DataSet(); SqlDataAdapter da = new SqlDataAdapter(cmd); da.Fill(ds); return ds; } } CONTROLLER // // GET: /Services/Create **public ActionResult Create(Organisation_Names organisation_names) { DataSet ds = organisation_names.GetOrg_Names(); ViewBag.OrganisationID = ds.Tables[0]; List<SelectListItem> items = new List<SelectListItem>(); foreach (System.Data.DataRow dr in ViewBag.OrganisationID.Rows) { items.Add(new SelectListItem { Text = #dr["OrganisationName"].ToString(), Value = #dr["OrganisationID"].ToString() }); } ViewBag.OrganisationID = items; return View(); } // // POST: /Services/Create [HttpPost] [ValidateAntiForgeryToken] **public ActionResult Create(CreateServiceModel createservicemodel, Organisation_Names organisation_names, FormCollection selection) { DataSet ds = organisation_names.GetOrg_Names(); if (ds == null) { return HttpNotFound(); } ViewBag.OrganisationID = ds.Tables[0]; List<SelectListItem> items = new List<SelectListItem>(); foreach (System.Data.DataRow dr in ViewBag.OrganisationID.Rows) { items.Add(new SelectListItem { Text = #dr["OrganisationName"].ToString(), Value = #dr["OrganisationID"] + 1.ToString() }); } ViewBag.OrganisationID = items;** if (this.IsCaptchaVerify("Answer was incorrect. Please try again.")) { try { int _records = createservicemodel.CreateService(createservicemodel.OrganisationID, createservicemodel.ServiceName, createservicemodel.ServiceDescription, createservicemodel.ServiceComments, createservicemodel.ServiceIdentificationNumber, createservicemodel.CreatedBy, createservicemodel.NewServiceID); if (_records > 0) { return RedirectToAction("Index", "Services"); } } catch //else { ModelState.AddModelError("", "Cannot Create"); } } { return View(createservicemodel); } } VIEW #model WS_TKC_MVC4.Models.CreateServiceModel #using CaptchaMvc.HtmlHelpers #using WS_TKC_MVC4.Models #{ViewBag.Title = "Service added by " ;} #User.Identity.Name <script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"> </script> #using (Html.BeginForm()) { #Html.AntiForgeryToken() #Html.ValidationSummary(true) <fieldset> <legend>CreateServiceModel</legend> <div class="editor-label"> <p>Select Organisation</p> </div> <div class="editor-field"> #Html.DropDownList("OrganisationID") #Html.ValidationMessageFor(model => model.OrganisationID) #Html.EditorFor(model => model.OrganisationID) </div> (Some more fields) <div class="editor-label"> #Html.LabelFor(model => model.MathCaptcha) </div> #Html.MathCaptcha("Refresh", "Type answer below", "Answer is a required field.") <p> <input type="submit" value="Create" /> </p> </fieldset> } <div> #Html.ActionLink("Back to List", "Index") </div>