i have the below jquery unobtrusive code which is not firing.
$.validator.unobtrusive.adapters.add('customvalidation', ['productname'], function (options) {
options.rules['customvalidation'] = { productname: options.params.productname };
});
$.validator.addMethod("customvalidation", function (value, element, param) {
alert(param.productname);
return false;
});
but the above code suppose to show alert i guess when pressing button to submit my form.
here is my full code
Model and view model
public class Product
{
public int ID { set; get; }
public string Name { set; get; }
}
public class Hobby
{
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class SampleViewModel
{
[Display(Name = "Products")]
public List<Product> Products { set; get; }
//[AtleastOne(ErrorMessage = "Select at least one checkbox.")]
public List<Hobby> Hobbies { get; set; }
[Required(ErrorMessage = "Select any Product")]
public int SelectedProductId { set; get; }
[Required(ErrorMessage = "Select Male or Female")]
public string Gender { get; set; }
public bool? IsAdult { get; set; }
public int? Age { get; set; }
[ConditionalAttribute(SelectedProductID = "SelectedProductId", Products = "Products", Hobbies = "Hobbies",IsAdult="IsAdult",Age="Age")]
public string ErrorMsg { get; set; }
}
Custom server side validation
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class ConditionalAttribute : ValidationAttribute , IClientValidatable
{
public string SelectedProductID = "", Products = "", Hobbies="";
public string IsAdult = "";
public string Age ="";
string _productname = "";
bool _hashobby = false;
bool _isadult = false;
int _age = 0;
public ConditionalAttribute() { }
public ConditionalAttribute(string SelectedProductId, string Products, string Hobbies, string IsAdult, string Age)
{
this.SelectedProductID = SelectedProductId;
this.Products = Products;
this.Hobbies = Hobbies;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//getting selected product
Product oProduct = null;
ValidationResult validationResult = ValidationResult.Success;
var containerType = validationContext.ObjectInstance.GetType();
var SelectedProductID = containerType.GetProperty(this.SelectedProductID);
Int32 selectedproduct = (Int32)SelectedProductID.GetValue(validationContext.ObjectInstance, null);
var ProductList = containerType.GetProperty(this.Products);
List<Product> oProducts = (List<Product>)ProductList.GetValue(validationContext.ObjectInstance, null);
oProduct = oProducts.Where(e => e.ID == selectedproduct).FirstOrDefault();
_productname = oProduct.Name;
if (_productname != "iPod")
{
var field2 = containerType.GetProperty(this.Hobbies);
List<Hobby> hobbies = (List<Hobby>)field2.GetValue(validationContext.ObjectInstance, null);
foreach (var hobby in hobbies)
{
if (hobby.IsSelected)
{
_hashobby = true;
break;
}
//return ValidationResult.Success;
}
if (!_hashobby)
{
this.ErrorMessage = "Select Any Hobbie's checkbox";
return new ValidationResult(ErrorMessage);
//return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
}
var PropIsAdult = containerType.GetProperty(this.IsAdult);
if (PropIsAdult.GetValue(validationContext.ObjectInstance, null) != null)
{
_isadult = (bool)PropIsAdult.GetValue(validationContext.ObjectInstance, null);
if (_isadult)
{
var PropAge = containerType.GetProperty(this.Age);
if (PropAge.GetValue(validationContext.ObjectInstance, null) != null)
{
_age = (Int32)PropAge.GetValue(validationContext.ObjectInstance, null);
if (_age != null && _age <= 0)
{
this.ErrorMessage = "Age is compulsory for adult";
return new ValidationResult(ErrorMessage);
}
}
else
{
this.ErrorMessage = "Age is compulsory for adult";
return new ValidationResult(ErrorMessage);
}
}
}
return ValidationResult.Success;
}
// Implement IClientValidatable for client side Validation
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "customvalidation",
};
rule.ValidationParameters.Add("productname", _productname);
yield return rule;
}
}
My view code
#model AuthTest.Models.SampleViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("Index", "TestVal", FormMethod.Post, new { name = "TestVal" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DateValTest</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Products, htmlAttributes: new { #class = "control-label col-md-2", style = "padding-top:0px;" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SelectedProductId, new SelectList(Model.Products, "ID", "Name"), "-- Select Product--")
#Html.ValidationMessageFor(model => model.SelectedProductId, "", new { #class = "text-danger" })
#for (int i = 0; i < Model.Products.Count(); i++)
{
<div>
#Html.HiddenFor(model => Model.Products[i].Name)
#Html.HiddenFor(model => Model.Products[i].ID)
</div>
}
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<b>Gender</b><br />
<label>
<span>Male</span> #Html.RadioButtonFor(model => model.Gender, "Male", new { style = "width:20px;" })
<span>Female</span>#Html.RadioButtonFor(model => model.Gender, "Female", new { style = "width:20px;" })
</label>
<label>
</label>
#Html.ValidationMessageFor(model => model.Gender, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10 input-validation-error">
<b>Hobbies</b><br />
#for (int x = 0; x < Model.Hobbies.Count(); x++)
{
#Html.CheckBoxFor(p => p.Hobbies[x].IsSelected, new { #class = "hobbycls", id = "Hobbies" }) #:
#Html.LabelFor(p => p.Hobbies[x].IsSelected, Model.Hobbies[x].Name) #:
#Html.HiddenFor(p => p.Hobbies[x].Name)
}
<span id="Hobbies-error" class="field-validation-error">
<span>Select any hobbies.</span>
</span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<b>Is Adult</b><br />
<label>
<span>Yes</span> #Html.RadioButtonFor(model => model.IsAdult, "true", new { style = "width:20px;" })
<span>No</span>#Html.RadioButtonFor(model => model.IsAdult, "false", new { style = "width:20px;" })
</label>
</div>
<div class="col-md-offset-2 col-md-10">
<label>
Enter Age #Html.TextBoxFor(model => model.Age)
</label>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<label>
#Html.HiddenFor(model => model.ErrorMsg)
#Html.ValidationMessageFor(model => model.ErrorMsg, "", new { #class = "text-danger" })
</label>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
#if (ViewBag.IsPostBack != null && ViewBag.IsPostBack)
{
<text>
<b>Your Selected Product ID :</b> #ViewBag.ProductID<br />
<b>Your Selected Product Name :</b> #ViewBag.ProductName<br />
<b>Gender :</b> #ViewBag.Gender<br />
<b>Hobbies :</b> #ViewBag.Hobbies <br />
<b>Is Adult :</b> #ViewBag.IsAdult <br />
<b>Age :</b> #ViewBag.Age <br />
</text>
}
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
//$.validator.unobtrusive.adapters.add('customvalidation', ['productname', 'hashobby', 'isadult', 'age'], function (options) {
$.validator.unobtrusive.adapters.add('customvalidation', ['productname'], function (options) {
options.rules['customvalidation'] = { productname: options.params.productname };
});
$.validator.addMethod("customvalidation", function (value, element, param) {
alert(param.productname);
return false;
});
</script>
}
Related
I have a category and sub category dropdown list in my view. I select the items in the DDL and submit. In my controller I can only see the categoryID, subCategoryID values for the DDL. I need to get the selected text values (categoryName, subCategoryName). In winforms it is known as the DisplayMember.
I can get the categoryName, subcategoryName values if I run a query based on the returned ID, but this feels like I'm hitting the database more times than I need to.
Here is my code:
ViewModel
public class MedicalCategoryVM
{
public int PersonID { get; set; }
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public int SubCategoryID { get; set; }
public string SubCategoryName { get; set; }
public string Notes { get; set; }
public Nullable<System.Guid> RowGuid { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public virtual Person Person { get; set; }
public List<MedicalCategory> MedicalCategories { get; set; }
}
View:
#model DofE.Models.MedicalCategoryVM
<br />
<br />
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="container">
#Html.Label("Medical Information", new { style = "font-size:16px; background-color:#002142; color:white;margin-left:10px" })
<div class="form-group">
#Html.DropDownListFor(model => model.CategoryID, new SelectList(Model.MedicalCategories,"CategoryID","CategoryName"),"--Select", new { #class = "form-control" })
</div>
<div class="form-group">
#Html.DropDownListFor(model => model.SubCategoryID, new SelectList(""), "--Select Sub Category", new { #class = "form-control" })
</div>
<div class="form-group">
<div class="row">
#Html.LabelFor(model => model.Notes, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Notes, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Notes, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
<div class="row">
#Html.LabelFor(model => model.PersonID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PersonID, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PersonID, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
<div style="padding-left:340px">
<span style="display: inline;">
#Html.ActionLink("Cancel", "Index", null, new { #class = "btn btn-danger btn-sm" }) <input type="submit" value="Create" class="btn btn-primary btn-sm" />
</span>
</div>
#*<div class="test">
<div style="float: right;">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</div>*#
</div>
</div>
}
<script src="~/Scripts/jquery-3.4.1.min.js"></script>
<script>
$(document).ready(function () {
$("#CategoryID").change(function () {
$.get("/MedicalHistory/GetSubCategoryList", { CategoryID: $("#CategoryID").val() }, function (data) {
$("#SubCategoryID").empty();
$.each(data, function (index, row) {
$("#SubCategoryID").append("<option value='" + row.SubCategoryID + "'>" + row.SubCategoryName + "</option>")
});
});
})
});
</script>
Controller:
public ActionResult Create()
{
MedicalCategoryVM CategoryVM = new MedicalCategoryVM();
CategoryVM.MedicalCategories = db.MedicalCategories.ToList();
//ViewBag.CategoryList = new SelectList(CategoryList, "CategoryID", "CategoryName");
return View(CategoryVM);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "PersonID,CategoryName,SubCategoryName,Notes,RowGuid,ModifiedDate")] MedicalHistory medicalHistory, FormCollection form)
{
//string catName = form["CategoryName"].ToString();
//string subCatName = form["SubCategoryName"].ToString();
int catID = Convert.ToInt16(form["CategoryID"]);
int subCatID = Convert.ToInt16(form["SubCategoryID"]);
MedicalSubCategory cat = db.MedicalSubCategories.Find(subCatID);
medicalHistory.CategoryName = cat.CategoryName;
medicalHistory.SubCategoryName=cat.SubCategoryName;
medicalHistory.RowGuid = Guid.NewGuid();
medicalHistory.ModifiedDate = DateTime.Now;
if (ModelState.IsValid)
{
db.MedicalHistories.Add(medicalHistory);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(medicalHistory);
}
Not used to ask questions here but there is a first time for everything.
I'm currently working on a web project in ASP.NET MVC and I am stuck at the very first form...
What I'm trying to do is to display a group of checkboxes and require at least one of them to be checked. Fortunately, I already found several answers on the matter and it works... only on the server side.
But I can't figure out why it is not working on the client side! Although all my other fields custom rules apply well on client side!
I'm still rather new to this tech so maybe I am missing something here...
I've been on it for days now. I hope someone here will be kind enough to help me.
Below is my code around the checkboxes :
View
<div class="col-sm-3">
<label>Documents:<br/>(select at least one)*</label><br/>
#Html.ValidationMessageFor(m => m.AllDocumentsChecked, "", new #class = "text-danger" })
</div>
<div class="col-sm-3">
<div class="form-check">
#Html.CheckBoxFor(m => m.AllDocumentsChecked, new { #class = "form-check-input", #id = "AllDocumentsChecked", #name = "outputFiles" })
<label for="select_all" class="form-check-label">All Documents</label>
</div>
<div class="form-check">
#Html.CheckBoxFor(m => m.Doc1Checked, new { #class = "form-check-input check", #id = "Doc1Checked", #name = "outputFiles" })
<label for="file_doc1" class="form-check-label">Doc1</label>
</div>
<div class="form-check">
#Html.CheckBoxFor(m => m.Doc2Checked, new { #class = "form-check-input check", #id = "Doc2Checked", #name = "outputFiles" })
<label for="file_doc2" class="form-check-label">Doc2</label>
</div>
</div>
<div class="col-sm-3">
<div class="form-check">
#Html.CheckBoxFor(m => m.Doc3Checked, new { #class = "form-check-input check", #id = "Doc3Checked", #name = "outputFiles" })
<label for="file_doc3" class="form-check-label">Doc3</label>
</div>
<div class="form-check">
#Html.CheckBoxFor(m => m.Doc4Checked, new { #class = "form-check-input check", #id = "Doc4Checked", #name = "outputFiles" })
<label for="file_doc4" class="form-check-label">Doc4</label>
</div>
<div class="form-check">
#Html.CheckBoxFor(m => m.Doc5Checked, new { #class = "form-check-input check", #id = "Doc5Checked", #name = "outputFiles" })
<label for="file_doc5" class="form-check-label">Doc5</label>
</div>
</div>
<div class="col-sm-3">
<div class="form-check">
#Html.CheckBoxFor(m => m.Doc6Checked, new { #class = "form-check-input check", #id = "Doc6Checked", #name = "outputFiles" })
<label for="file_doc6" class="form-check-label">Doc6</label>
</div>
</div>
Model
[Display(Name = "All Documents")]
[RequireAtLeastOneOfGroup("Documents")]
public bool AllDocumentsChecked { get; set; }
[Display(Name = "Doc1")]
[RequireAtLeastOneOfGroup("Documents")]
public bool Doc1Checked { get; set; }
[Display(Name = "Doc2")]
[RequireAtLeastOneOfGroup("Documents")]
public bool Doc2Checked { get; set; }
[Display(Name = "Doc3")]
[RequireAtLeastOneOfGroup("Documents")]
public bool Doc3Checked { get; set; }
[Display(Name = "Doc4")]
[RequireAtLeastOneOfGroup("Documents")]
public bool Doc4Checked { get; set; }
[Display(Name = "Doc5")]
[RequireAtLeastOneOfGroup("Documents")]
public bool Doc5Checked { get; set; }
[Display(Name = "Doc6")]
[RequireAtLeastOneOfGroup("Documents")]
public bool Doc6Checked { get; set; }
RequireAtLeastOneFromGroupAttribute class
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class RequireAtLeastOneOfGroupAttribute : ValidationAttribute, IClientValidatable
{
public string GroupName { get; private set; }
public RequireAtLeastOneOfGroupAttribute(string groupName)
{
ErrorMessage = string.Format("You must select at least one value from this group", groupName);
GroupName = groupName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
foreach (var property in GetGroupProperties(validationContext.ObjectType))
{
var propertyValue = (bool)property.GetValue(validationContext.ObjectInstance, null);
if (propertyValue)
{
// at least one property is true in this group => the model is valid
return ValidationResult.Success;
}
}
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
private IEnumerable<PropertyInfo> GetGroupProperties(Type type)
{
return
from property in type.GetProperties()
where property.PropertyType == typeof(bool)
let attributes = property.GetCustomAttributes(typeof(RequireAtLeastOneOfGroupAttribute), false).OfType<RequireAtLeastOneOfGroupAttribute>()
where attributes.Count() > 0
from attribute in attributes
where attribute.GroupName == GroupName
select property;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var groupProperties = GetGroupProperties(metadata.ContainerType).Select(p => p.Name);
var rule = new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessage
};
rule.ValidationType = string.Format("group", GroupName.ToLower());
rule.ValidationParameters.Add("group", string.Join(",", groupProperties));
//rule.ValidationParameters["propertynames"] = string.Join(",", groupProperties);
yield return rule;
}
}
require_at_least_one_from_group javascript
$.validator.unobtrusive.adapters.add('atleastone', ['propertynames'],
function (options) {
options.rules['group'] = { propertynames: options.params.propertynames.split(',') };
options.messages['group'] = options.message;
});
$.validator.addMethod('group',
function (value, element, params) {
var properties = params.properties.split(',');
var isValid = false;
for (var i = 0; i < properties.length; i++) {
var property = properties[i];
if ($('#' + property).is(':checked')) {
isValid = true;
break;
}
}
return isValid;
}, '');
EDIT
Example of html tagging for a checkbox
Below the html resulting from the helpers above. Still can't see what is wrong.
<input class="form-check-input check" data-val="true" data-val-atleastone="You must select at least one value from this group"
data-val-atleastone-group="AllDocumentsChecked,Doc1Checked,Doc2Checked,
Doc3Checked,Doc4Checked,Doc5Checked,Doc6Checked" data-val-required="The Doc1 field is required."
id="Doc1Checked" name="Doc1Checked" value="true" type="checkbox">
<input name="Doc1Checked" value="false" type="hidden">
Thanks by advance!
I created a 2-dependent cascading dropdown list:
Model Classes
COUNTRIES
public int COUNTRY_ID { get; set; }
public string COUNTRY_NAME { get; set; }
STATES
public int STATE_ID { get; set; }
public Nullable<int> COUNTRY_ID { internal get; set; }
public string STATE_NAME { get; set; }
CITIES
public int CITY_ID { get; set; }
public Nullable<int> STATE_ID { internal get; set; }
public Nullable<int> COUNTRY_ID { internal get; set; }
public string CITY_NAME { get; set; }
The drop-down list worked as expected. But when clicked on save button, nothing is saved.
Controller: CITIES
// Json Call to get state
public JsonResult GetStates(string id)
{
List<SelectListItem> states = new List<SelectListItem>();
var stateList = this.Getstate(Convert.ToInt32(id));
var stateData = stateList.Select(m => new SelectListItem()
{
Text = m.STATE_NAME,
Value = m.STATE_ID.ToString(),
});
return Json(stateData, JsonRequestBehavior.AllowGet);
}
// Get State from DB by country ID
public IList<STATES> Getstate(int CountryId)
{
return _statesService.GetStates().Where(stat => stat.COUNTRY_ID == CountryId).ToList();
}
//
public ActionResult Create()
{
ViewBag.COUNTRIES = new SelectList(_countriesService.GetCountries(), "COUNTRY_ID", "COUNTRY_NAME");
ViewBag.STATES = new SelectList(_statesService.GetStates(), "STATE_ID", "state_NAME");
return View();
}
[HttpPost]
public ActionResult Create(CITIES cities)
{
try
{
IEnumerable<COUNTRIES> lstCountries = _countriesService.GetCountries();
if (ModelState.IsValid)
{
cities.ACTION_STATUS = 0;
cities.CREATED_DATE = DateTime.Now;
_citiesService.AddCity(cities);
return RedirectToAction("Index");
}
}
catch
{
ModelState.AddModelError("", "We cannot add this Cities. Verify your data entries !");
}
ViewBag.COUNTRIES = new SelectList(_countriesService.GetCountries(), "COUNTRY_ID", "COUNTRY_NAME", cities.COUNTRY_ID);
ViewBag.STATES = new SelectList(_statesService.GetStates(), "STATE_ID", "STATE_NAME", cities.STATE_ID);
return View(cities);
}
View : Cities
<div class=" box box-body box-primary">
#*#using (Html.BeginForm())*#
#using (Html.BeginForm("Create", "Cities", FormMethod.Post, new { #class = "form-horizontal", #enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, null, new { #class = "text-danger" })
<div class="row .col">
<div style="margin-top:20px" class="mainbox col-md-12 col-md-offset-0 col-sm-8 col-sm-offset-2">
<div class="panel panel-info">
<div class="panel-heading">
<div class="panel-title">Create City</div>
</div>
<div class="panel-body">
<div class="col-md-4">
<div>
#Html.LabelFor(model => model.COUNTRY_ID, "Country Name", new { #class = "control-label" })
#Html.DropDownList("COUNTRIES", ViewBag.COUNTRIES as SelectList, "-- Please Select a Country --", new { style = "width:250px" })
#Html.ValidationMessageFor(model => model.COUNTRY_ID, null, new { #class = "text-danger" })
</div>
</div>
<div class="col-md-4">
<div>
#Html.LabelFor(model => model.STATE_ID, "State Name", new { #class = "control-label" })
#Html.DropDownList("STATES", new SelectList(string.Empty, "Value", "Text"), "-- Please select a State --", new { style = "width:250px", #class = "dropdown1" })
#Html.ValidationMessageFor(model => model.STATE_ID, null, new { #class = "text-danger" })
</div>
</div>
<div class="col-md-4">
<div>
#Html.LabelFor(model => model.CITY_NAME, "City Name", new { #class = "control-label" })
#Html.TextBoxFor(model => model.CITY_NAME, new { #style = "border-radius:3px;", #type = "text", #class = "form-control", #placeholder = Html.DisplayNameFor(m => m.CITY_NAME), #autocomplete = "on" })
#Html.ValidationMessageFor(model => model.CITY_NAME, null, new { #class = "text-danger" })
</div>
</div>
</div>
<div class="panel-footer">
<div class="panel-title">
<div class="form-actions no-color">
<input type="submit" value="Create" class="btn btn-success" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$(document).ready(function () {
//Country Dropdown Selectedchange event
$("#COUNTRIES").change(function () {
$("#STATES").empty();
$.ajax({
type: 'POST',
url: '#Url.Action("GetStates")', // Calling json method
dataType: 'json',
data: { id: $("#COUNTRIES").val() },
// Get Selected Country ID.
success: function (states) {
$.each(states, function (i, state) {
$("#STATES").append('<option value="' + state.Value + '">' +
state.Text + '</option>');
});
},
error: function (ex) {
alert('Failed to retrieve states.' + ex);
}
});
return false;
})
});
</script>
When I clicked on Countries Drop-down list, it populates and the respective states are loaded as expected. When I clicked on Save button, nothing is being saved. Please what is the problem
I am working on an ASP.NET MVC web application that uses Entity Framework.
Here is the model class:
public partial class Complaint
{
public int ComplaintId { get; set; }
public string Reference { get; set; }
public string ClientName { get; set; }
public string AccountNo { get; set; }
public Nullable<System.DateTime> DateComplaint { get; set; }
public Nullable<decimal> Value { get; set; }
public Nullable<int> TypeId { get; set; }
public string Comment { get; set; }
public Nullable<int> StatusId { get; set; }
public Nullable<System.Guid> InsertedBy { get; set; }
public Nullable<System.DateTime> CreatedDateTime { get; set; }
public Nullable<System.DateTime> ModifiedDateTime { get; set; }
public virtual ICollection<FileAttach> FileAttaches { get; set; }
}
Controller method:
[Authorize]
[AuthorizeRoles("Admin", "Oficial")]
public ActionResult RequestApproval(int? id)
{
ViewBag.Status = _context.Status.ToList();
//ViewBag.User = _context.Profiles.Where(x => x.User.UserRoles.FirstOrDefault().RoleId == 2 || x.User.UserRoles.FirstOrDefault().RoleId == 3).ToList();
if (id == 0)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Complaint c = _context.Complaints.Include(s => s.FileAttaches).SingleOrDefault(x => x.ComplaintId == id);
if (c == null)
{
return HttpNotFound();
}
return View(c);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult RequestApproval(int? id, HttpPostedFileBase upload)
{
var c = _context.Complaints.Find(id);
if (TryUpdateModel(c, "", new string[] { "Reference", "ClientName", "AccountNo", "DateComplaint", "Value", "TypeId", "Comment", "StatusId", "IsApproved", "ApprovedBy", "InsertedBy", "UpdatedBy", "CreatedDateTime", "ModifiedDateTime" }))
{
for (int i = 0; i < Request.Files.Count; i++)
{
upload = Request.Files[i];
if (upload != null && upload.ContentLength > 0)
{
Stream str = upload.InputStream;
BinaryReader Br = new BinaryReader(str);
Byte[] FileDet = Br.ReadBytes((Int32)str.Length);
var fileName = Path.GetFileName(upload.FileName);
FileAttach fileDetail = new FileAttach()
{
Name = fileName,
ContentType = upload.ContentType,
Data = FileDet,
AttachId = new int(),
Path = Path.Combine(Server.MapPath("~/Uploads/"), fileName),
ComplaintId = id
};
var path = Path.Combine(Server.MapPath("~/Uploads/"), fileDetail.Name);
upload.SaveAs(path);
_context.Entry(fileDetail).State = EntityState.Added;
}
}
_context.Entry(c).State = EntityState.Modified;
_context.SaveChanges();
return RedirectToAction("List");
}
return View(c);
}
My view:
<div data-parsley-validate class="form-horizontal form-label-left">
#using (Html.BeginForm("RequestApproval", "Complaint", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ComplaintId, new { #class = "id" })
<div class="form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="first-name">
#Html.LabelFor(model => model.Reference) <span class="required">*</span>
</label>
<div class="col-md-6 col-sm-6 col-xs-12">
#Html.DropDownListFor(model => model.StatusId, new SelectList(ViewBag.Status, "StatusId", "Denomination"), "- Please select a type -",
new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="first-name">
#Html.LabelFor(model => model.Comment) <span class="required">*</span>
</label>
<div class="col-md-6 col-sm-6 col-xs-12">
#Html.TextAreaFor(model => model.Comment, new { #class = "form-control col-md-7 col-xs-12", style = "width:100% auto; height:150px;" })
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="first-name">
Files
</label>
<div class="col-md-6 col-sm-6 col-xs-12">
<input type="file" name="file" id="fileUpload" multiple="multiple" />
<ul class="attachment">
#foreach (var item in Model.FileAttaches)
{
<li>
<a class="title" href="/Complaint/Download/?p=#(item.Name)&d=#item.Name">#item.Name</a>
</li>
}
</ul>
</div>
</div>
<div class="ln_solid"></div>
<div class="form-group">
<div class="col-md-6 col-sm-6 col-xs-12 col-md-offset-3">
<button type="submit" class="btn btn-primary btn-sm">Back To List</button>
<button type="submit" class="btn btn-success btn-sm">Save <i class="fa fa-save"></i></button>
</div>
</div>
}
</div>
When I try to edit I get this error:
Server Error in '/' Application
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: model
enter image description here
I'm pretty new to programming and I'm strugling with quite simple "Create" controller and view.
In the view user is declaring values, which should be passed to database. Here's the model:
public class Expense
{
public int ExpenseID { get; set; }
[DisplayName("Data")]
[Column(TypeName = "DateTime2")]
public DateTime Date { get; set; }
[DisplayName("Wartość")]
public decimal Amount { get; set; }
[DisplayName("Opis")]
public string Details { get; set; }
[DisplayName("Rodzaj")]
public int CategoryID { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
public string Name { get; set; }
public virtual ICollection<Expense> Expenses { get; set; }
}
In the same view I want to include partial view for managing the categories (adding, removing).
To have this working I've implemented ViewModel:
public class ExpenseCreateViewModel
{
public Expense ExpenseCreate { get; set; }
public Category CategoryCreate { get; set; }
}
And here's the code for my View:
#model Wydatki2._0.Models.ExpenseCreateViewModel
#{
ViewBag.Title = "Create";
}
<h2>Dodaj wydatek</h2>
<table class="table">
<tr>
<th>
<div>
#using (Html.BeginForm("CreateExpense", "ExpenseCreate", FormMethod.Post, new { }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.ExpenseCreate.CategoryID, "Rodzaj", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CategoryID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.ExpenseCreate.CategoryID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ExpenseCreate.Amount, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ExpenseCreate.Amount, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ExpenseCreate.Amount, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ExpenseCreate.Date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ExpenseCreate.Date, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ExpenseCreate.Date, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ExpenseCreate.Details, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ExpenseCreate.Details, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ExpenseCreate.Details, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Dodaj" class="btn btn-default" />
</div>
</div>
</div>
}
</div>
</th>
<th>
<div>
<input type="button" id="btn" class="btn btn-default" value="Dodaj/Usuń Kategorię" />
<p class="error">#ViewBag.Warning</p>
</div>
<div id="Create" style="display:none">
#Html.Partial("CreateCategory", Model.CategoryCreate)
</div>
</th>
</tr>
</table>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/toggle")
}
The problem is that when I submit the form only the CategoryID value is passed correctly. The rest of parameters have just default values. The partial view is working well - I'm able to add or remove categories, which then are passed to the main view.
The code for my Controller:
namespace Wydatki2._0.Controllers
{
public class ExpenseCreateController : Controller
{
private WydatkiContext db = new WydatkiContext();
public ActionResult Create(bool? warn = false)
{
ExpenseCreateViewModel model = new ExpenseCreateViewModel()
{
ExpenseCreate = new Expense(),
CategoryCreate = new Category()
};
var query = from b in db.Categories
where b.CategoryID != 1
select b;
if (warn.GetValueOrDefault())
{
ViewBag.Warning = "Nie możesz usunąć tej kategorii.";
}
ViewBag.CategoryID = new SelectList(query, "CategoryID", "Name");
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateExpense([Bind(Include = "ExpenseID,Date,Amount,Details,CategoryID")] Expense expense)
{
if (ModelState.IsValid)
{
db.Expenses.Add(expense);
db.SaveChanges();
return RedirectToAction("Create");
}
var query = from b in db.Categories
where b.CategoryID != 1
select b;
ViewBag.CategoryID = new SelectList(query, "CategoryID", "Name", expense.CategoryID);
return View(expense);
}
public ActionResult CreateCategory()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateCategory([Bind(Include = "CategoryID,Name")] Category category)
{
if (ModelState.IsValid)
{
Category cat = db.Categories.FirstOrDefault(c => c.Name== category.Name);
if (cat != null)
{
if (cat.CategoryID == 1 || cat.CategoryID ==2)
{
return RedirectToAction("Create", new { warn = true });
}
else
{
db.Categories.Remove(cat);
db.SaveChanges();
return RedirectToAction("Create");
}
}
else
{
db.Categories.Add(category);
db.SaveChanges();
return RedirectToAction("Create");
}
}
return View(category);
}
I'm inclined to believe that the problem is caused by the model, which I'm passing to the view, but I really don't know, how to pass it correctly... Anyone could help with this?
You should use
([Bind(Prefix="prefixhere")]Category category)
I assume your prefix should be CategoryCreate for category, which the extension from your model ExpenseCreateViewModel, same for your CreateExpense, your prefix should be ExpenseCreate, this way you tell your controller to expect your input names after this prefix, so it will look for CategoryID and Name in create category action after the prefix you passed.
Something Like this
([Bind(Prefix="CategoryCreate")]Category category)
([Bind(Prefix="ExpenseCreate")]Expense expense)