Showing and hiding controls from both controller and view - c#

I am trying to create a best possible solution for this but as this is the first time I am encountering this scenario so I am not sure how to best implement this.
I have a very simple model,
public class Feedback
{
[Required]
[Display(Name = "Current ID")]
public int? PreviousID { get; set; }
[Required]
[Display(Name = "Next ID")]
public int? NextID { get; set; }
[Required]
public int ScenarioID { get; set; }
[Display(Name = "Select you scenario")]
public IEnumerable<SelectListItem> YourScenario { get; set; }
}
When User first loads the view then only dropdownlist for YourScenario and TextBox for PreviousID is displayed. When User select dropdownlist then based on its value the TextBox for NextID is displayed to user. Here is my view,
<div class="form-group">
#Html.LabelFor(m => m.YourScenario, new { #class = "col-sm-3 control-label" })
<div class="col-sm-9">
#Html.DropDownListFor(m => m.ScenarioID, m.YourScenario, "Choose Scenario", new { #class = "form-control chosen-select" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.PreviousID, new { #class = "col-sm-3 control-label" })
<div class="col-sm-9">
#Html.TextBoxFor(m => m.PreviousID)
</div>
</div>
<div class="form-group" style="display:none">
#Html.LabelFor(m => m.NextID, new { #class = "col-sm-3 control-label" })
<div class="col-sm-9">
#Html.TextBoxFor(m => m.NextID)
</div>
</div>
To show/hide the NextID I use the Jquery on the view,
$('#YourScenario').change(function () {
var selectedValue = $(this).val();
var nextID = $('#NextID');
if (selectedValue = "3") {
nextID .show();
}
else {
nextID .hide();
}
});
All this works great. Now to use the same View as Edit Mode I pass the model to the view from controller. What I want is that the TextBox for NextID should be displayed or hidden automatically based on the model values. If I use if condition on the View then the control is not available through Javascript so how can I achieve this?

Here is a simple example: https://jsfiddle.net/jma71jf7/
When you run the code, it will hide the input, because there is no selected value, and this line will trigger the change event function:
$('#YourScenario').trigger('change');
But when editing, the select will have some value, and it will hide/show the input according to the value.

Related

MVC Model data is not binding

I have created form to add customer. I rendered customer page with Viewmodel. View Model class as follows,
public class CustomerViewModel
{
public IEnumerable<MemberShipType> MemberShipTypes { get; set; }
public Customer Customers { get; set; }
}
public class Customer
{
[Display(Name ="Customer ID")]
public int CustomerId { get; set; }
[Required(ErrorMessage = "Please enter customer name")]
[StringLength(255)]
[Display(Name ="Customer Name")]
public string CustomerName { get; set; }
public MemberShipType MemberShipType { get; set; }
[Required(ErrorMessage = "Please select membership type")]
[Display(Name = "Membership Type")]
public byte MembershipTypeId { get; set; }
}
public class MemberShipType
{
[Display(Name ="Membership Id")]
public byte Id { get; set; }
[Required]
[Display(Name = "Subscription Plan")]
public string Name { get; set; }
}
After adding that class, we have created Action to save customer form data using a single model class(Not viewModel)
I have created Customer form using Viewmodel to display with Membership type data.
UI is rendering fine with the below code. But, I am not able to get the model data in the action method.
If I directly use the viewmodel in the action data is coming fine. The problem needs to map all the view model property to a particular model.
It's required more time to map model property each time.
Can any know how to directly use entity framework add method with customer Model(Not View model)
#using (Html.BeginForm("Save", "Customer", FormMethod.Post))
{
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.Customers.CustomerName, htmlAttributes:
new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(m => m.Customers.CustomerName,
new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Customers.CustomerName, "",
new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Customers.MembershipTypeId, htmlAttributes:
new { #class = "control-label col-md-2" })
<div class="col-lg-10">
#Html.DropDownListFor(m => m.Customers.MembershipTypeId,
new SelectList(Model.MemberShipTypes, "Id", "Name"),
"Please Select", new {#class = "form-control"})
#Html.ValidationMessageFor(m => m.Customers.MembershipTypeId,
"",
new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-lg-10 col-lg-offset-2">
<input type="reset" value="Reset" class="btn btn-default" />
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</div>
}
The below action model always return null.
[System.Web.Mvc.HttpPost]
public ActionResult Save(Customer customer)
{
if (customer.CustomerId == 0)
{
_context.Customer.Add(customer);
_context.SaveChanges();
}
}
I am getting a Customer model is null. If I pass customerViewModel data is coming. Can anyone know the answer on how to directly get the data in the model class?
Since you're binding the view to a model of CustomerViewModel and you're using the HTML helpers EditorFor (lambda overload), you should expect that same model in return on your POST. When you use LabelFor and EditorFor, the automatic naming will probably give you something like "Customers_CustomerName" so it can put your view model back together again.
One solution is to change your expected model on your save method to be a 'CustomerViewModel' and just use the .Customer property to get the data.
[System.Web.Mvc.HttpPost]
public ActionResult Save(CustomerViewModel model)
{
if (model.Customer.CustomerId == 0)
{
_context.Customer.Add(model.Customer);
_context.SaveChanges();
}
}
Another option is to name your input fields manually to reflect properties of the 'Customer' model directly and it will map them into a "Customer" model for you on POST. eg Instead of #Html.LabelFor(m => m.Customers.CustomerName you'd just use #Html.EditorFor("CustomerName", Model.Customers.CustomerName)
<div class="form-group">
#Html.LabelFor(m => m.Customers.CustomerName, htmlAttributes:
new { #class = "control-label col-md-2" })
<div class="col-md-10">
*********EDIT HERE --> #Html.TextBox("CustomerName", Model.Customers.CustomerName
new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Customers.CustomerName, "",
new { #class = "text-danger" })
</div>
</div>
I got the solution for this issue. The reason for the problem is, I've created controller and Model in the same name. So, I've changed Model name with different alias in Viewmodel like below,
public class CustomerViewModel
{
public IEnumerable<MemberShipType> MemberShipTypes
{ get; set; }
public ProductionCustomer productionCustomers
{ get; set; }
}
If we use model object in controller to Get/POST it will work even if we rendered the form with ViewModel(Multiple Model). By default mvc will identify the model to post in the form.

How can I retrieve selected item's both Text and Value from DropDownList in MVC

Model:
public class SelectBillingSiteReportModel
{
public IEnumerable<SelectListItem> Sites { get; set; }
public string SelectedSiteName { get; set; }
public string SelectedSiteKey { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime FromDateTime { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime ToDateTime { get; set; }
}
View:
#model MuseReport.Models.SelectBillingSiteReportModel
#{
ViewBag.Title = "Select Site and Date Range";
}
<div class="page-header">
<h4>Select Site and Date Range for Ecg Billing Summary</h4>
</div>
<div class="row">
#using (Html.BeginForm("EcgBilling", "BillingRpt"))
{
<div class ="form-horizontal" role="form">
<div class="form-group">
#Html.Label( "Muse Site", new { #class = "col-md-2 control-label"})
<div class="col-md-10">#Html.DropDownListFor(model => model.SelectedSiteKey, Model.Sites, new { id="museSites", #class = "selectpicker"})</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FromDateTime, "From Date", new { #class = "col-md-2 control-label"})
<div class="col-md-10">#Html.EditorFor(model => model.FromDateTime)</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ToDateTime, "To Date", new { #class = "col-md-2 control-label"})
<div class="col-md-10">#Html.EditorFor(model => model.ToDateTime)</div>
</div>
<div class="form-group">
<div class="col-md-10">#Html.HiddenFor(model => model.SelectedSiteName)</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-primary">Go</button>
</div>
</div>
</div>
}
In a perfect world, I would like to get the whole SelectListItem that the user has selected in the dropdown, rather than just the Value in model.SelectedSiteKey. At worst, I would like to get the Text associated with the Value that was selected into the hidden field (model.SelectedSiteName).
My research suggests that I could use javascript, or I could use an EditorTemplate, neither of which I am clear on how to do.
It seems like this is a lot of work just to trap one extra text string. Am I missing an obvious way to get the text value from the dropdown on the selection?
I just want to save the text value for display with my results without
having to do another call back to the first db.
Based on your comment, you can combine both Text and Value store inside Value. Then split it when the Form is posted back.
For example -
public ActionResult EcgBilling()
{
var model = new SelectBillingSiteReportModel
{
Sites = new List<SelectListItem>
{
new SelectListItem {Text = "One", Value = "One:1"},
new SelectListItem {Text = "Two", Value = "Two:2"},
new SelectListItem {Text = "Three", Value = "Three:3"},
}
};
return View(model);
}
[HttpPost]
public ActionResult EcgBilling(SelectBillingSiteReportModel model)
{
string[] array = model.SelectedSiteKey.Split(':');
string text = array[0];
string value = array[1];
return View();
}
create two fields, 1-value, 2-text,
in the html page
get the selected item's text and assign it to text field
<input id="text_field" name="text_field" type="hidden" />
#Html.DropDownList("value_field", null, " ---Select ---", new { #id = "value", #class = "form-control selectpicker", required = "required", style = "width:180px" })
//jquery
$('#value').change(function () {
var text = $("option:selected", this).text();
$('#text_field').val(text);
});
this way you can save both text and value

dependency validation based on dropdown selecion in MVC4

I have a dropdropdown list have values Yes, No. If you select yes I have validate next two textboxes. How can I do dependency validation based on dropdown selection?
public class MeetingAbstract
{
public string HasMaterialPublishedElseWhereLabel { get; set; }
public string HasMaterialPublishedElseWhereOptions { get; set; }
[Required]
public string HasMaterialPublishedElseWhereText { get; set; }
public string DtPublishedTimeId { get; set; }
public string DtPublishedTimeLabel { get; set; }
//validate this based on HasMaterialPublishedElseWhereText =Yes value
public string DtPublishedTimeText { get; set; }
public string PublishedPlaceId { get; set; }
public string PublishedPlaceLabel { get; set; }
//validate this based on HasMaterialPublishedElseWhereText =Yes value
public string PublishedPlaceText { get; set; }
}
view
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-6">#Html.Label(Model.HasMaterialPublishedElseWhereLabel,
new {#class = "control-label mandatory"})</div>
<div class="col-md-4">
#{
options = Model.HasMaterialPublishedElseWhereOptions;
optionsList = options.Split(',').ToList();
optionSelect = optionsList.Select(option => new SelectListItem()
{Text = option, Value = option}).ToList();
}
#Html.DropDownListFor(model => model.HasMaterialPublishedElseWhereText,
optionSelect, i18n_Models_Abstract.SelectOption, new { #class = "input-
validation-error form-control" })
#Html.ValidationMessageFor(model => model.HasMaterialPublishedElseWhereText,
i18n_Models_Abstract.RequiredField,
new { style = "padding-left: 5px" })
</div>
</div>
</div>
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-6">#Html.Label(Model.DtPublishedTimeLabel, new {#class =
"control-label mandatory"})</div>
<div class="col-md-4">#Html.TextBoxFor(model => model.DtPublishedTimeText, new \
{#class = "form-control", #placeholder = Model.DtPublishedTimeLabel,
required = "required", maxlength = 40})
#Html.ValidationMessageFor(model => model.DtPublishedTimeText,
i18n_Models_Abstract.RequiredField, new { style = "padding-left: 5px" })</div>
</div>
</div>
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-6">#Html.Label(Model.PublishedPlaceLabel, new {#class =
"control-label mandatory"})</div>
<div class="col-md-4">#Html.TextBoxFor(model => model.PublishedPlaceText, new
{#class = "form-control", #placeholder = Model.PublishedPlaceLabel,
required = "required", maxlength = 40})
#Html.ValidationMessageFor(model => model.PublishedPlaceText,
i18n_Models_Abstract.RequiredField, new { style = "padding-left: 5px" })
</div>
</div>
</div>
I would add a Validate method on your model which will be called and exposed via ModelState.IsValid:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
if (this.HasMaterialPublishedElseWhereText == "Yes") {
yield return new ValidationResult("This isn't valid! Let me tell you why...");
}
}
Then in your controller simply call:
if (ModelState.IsValid) {
//You can only get this far if your Validate method
//doesn't return any ValidationResult objects.
//Do your magic!
}
Obviously this won't be executed client-side so if validation on the client is a requirement then you might need to roll that in.
One last note is that if you only have two values (e.g. Yes and No) a radio button may make more sense than a drop down.

Select 1 radiobutton out of three

I haven't worked with radiobuttons previously in MVC project.
And now when since i'm working with it for first time, i seem to face a problem.
What I want to do is, there will be questions, and user can select one answer from the available answers. Each answer will have a radiobutton.
Here is my model
public class CreateAppointmentSelectOptions
{
public Guid AppointmentId { get; set; }
//question1
[Display(Name = "Repeat invitation untill a common date is found")]
public bool RepeatTillCommonDateIsFound { get; set; }
[Display(Name = "Repeat times")]
[Range(1,5,ErrorMessage="Repeat times must be between {1} and {2}")]
public int RepeatTimes { get; set; }
//question 1
[Display(Name="Repeat invitation once")]
public Boolean RepeatOnce { get; set; }
//question 1
[Display(Name="Do not repeat invitation")]
public Boolean NoRepeat { get; set; }
//question 2
[Display(Name = "Cancel the invitation")]
public Boolean CancelInvitation { get; set; }
//question 2
[Display(Name="Plan appointment with my first available date")]
public Boolean FirstAvailableCommon { get; set; }
//question 2
[Display(Name="Plan with all participants available on the first available common date")]
public Boolean OwnerFirstAvailableCommon { get; set; }
}
and the controller
[HttpGet]
public ActionResult Create_WhatIf(Guid appointmentId)
{
var appointmentCondition = new CreateAppointmentSelectOptions
{
AppointmentId = appointmentId,
RepeatOnce = true,
NoRepeat = false,
RepeatTillCommonDateIsFound=false,
CancelInvitation = false,
OwnerFirstAvailableCommon=false,
FirstAvailableCommon = true
};
return View(appointmentCondition);
}
[HttpPost]
public ActionResult Create_WhatIf(CreateAppointmentSelectOptions options)
{
return View();
}
and the view
#model CreateAppointmentSelectOptions
#{
ViewBag.Title = "Create_WhatIf";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>What If?</h2>
#using (Html.BeginForm("Create_WhatIf", "Appointment", FormMethod.Post))
{
#Html.HiddenFor(m=>m.AppointmentId)
<div class="col-md-10">
<h3>What would you like to do if a common appointment with all participants cannot be made after the first invitation?</h3>
<div class='well'>
<div class="form-group">
<div class="input-group">
#Html.RadioButtonFor(m => m.RepeatTillCommonDateIsFound,Model.RepeatTillCommonDateIsFound)
#Html.LabelFor(m => m.RepeatTillCommonDateIsFound)
</div>
<div id="RepeatTimes">
#Html.EditorFor(m=>m.RepeatTimes)
</div>
<div class="input-group">
#Html.RadioButtonFor(m => m.RepeatOnce,Model.RepeatOnce)
#Html.LabelFor(m => m.RepeatOnce)
</div>
<div class="input-group">
#Html.RadioButtonFor(m => m.NoRepeat,Model.NoRepeat)
#Html.LabelFor(m => m.NoRepeat)
</div>
</div>
</div>
</div>
<div class="col-md-10">
<h3>What would you like to do if a common appointment cannot be made in the end?</h3>
<div class='well'>
<div class="form-group">
<div class="input-group">
#Html.RadioButtonFor(m => m.CancelInvitation,Model.CancelInvitation)
#Html.LabelFor(m => m.CancelInvitation)
</div>
<div class="input-group">
#Html.RadioButtonFor(m => m.OwnerFirstAvailableCommon,Model.OwnerFirstAvailableCommon)
#Html.LabelFor(m => m.OwnerFirstAvailableCommon)
</div>
<div class="input-group">
#Html.RadioButtonFor(m => m.FirstAvailableCommon,Model.FirstAvailableCommon)
#Html.LabelFor(m => m.FirstAvailableCommon)
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input class="btn btn-default" value="<<Previous" />
<input type="submit" class="btn btn-default" value="Next>>" />
</div>
</div>
}
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/bootstrap")
#Scripts.Render("~/bundles/jqueryval")
This is view being rendered in browser currently
Question
(Well the editor below the question is something you can ignore.)
Why are all the radio button being selected by default? And what should I do in order to make sure that only 1 radio button can be selected for each question?
Each group of radio buttons should be bound to the same property on the model. Currently, each radio is its own property and therefore in its own group. And they are all checked because the 2 arguments passed to it are the same so a comparison always returns true (for example, m => m.RepeatOnce and Model.RepeatOnce are equal therefore radio is checked).
Instead, you need to add 1 property on your view model the represent each group. For example, question 2...
#Html.RadioButtonFor(m => m.Question2Answer, "CancelInvitation")
#Html.RadioButtonFor(m => m.Question2Answer, "OwnerFirstAvailableCommon")
#Html.RadioButtonFor(m => m.Question2Answer, "FirstAvailableCommon")
The second value is the value to be assigned to Question2Answer if the corresponding value is selected. (Here I am using a string, but you can also use an Enum)
When the form is submitted, you have to use the value in Question2Answer to populate the CancelInvitation, OwnerFirstAvailableCommon, and FirstAvailableCommon properties on the model. Likewise, if you display an existing entity, you have to populate the Question2Answer property on your view model before rendering the view.
--- Update ---
View Model would look something like this. In your controller, you need to populate it from the model...
public class CreateAppointmentSelectOptionsViewModel
{
public Guid AppointmentId { get; set; }
[Display(Name = "Repeat times")]
[Range(1,5,ErrorMessage="Repeat times must be between {1} and {2}")]
public int RepeatTimes { get; set; }
public string Question1Answer { get; set; } // should have a more meaningful name
public string Question2Answer { get; set; }
}
I think you forgot to set the groupName for your radio buttons.
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.radiobutton.groupname(v=vs.110).aspx
Try this
#Html.RadioButtonFor(m => m.CancelInvitation,Model.CancelInvitation,new { Name = "grp" })
#Html.RadioButtonFor(m => m.OwnerFirstAvailableCommon,Model.OwnerFirstAvailableCommon,new { Name = "grp" })
#Html.RadioButtonFor(m => m.FirstAvailableCommon,Model.FirstAvailableCommon,new { Name = "grp" }) &nbsp

How to get dynamic dropdownlist selected value

I am new to MVC and using MVC4 for project.
Here is the piece of code in view -
#model SomeModel
#{
for (var i = 0; i < Model.NumberOfQuestions; i++)
{
<div class="ques">
<div class="leftCol">
<label>#Resources.Enroll.Question</label>
</div>
<div class="rightCol">
#Html.DropDownListFor(m => m.Question, Model.Questions, new { id = "ddlQuestion" + i, #data_native_menu = "false", onchange = "SelectedIndexChanged(id)" })
#Html.ValidationMessageFor(m => m.Question)
</div>
<div class="clear" />
<div id="#("customQuestion" + i)" class="hide">
<div class="leftCol">
<label>#Resources.Enroll.CustomQuestion</label>
</div>
<div class="rightCol">
#Html.TextBoxFor(m => m.CustomQuestion, new { id = "txtCustomQues" + i })
#Html.ValidationMessageFor(m => m.CustomQuestion)
</div>
</div>
<div class="clear" />
<div class="leftCol">
<label>#Resources.Enroll.Answer</label>
</div>
<div class="rightCol">
#Html.TextBoxFor(m => m.Answer, new { id = "txtAnswer" + i })
#Html.ValidationMessageFor(m => m.Answer)
</div>
<div class="clear" />
</div>
}
}
The problem here is I'm not able to get some way of managing selected value for all dropdownlist and passing them to controller.
I am able to get single value retained in Question which is a public property in Model. But I am not getting how to do for all dynamically.
Can you please guide me how to do same?
Create a view model that represents the questions you are going to select i.e.
public class QuestionViewModel
{
public string QuestionId { get; set; }
}
Then add them to your existing model, without seeing your current one it should resemble this:
public class QuestionsViewModel
{
public QuestionsViewModel()
{
SelectedQuestions = new List<QuestionViewModel>();
}
public List<SelectListItem> QuestionsSelectList { get; set; }
public List<QuestionViewModel> SelectedQuestions { get; set; }
}
Use indexing in your for loop with the count of the Questions. The important bit is how the QuestionId is used by the model binder to send it back in the post:
#for(var i = 0; i < Model.SelectedQuestions.Count; i++)
{
#Html.DropDownListFor(m => m.SelectedQuestions[i].QuestionId, Model.QuestionsSelectList as List<SelectListItem>, "Select..", new { #data_native_menu = "false", onchange = "SelectedIndexChanged(id)" })
}
When the form is submitted you will have a collection of selected questions i.e.
var selectedQuestion1 = model.SelectedQuestions[0].QuestionId;

Categories