Data not binding to model from partial view when using TextBoxfor - asp.net-mvc

In my project we have one partial view which is used to book details of a job. I have added new property WorkerID to model which is used in this partial view and main view. In the partial view using html.textboxfor i have added a field for workerid.
Model:
[Display(Name = "Worker Name")]
public Int32? WorkerID { get; set; }
[UIHint("CheckBoxNullable")]
[Display(Name = "Confirm Client")]
public bool ConfirmClient { get; set; }
[UIHint("CheckBoxNullable")]
[Display(Name = "Confirm Worker")]
public bool ConfirmWorker { get; set; }
[UIHint("CheckBoxNullable")]
[Display(Name = "Plan")]
public bool Plan { get; set; }
Partial View:
#model BookingsModel
<style>
div.k-edit-form-container {
width: auto;
}
</style>
<style>
.k-textbox {
width: 100% !important;
height: 34px !important;
}
.form-control {
padding: 0px 6px;
}
</style>
<div class="col-lg-11">
<ul class="errors alert alert-danger" style="display:none;"></ul>
<div class="form-group">
#Html.LabelFor(model => model.ShiftName)<span class="colon">:</span>
<label id="ShiftValue"></label>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StartDate)<span class="colon">:</span>
#Html.EditorFor(model => model.StartDate)
#Html.ValidationMessageFor(model => model.StartDate, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.LabelFor(model => model.EndDate)<span class="colon">:</span>
#Html.EditorFor(model => model.EndDate)
#Html.ValidationMessageFor(model => model.EndDate, "", new { #class = "text-danger" })
</div>
<div class="form-group" id="AllowQuickFill">
#Html.LabelFor(model => model.WorkerID)<span class="colon">:</span>
#Html.EditorFor(model => model.WorkerID, new { #class = "form-control", #placeholder = "Select Worker", #style = "Width:100%;" })
<label>Confirm Client</label><span class="colon">:</span> <input type="checkbox" id="chkConfirmClient" name="ConfirmClient" value="0" />
<label>Confirm Worker</label><span class="colon">:</span> <input type="checkbox" id="chkConfirmWorker" name="ConfirmWorker" value="0" />
<label>Plan</label><span class="colon">:</span> <input type="checkbox" id="chkPlan" name="Plan" value="0" />
</div>
</div>
<script type="text/javascript">
$(document).ready(function () {
initializeCombo("WorkerID", "/Workers/GetDataForCombo?workerStatusTypeID=3");
});
</script>
The InitializeCombo is a method we have created which loads drop down box. Out of 4 new properties which i added in the model only value of workerid is not getting passed to controller. When i run the application WorkerID drop down is get loaded properly, for testing i placed alerts to get value from drop down when user changes or selects any worker and it gave proper notification also.
If i change workerId from TextBoxFor to EditorFor then it works fine, but in that case it is not allowing me to define WorkerID property in model as nullable and this property should be defined as nullable. In my project i have used TextBoxFor in almost all places where i want to load comboboxes in this way and it works fine. But i am not able to find out why it is not working in this instance.
Regards,
Savan Parmar

Related

ASP MVC Validation

I need to perform validation on a textbox and Dropdown which triggers only when both the values are empty and does nothing when one of the value is empty. How would i implement it? Do i need to create a custom validator? Below is my Model and View
Model
public class CustomValidators
{
[Required]
[Required(ErrorMessage = "State Required")]
public string drpStateId { set; get; }
public System.Web.Mvc.SelectList drpState { set; get; }
[Required(ErrorMessage ="Region Required")]
public string txtRegion { set; get; }
}
View
#model InterviewTest.Models.CustomValidators
#{
ViewBag.Title = "Custom Validator";
Layout = "~/Views/_Layout.cshtml";
}
<p>#Html.ActionLink("< Back", "Index")</p>
#using (Html.BeginForm("CustomValidatorPost"))
{
#Html.ValidationSummary()
<div class="container-fluid">
<div class="row">
<div class="col-sm-3">
<div class="form-group">
#Html.DropDownListFor(c => c.drpStateId, Model.drpState, "", new { #class = "form-control" })
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
#Html.TextBoxFor(x => Model.txtRegion, new { #class = "form-control" })
#*<input type="text" id="txtRegion" name="txtRegion" class="form-control" />*#
</div>
</div>
<div class="col-sm-3">
<button type="submit" name="btnSubmit" id="btnSubmit" class="btn btn-default">Submit</button>
</div>
</div>
</div>
}
There is no out of the box validation that works on 2 fields except for the compare validator, so in your case you have to create a custom validation.
You can create a JavaScript function and fire it on onchange on both the two text boxes and within it check the values and if both are empty, show an error message and prevent the form from being submitted, you can achieve that using JQuery validation by adding a custom validator, see this link for more details https://jqueryvalidation.org/jQuery.validator.addMethod/
On Server side, you can do a simple if statement in the controller action to validate that both the values are not empty and if both are empty, then add an error to the ModelState

MVC parent child kind of model form submit doesn't send child collection to controller

I have a company model and it has employees list model as shown below
public class Company
{
[Required]
[Display(Name = "Company Name")]
public string Name { get; set; }
public List<EmployeeModel> Managers { get; set; }
}
and the Employee model as below
public class EmployeeModel
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
and my parent view is as shown below
#using (Html.BeginForm("CompanySignupSuccess", "Home", FormMethod.Post, new { #class = "horizontal-form", role = "form", enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary("", new { #class = "text-danger" })
<div>
<div class="form-group">
#Html.LabelFor(m => m.Name, new { #class = "control-label" })<span class="required">*</span>
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
</div>
<div class="form-group">
<label for="file">Logo:</label>
<input type="file" name="logo" id="logo" accept=".png,.jpg,.jpeg" />
</div>
<div id="managerList">
<div id="editorRowsManagers">
#foreach (var item in Model.Managers)
{
#Html.Partial("DetailsView", item)
}
</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-default pull-right" value="Send" />
</div>
</div>
}
and the partial view shown below
#model yourAssembly.EmployeeModel
<div style="border:1px solid;margin:20px; padding:10px;">
Manager Details:
<div class="form-group">
#Html.LabelFor(m => m.Name, new { #class = "control-label" })
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "control-label" }) <span class="required">*</span>
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Phone, new { #class = "control-label" }) <span class="required">*</span>
#Html.TextBoxFor(m => m.Phone, new { #class = "form-control phoneno" })
</div>
</div>
When I click on submit button, the model that goes to controller does have only Name and Logo properties filled in and the list object(Managers) is null, so I am not sure what is that I am missing here. BTW, I used the list of employees , because I would like add more employees by having a 'Add' button, and the Add button will just render another partial view.
public ActionResult CompanySignupSuccess(Company model)
{
if (ModelState.IsValid)
{
//do some process
}
else
{
ModelState.AddModelError("", "Invalid Data entered.");
}
// If we got this far, something failed, redisplay form
return View("CompanySignup", Model);
}
Can anyone please help me on how to send the child list object along with some properties on parent class when the Submit button is hit.
You cannot use a partial to generate controls for a collection unless you pass the HtmlFieldPrefix (refer this answer for an example). However the correct way to do this is to use an EditorTemplate. Rename your partial to EmployeeModel.cshtml (i.e. to match the name of the class) and move it to the /Views/Shared/EditorTemplates folder (or /Views/YourControllerName/EditorTemplates folder).
Then replace your loop in the view with
#Html.EditorFor(m => m.Managers)
which will correctly generate the necessary name attributes for binding, i.e
<input ... name="Managers[0].Name" />
<input ... name="Managers[1].Name" />
etc (currently all your generating is duplicate name attributes (and duplicate id attributes which is invalid html)

Bootstrap glyphicon with MVC

Withour Razor syntax I can use glyphicon like that:
<div id="addIndirectCityBtn" class="btn btn-success btn-xs">
<span class="glyphicon glyphicon-plus"></span> Add
</div>
And it works perfect, glyphicon with textbox using razor syntax as below
#Html.TextBoxFor(model => model.From.City, new { #id = "cityFrom", #class = "form-control", #placeholder = "City", #style = "margin-bottom: 10px;" })
Where should I use glyphicon to have it in textbox or next to textbox
You can use input group in bootstrap, form-control class will put the object 100% width of box, so you need to group them.
Here is an example
<div class="input-group">
#Html.TextBoxFor(model => model.From.City, new { #id = "cityFrom", #class = "form-control", #placeholder = "City", #style = "margin-bottom: 10px;" }
<span class="input-group-addon"><i class="glyphicon glyphicon-plus"></i> Add</span>
</div>
How To use Glyphicons in Mvc Using Model
for example :
Model
public class Menu
{
public int Id { get; set; }
public string Name { get; set; }
public string Menuicon { get; set; }
}
Controller
[HttpPost]
public ActionResult Index(Menu m)
{
ViewBag.Message = m.Menuicon;
return View(m);
}
Index View
#model projectname.Models.Menu
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
#Html.LabelFor(Model => Model.Name)<br />
#Html.EditorFor(Model => Model.Name)<br />
#Html.LabelFor(Model => Model.Menuicon)<br />
#Html.EditorFor(Model => Model.Menuicon)<br />
<input type="submit" value="Save">
}
<br />
<div>
<span class="#ViewBag.Message"></span>
</div>
Result

Showing RDLC report inside asp.net mvc 5 application

I have created following view form to filter results ,
also I have created following aspx web-form to generate report
Now I want to once I click Generate Report button in view form(1st image) I should be able to pass parameters to text boxes
Type
Category
Subsidary
Country
Date
generate results in web-form and show Microsoft report wizard(RDLC) like 2nd image .
I have done these things separately, I want to link those together
1
Create a model with the filter properties but add a datatable for dataset in report.rdlc for example
public class paramsModel
{
public string typeRep {get; set;}
public DateTime dateRep {get; set;}
public DataTable dataReport {get; set;}
}
2
In your Controller create the ActionResult for the View
public ActionResult showReport()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult showReport(paramsModel paramsRep)
{
if (ModelState.IsValid)
{
paramsRep.dataReport = LocalData.GetDataFromDb(paramsRep.typeRep, paramsRep.dateRep);
}
return View(paramsRep);
}
3
In your View
#model yourproyect.models.paramsModel
#{
ViewBag.Title = "Report page";
var settings = new ControlSettings
{
UseCurrentAppDomainPermissionSet = true,
EnableHyperlinks=true
};
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Report filters</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.typeRep , new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.typeRep , ModelMetadata.FromLambdaExpression(model => model.typeRep , ViewData).EditFormatString, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.typeRep )
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.dateRep , new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.dateRep , ModelMetadata.FromLambdaExpression(model => model.dateRep , ViewData).EditFormatString, new { #class = "form-control datepicker" })
#Html.ValidationMessageFor(model => model.dateRep )
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#if (Model != null)
{
<div >
#Html.MvcReportViewer("Reports/YourReport.rdlc").ProcessingMode(ProcessingMode.Local).LocalDataSource("DataSet1", Model.dataReport).ControlSettings(settings).Attributes(new { Style = "width:100%; border:none; height: 87vh;" }).Method(FormMethod.Post)
</div>
}
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$('.datepicker').datepicker({
dateformat: "#System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern.ToLower()",
language: "es",
autoclose: true,
showAnim: 'blind',
highlightWeek: true
}); //Initialise any date pickers
</script>
}

Field required attribute acidentially invoked but clicking a button

I have these fields, and I implemented required attribute on them.
#using (Html.BeginForm("Edit", "ChannelsGrid", FormMethod.Post, new {name = "channelForm", #class = "channelForm", #enctype = "multipart/form-data"}))
{
<div class="form-group">
#Html.HiddenFor(model => Model.Id)
<div class="row">
<div class="col-md-6">
#Html.Label("Part/Location", new {#class = "control-label"})
#Html.TextBox("PartLocation", null, new { #class = "form-control", #required = "required" })
</div>
<div class="col-md-6">
#Html.Label("Index", new {#class = "control-label"})
#Html.TextBox("Index", null, new {#class = "form-control"})
</div>
</div>
<div class="row">
<div class="col-md-6">
#Html.Label("Measurement", new {#class = "control-label"})
#Html.DropDownListFor(model => model.Measurement, (SelectList)ViewBag.Measurements, "-- Select Measurement --", new { #class = "form-control", #required = "required" })
</div>
<div class="col-md-6">
#Html.Label("Location", new {#class = "control-label"})
#Html.DropDownList("Directions", ViewBag.DirectionTypes as List<SelectListItem>, "-- Select Direction --", new { #class = "form-control", #required = "required" })
</div>
</div>
<div class="row">
<div class="col-md-6">
#Html.LabelFor(model => model.ChannelGroupId, new {#class = "control-label"})
#Html.DropDownListFor(x => x.ChannelGroupId, Model.ChannelGroups, "Select Channel Group", new {#class = "form-control"})
#Html.ValidationMessageFor(model => model.ChannelGroupId)
</div>
<div class="col-md-3">
<label class="control-label"></label>
<a href="#" id="addChannelGroup" class="form-control" style="border: none">
<i class="fa fa-plus-circle">Add Group</i>
</a>
</div>
<div class="col-md-3">
<label class="control-label"></label>
<a href="#" id="addMeasurement" class="form-control" style="border: none">
<i class="fa fa-plus-circle">Add Measurement</i>
</a>
</div>
</div>
<br/>
<div class="row">
<div class="col-md-6">
#Html.Label("Channel name: ", new {id = "channelName", #class = "control-label"})
</div>
<div class="col-md-6">
#Html.TextBox("HiddenTextBox", null, new {#class = "hidden"})
<div class="col-md-1">
#Html.TextBoxFor(a => a.Name, new {#class = "hidden"})
</div>
</div>
</div>
</div>
<div class="row" id="pnlAddChannelGroupName" style="display: none">
<div class="col-md-6">
<label class="control-label">Channel Group Name :</label>
<input type="text" id="ChannelGroupName" name="ChannelGroupName" class="form-control"/>
<input type="button" value="Cancel" id="channelGroupButton" />
#*<button id="channelGroupButton">Cancel</button>*#
</div>
</div>
<div class="row" id="pnlMeasurement" style="display: none">
<div class="col-md-6">
#Html.Label("Measurement :", new {#class = "control-label"})
#Html.TextBox("MeasurementName", null, new {#class = "form-control"})
<input type="button" value="Cancel" id="measurementButton" />
#*<button id="measurementButton">Cancel</button>*#
</div>
</div>
}
I also have two buttons which are used to toggle other textboxes in this form. Here is the code.
<div class="row" id="pnlAddChannelGroupName" style="display: none">
<div class="col-md-6">
<label class="control-label">Channel Group Name :</label>
<input type="text" id="ChannelGroupName" name="ChannelGroupName" class="form-control"/>
<button id="channelGroupButton">Cancel</button>
</div>
</div>
<div class="row" id="pnlMeasurement" style="display: none">
<div class="col-md-6">
#Html.Label("Measurement :", new {#class = "control-label"})
#Html.TextBox("MeasurementName", null, new {#class = "form-control"})
<button id="measurementButton">Cancel</button>
</div>
</div>
The problem is whenever I click these two Cancel buttons in that field, the three fields seems to be invoked and there is brown border around the textbox dropdownlist. I guess these field have been submitted. But I thought I use button element instead of type button of an input so I can eliminate the submitting action of the button, right? Any clues? And how can I click these Cancel buttons withouts invoking validation in these other field?
Edited: I changed all the buttons to input type="button" and the validation of these other field dissapeared. Can someone explain?
This is my viewmodel:
namespace CrashTestScheduler.Entity.ViewModel
{
public class ChannelViewModel
{
public int Id { get; set; }
//[Display(Name = "Name")]
//[Required(ErrorMessage = "Please specify the channel name.")]
public string Name { get; set; }
public string Description { get; set; }
public string ChannelGroupName { get; set; }
public string MeasurementName { get; set; }
[Required(ErrorMessage = "Please select a channel group.")]
public int ChannelGroupId { get; set; }
public IEnumerable<SelectListItem> ChannelGroups { get; set; }
//[Required]
public string Measurement { get; set; }
}
}
The reason your form is submitting when clicking buttons is that the default action for a <button> element is type="submit" (refer documentation). You need to explicitly set the type attribute
<button type="button" ....>
However you have numerous issues with your approach.
By removing the [Required] attributes and using the required =
"required" html attribute, you now need to include manual
validation on the controller (never trust the user!)
Your mixing up Razor and manual html in the view, potentially
creating problems for model binding. Some of your label elements
wont work. (e.g. the first one is associated with a control named
"Part/Location" but there is no control named "Part/Location").
The user interface where your force users to click buttons to swap
between textboxes and dropdown lists is confusing and a sure way to
lose customers. Instead you should use an autocomplete control such
as jQuery Autocomplete which allows selection from a list or
direct text entry.
Your view model should contain validation attributes for its properties and can be simplified to
public class ChannelViewModel
{
public int Id { get; set; }
[Display(Name = "Part/Location")]
[Required]
public string PartLocation { get; set; }
public string Index { get; set; }
[Required]
public string Measurement { get; set; }
[Required]
[Display(Name = "Location")]
public int Direction { get; set; }
.... // other properties
public SelectList DirectionList { get; set; }
}
View
#Html.HiddenFor(model => Model.Id)
#Html.LabelFor(m => m.PartLocation, new {#class = "control-label"})
#Html.TextBoxFor(m => m.PartLocation, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.PartLocation)
#Html.LabelFor(m => m.Index, new {#class = "control-label"})
#Html.TextBoxFor(m => m.Index, new {#class = "form-control"})
#Html.LabelFor(m => m.Measurement, new {#class = "control-label"})
#Html.TextBoxFor(m => m.Measurement, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Measurement)
#Html.LabelFor(m => m.Direction, new {#class = "control-label"})
#Html.DropDownListFor(m => m.Direction, Model.DirectionList, "-- Select Direction --", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Direction)
.... // more controls
The attach the autocomplete to $(#Measurement).autocomplete({...
This will give you client and server side validation out of the box, and a better user interface.

Resources