Conditionally change the [Display(Name = "")] attribute - c#

I have a simple requirement but unable to understand how to achieve this. I have these properties in my View 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; }
}
Now on the view I have,
<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>
On the view as it can be seen that only PreviousID and a DropDownList for Scenario is displayed. Using JQuery I am showing and hiding the div for NextID based on some specific value of DropDownList. So suppose if user select "Scenario 3" from the dropdown then the Input for NextID is displayed.
Having explained all of this what I want is to change the Display name for PreviousID based on the selected value of dropdownlist. So if user selects "Scenario 1" and "Scenario 2" then the Display Name for PreviousID should be "Current ID" and if user selects "Scenario 3" then the Display Name should be "Previous ID". This change should also be reflected in the Validation Summary. So kindly tell me how to achieve this? Many Thanks

I have two ideas:
1)
You could remove the line [Display(Name = "Current ID")]. Then you need to use Label("SomeName") instead of LabelFor.
Then you create an event like DropDownList_SelectionChanged that then changes the Name of the Label.
2)
Or you add a member DisplayName and bind to it:
public class Feedback
{
[Required]
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; }
public String DisplayName {
get
{
String name = "";
if (ScenarioID == 1) {
name = "SomeName";
} else {
name = "otherName";
}
return name;
}
}
}
In your view:
#Html.Label({Binding Content=m,Path=DisplayName}
It's probably not completely correct since I can not test your project but I hope you get the idea. Please tell me what you tried.

I suggest you remove the [DisplayName] attribute, and instead use a <label> element manually in the view. Give the label an id e.g.
<label for="whatever" id="lbl-whatever">Hello World</label>
Create a JavaScript file that the view references, via a <script> tag, which uses jQuery to capture the change event of the dropdown
$(function(){
$("#ScenarioID").on("change", function() {
console.log(this.id);
console.log(this.value);
// I think this.id and this.value should display the id and value of the selected // item in the list.
$("#lbl-whatever").text("Foo");
});
});
Hopefully the above should be enough to get you started.

Related

Filter validation-type in Html.ValidationMessageFor

I got a model like this:
public class SignUpModel
{
[Required, DisplayName(#"Particulars/Salutation")]
public short Salutation { get; set; }
public IEnumerable<SalutationOption> SalutationOptions { get; set; }
[Required, DisplayName("Particulars/FirstName")]
public string FirstName { get; set; }
[Required, Display(Name = "Particulars/LastName")]
public string LastName { get; set; }
[Required, DataType(DataType.EmailAddress), Display(Name = "Particulars/Email")]
public string Email { get; set; }
[Required, DataType(DataType.EmailAddress), Compare("Email"), Display(Name = "Particulars/EmailConfirmation")]
public string EmailConfirmation { get; set; }
}
Now in my view, I want to post validation-errors (also client-side) using #Html.ValidationMessageFor.
<div class="section-field">
<div class="form-group">
#Html.LabelFor(model => model.Email, new { #class = "control-label required" })
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { autocomplete = "off" } })
#Html.ValidationMessageFor(x=>x.Email);
</div>
</div>
<div class="section-field">
<div class="form-group">
#Html.LabelFor(model => model.EmailConfirmation, new { #class = "control-label required" })
#Html.EditorFor(model => model.EmailConfirmation, new { htmlAttributes = new { autocomplete = "off" } })
#Html.ValidationMessageFor(x=>x.EmailConfirmation);
</div>
</div>
But in Html.ValidationMessageFor I only want to show things like "invalid email-adress" or "both emails must match".
I don't want to show the message saying, that the fields are required, since all are required - and they get bordered red.
Is it possible somehow? Tried with implementing a custom HtmlHelper, but that doesn't filter anything client-side.
So I fixed it simply with css now.
I add a class required-validation to invalid Required-validations:
<p class="validation-error required-validation">The field 'FirstName' is required</p>
and added into my custom.css
p.validation-error.required-validation {
display: none;
}
Other ideas on how to handle this with ASP are welcome :)

MVC post action ViewModel is returned as NULL

I am trying to post the form values back to the controller. But in the action, I get the ViewModel as null.
Here is ViewModel
public class CommunicationDetailsViewModel
{
public string OrganizationName { get; set; }
public List<Country> Country { get; set; }
public List<State> State { get; set; }
public List<City> City { get; set; }
[Display(Name = "Id")]
public int CountryId { get; set; }
[Display(Name = "Id")]
public int StateId { get; set; }
[Display(Name = "Id")]
public int CityId { get; set; }
[StringLength(32), Required(ErrorMessage ="Address is required")]
public string Address { get; set; }
[StringLength(32), Required(ErrorMessage = "Building name is required")]
public string BuildingName { get; set; }
}
Below is the controller action:
[HttpPost]
public ActionResult Save(CommunicationDetailsViewModel communicationDetailsViewModel)
{
return View();
}
Does it have to do anything with the Kendo UI for MVC? Because this is the very first time I am using Kendo UI. Below is the view:
#model WebAPI.ViewModels.CommunicationDetailsViewModel
#{
ViewBag.Title = "Supplier Information";
}
<h4>Supplier Details</h4>
#using (Html.BeginForm("Save", "SupplierInformation", FormMethod.Post ))
{
<div class="demo-section k-content">
<div class="form-group">
#Html.Label("Organization name")
#Html.Kendo().TextBoxFor(model => model.OrganizationName).Name("txtOrganization").HtmlAttributes(new { #class = "k-textbox required", placeholder = "Organization Name" })
</div>
<div class="form-group">
#Html.Label("Country")
#(Html.Kendo().DropDownList().Name("ddlCountry").DataTextField("CountryName").DataValueField("Id").BindTo(Model.Country))
</div>
<div class="form-group">
#Html.Label("State")
#(Html.Kendo().DropDownList().Name("ddlState").DataTextField("StateName").DataValueField("Id").BindTo(Model.State))
</div>
<div class="form-group">
#Html.Label("City")
#(Html.Kendo().DropDownList().Name("ddlCity").DataTextField("CityName").DataValueField("Id").BindTo(Model.City))
</div>
<div class="form-group">
#Html.Label("Address")
#Html.Kendo().TextBoxFor(model => model.Address).Name("txtAddress").HtmlAttributes(new { #class="k-textbox required", placeholder="Address", #maxlength = "32" })
</div>
<div class="form-group">
#Html.Label("Building name")
#Html.Kendo().TextBoxFor(model => Model.BuildingName).Name("txtBuildingName").HtmlAttributes(new { #class = "k-textbox required", placeholder = "Address", #maxlength = "32" })
</div>
</div>
#Html.Kendo().Button().Name("btnSave").Content("Save").HtmlAttributes(new { type = "submit", #class = "k-button k-primary" })
}
And interestingly, if I use FormCollection instead of my ViewModel, I am able to get the values in the action.
What am I missing here? Must be something stupid. Any help appreciated.
I think problem here is caused by you change name by Name function. Note that MVC binding properties by name attribute of input tag so don't change it
For example you use
#Html.Kendo().TextBoxFor(model => model.OrganizationName).Name("txtOrganization").HtmlAttributes(new { #class = "k-textbox required", placeholder = "Organization Name" })
You change name of input from OrganizationName to txtOrganization that may cause MVC cann't binding properties exactly. You should keep its original name or ignore change its name like this
#Html.Kendo().TextBoxFor(model => model.OrganizationName).Name("OrganizationName").HtmlAttributes(new { #class = "k-textbox required", placeholder = "Organization Name" })

Possibility to have Range data annotation validation only on form submit?

I have been searching through the stackoverflow website for an answer to my question, but I couldn't find a proper solution. So I hope I can get the solution this way.
Situation:
I have a form with several fields; 2 of them are required: one of them is an EnumDropDownListFor which has an extra 'empty option'.
Problem:
The validation of the dropdownlist triggers on the change event of the dropdown/when the empty option has been selected.
Wanted behavior:
Only validate the dropdownlist when the form is submitted and NOT on the change event.
Here is my code:
public class SearchOrderViewModel
{
[Required]
[Display(Name = "Supplier", ResourceType = typeof(SearchOrderStrings))]
public string Supplier { get; set; }
[Required]
[Range(1, int.MaxValue, ErrorMessageResourceType = typeof(SearchOrderStrings), ErrorMessageResourceName = "OrderTypeRequired")]
[Display(Name = "OrderType", ResourceType = typeof(SearchOrderStrings))]
public OrderTypeEnum OrderType { get; set; }
[Display(Name = "PurchasingReason", ResourceType = typeof(SearchOrderStrings))]
public int PurchasingReason { get; set; }
}
public enum OrderTypeEnum
{
OpenOrder = 1,
ClosedOrder = 2
}
Index.cshtm
<div id="divExternalSupplierSearch" class="form-group">
#Html.LabelFor(model => model.Supplier)
#Html.ValidationMessageFor(model => model.Supplier)
<div id="divDropdown">
#Html.DropDownListFor(model => model.Supplier,
Model.SupplierCodes, new { #class = "form-control" })
</div>
</div>
<div id="divOrderTypes" class="form-group">
#Html.LabelFor(model => model.OrderType)
#Html.ValidationMessageFor(model => model.OrderType)
<div id="divDropdown">
#Html.EnumDropDownListFor(model => model.OrderType, new { #class = "form-control" })
</div>
</div>
<div id="divPurchasingReasons" class="form-group">
#Html.LabelFor(model => model.PurchasingReason)
<div id="divDropdown">
#Html.DropDownListFor(model => model.PurchasingReason, Model.PurchasingReasons, new { #class = "form-control" })
</div>
</div>
If more information is needed, let me know.
Thanks
EDIT (Solution):
By combining #Elmer Dantas answer and trying some things, I found that I could become the wanted behaviour by removing the [Range] data-annotation and making the OrderType property in my SearchOrderViewModel nullable.
So this code is only validating the OrderType value on submit of the form:
[Required]
[Display(Name = "OrderType", ResourceType = typeof(SearchOrderStrings))]
public OrderTypeEnum? OrderType { get; set; }
Apparently the
[Range(1, int.MaxValue, ErrorMessageResourceType = typeof(SearchOrderStrings), ErrorMessageResourceName = "OrderTypeRequired")]
data-annotation was checking if the value was valid on the change event of the dropdownlist. By making the property nullable and just keeping the [Required] data-annotation, I could keep the validation to only be triggered when submitting the form.

Start of with empty View Model MVC

I would like to create a view which is linked to multiple tables. From what I understand I need to create a View Model and link that to the page.
I get a couple of errors using the below
'PaymentViewModel' is a type, which is not valid in the given context.
An expression tree may not contain a dynamic operation (related to first error?)
I am new to MVC - come from ASP....Any help is appreciated
public class PaymentViewModel
{
public string playername { get; set; }
public DateTime dob { get; set; }
public string phone { get; set; }
public string email { get; set; }
public string clubname { get; set; }
public string productname { get; set; }
public decimal amount { get; set; }
public int transactionID { get; set; }
public bool approved { get; set; }
public string subtype { get; set; }
public DateTime subdate { get; set; }
}
Controller
I need to start with a blank view as this is the first step to register a player so the information is not in the database.
Below is the code I use to get a populated View.
public ActionResult Payment()
{
DateTime blank = Convert.ToDateTime("01-01-1900");
var prod = from p in db.Product
join c in db.Club on p.clubname equals c.clubname
where p.clubname == "Club1"
select new PaymentViewModel
{
productname = p.prodname,
clubname = c.clubname,
playername = c.add1,
dob = blank,
phone = c.phone,
email = c.email,
transactionID = 0,
amount = p.amount,
approved = Convert.ToBoolean("1"),
subtype = c.city,
subdate = blank
};
return View(prod);
}
View
#S4C.BAL.PaymentViewModel;
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Player Name</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<b class="control-label col-md-2" style="">Full Name</b>
<div class="col-md-10">
#Html.EditorFor(model => model.playername, new { htmlAttributes = new { autofocus = "autofocus", #maxlength = "25", #class = "form-control" } })
#Html.ValidationMessageFor(model => model.playername, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<br /><br />
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</div>
}
<div>#Html.ActionLink("Back to List", "Index")</div>
#section Scripts {#Scripts.Render("~/bundles/jqueryval")}
You cannot do approved = Convert.ToBoolean("1") in your select because the whole projection will happen at the database side and it does not know what Convert.ToBoolean() is. You need to do this in your view model:
public class PaymentViewModel {
// other properties ...
public string approved { get; set; }
public bool IsApproved {get {return this.approved == "1" }}
}
Also change the first line in your view to this:
#model S4C.BAL.PaymentViewModel
Not sure if I am understanding this correctly so please tell me if I'm wrong here.
Sounds like you know how to get a view filled with data from your database and you want to get an empty view without the data filled. To get an empty view request with just return a view without the model.
// Must request with /{Controller}/PaymentEmpty
Public ActionResult PaymentEmpty()
{
return View("Payment", new PaymentViewModel());
}
If you look at the default templates for ASP MVC applications the controller contains actions for Index, Details, Create, Edit and Delete. Thinking of actions in this manner can help with structuring your requests. Maybe place Payment into its own controller named PaymentsController and having the actions from the controller follow the default template.

Showing and hiding controls from both controller and view

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.

Categories