SelectListItem not setting via dropdown after extending SelectList class - c#

I am trying to extend the SelectListItem class to add another property called CardColor. However when I try to access the property in my controller I get
NullReferenceException: Object reference not set to an instance of an object....
return View("StringView", c.IssueSelected.CardColor);
Controller:
[HttpPost]
public async Task<ActionResult> CardCreate(UpdateCardFormOptions c)
{
return View("StringView", c.IssueSelected.CardColor);
}
View
#using (#Html.BeginForm())
{
<p><label>Issue Category*:</label> #Html.DropDownListFor(c => #Model.IssueSelected, Model.IssueList, new { #class = "form-control", #style = "width: 350px" })</p>
}
Model:
public IssueSelectListItem IssueSelected { get; set; }
public List<IssueSelectListItem> IssueList = new List<IssueSelectListItem>() {
new IssueSelectListItem() {Text="xxx", Value="yyy",CardColor="pink"},
};
public class IssueSelectListItem : SelectListItem
{
public string CardColor { get; set; }
}

This post gave me a clue, turns out I needed to set the value CardColor in the View, I couldnt just set the object equivalent. My View needed to set CardColor based on my dropdown choice like this:
<p><label>Issue Category*:</label> #Html.DropDownListFor(c => #Model.IssueSelected.CardColor, Model.IssueList, new { #class = "form-control", #style = "width: 350px" })</p>
Not going to accept my own answer, still hoping someone has a better one, this just solved my immediate problem

Related

ViewModel Properties in Query String are not bound when arriving at Controller

I'm working in an ASP.net MVC application, and I have a table of products as shown in the screenshot:
I would like the ability to filter that table of products, and I'd like the filtering to happen via the query string params (as a GET) so that the URL can be shared.
The ViewModel for the page is like this:
public class InventoryReportViewModel
{
public SearchViewModel Search { get; set; } // 2 string props [Type and Term]
public IEnumerable<ProductViewModel> Products { get; set; }
public PaginationViewModel Pagination { get; set; } // 3 int props [currentPage, recordsPerPage, totalRecords]
}
I'm using Razor helpers to draw the filter inputs, like this:
#Html.EditorFor(m => m.Search.Term, new { htmlAttributes = new { #class = "form-control" } })
And also I've set up my form to use GET like so:
#using (Html.BeginForm("Inventory", "Report", FormMethod.Get))
{
// form elements
}
My ReportController.cs has the following method that is relevant to my question here:
public ActionResult Inventory(string SearchTerm, string SearchType, int page = 1)
{
var viewModel = _reportService.GetProducts(page, SearchTerm, SearchType);
return View(viewModel);
}
When I pass a Search term, and click the Filter Results button, I do arrive at my Controller method above, but the SearchTerm and SearchType are null.
I know how to "hack" this to work, for example, if I do this:
<input type="text" name="SearchTerm" class="form-control"/>
Then the search term I input would be picked up by the Controller, but is there no other way?
since you already made a viewmodel for Search
public SearchViewModel Search { get; set; }
you just need to pass it to the controller like this
public ActionResult Inventory(SearchViewModel Search, int page = 1
{
var viewModel = _reportService.GetProducts(page, Search.Term, Search.Type);
return View(viewModel);
}
you were getting null because the textboxes were named as Search.Term that is why it was not matching the parameters.
The form should be post
#using (Html.BeginForm("Inventory", "Report", FormMethod.Post))
{
// form elements
}
This can also be cleaner:
#Html.EditorFor(m => m.Search.Term, new { htmlAttributes = new { #class = "form-control" } })
to
#Html.EditorFor(m => m.Search.Term, new { #class = "form-control" } )
Another question,
In the razor view, do you have a model specified on the first line?

return model to view on error with selectlistitem default value

i have some issues with default value of my dropdownlist when returning my model to view in case of one or many errors. I have a dropdownlist in the view which is filled from the controller and others empty dropdownlists in the same view which are filled with JSON on selection of the first dropdownlist.
public ActionResult Countriesdata()
{
CountrydetailsViewModel vm= new CountrydetailsViewModel();
vm.countries= dal.countries().Select(x => new SelectListItem { Text = x.Name, Value = x.CountryID.ToString() })
.ToList();
return View(vm);
}
here, dal is my data access layer and allows me to fill the list of countries from the database. The code use to fill the countries list in the view is like this
#Html.DropDownListFor(m => m.selectedcountry, new SelectList(Model.countries, "Value", "Text", Model.selectedcountry), "-Select a Country-", new { #class = "ddlist" })
one of the empty dropdowlists is as the one below
#Html.DropDownListFor(m => m.selectedtown, new SelectList(Enumerable.Empty<SelectListItem>(), "Value", "Text", Model.selectedtown), "-Select a Town/City-", new { #class = "ddlist" })
This code work very well i reach the page for the first time because i have set a default value for country dropdownlist which is select a country. i use the following code to post my form.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Countriesdata(CountrydetailsViewModel returnmodel)
{
if (! ModelState.IsValid)
{
returnmodel.countries= dal.countries().Select(x => new SelectListItem { Text = x.Name, Value = x.CountryID.ToString() })
.ToList();
return View(returnmodel);
}
return RedirectToAction("mainpage");
}
If the form contains errors, my model is returned back to my view with the posted value of country selected dropdownlist as default, which is not my goal because the others dropdowlists which are filled using JSON on the country dropdownlist selection change are empty.Thus, I ought to select this same country once to fill the others dropdowlists, which is cumbersome. To be logic, i would like to send back my model to my view with default value of the dropdowlist of country when an error occurs. I am using MVC4 and VS 2010
You need to populate both SelectList's in the controller methods so they get passed to the view. In the GET method, the 2nd one will be an empty SelectList (assuming its a 'Create' metod), but in the POST method it will be populated based on the country that has been selected.
You model should include
public class CountrydetailsViewModel
{
[Required(Error Message = "..")]
public int? SelectedCountry { get; set; }
[Required(Error Message = "..")]
public int? SelectedTown { get; set; }
....
public IEnumerable<SelectListItem> CountryList{ get; set; }
public IEnumerable<SelectListItem> TownList { get; set; }
}
And your controller methods
public ActionResult Countriesdata()
{
CountrydetailsViewModel vm = new CountrydetailsViewModel();
ConfigureViewModel(vm);
return View(vm);
}
[HttpPost]
public ActionResult Countriesdata(CountrydetailsViewModel returnmodel)
{
if(!ModelState.IsValid)
{
ConfigureViewModel(returnmodel);
return View(returnmodel);
}
.... // save and redirect
}
private ConfigureViewModel(CountrydetailsViewModel model)
{
var countries = dal.countries();
model.CountryList= countries.Select(x => new SelectListItem
{
Text = x.Name,
Value = x.CountryID.ToString()
});
if (model.SelectedCountry.HasValue)
{
// adjust query to suit your property names
var towns = db.towns.Where(e => e.CountryId == model.SelectedCountry);
model.TownList = towns.Select(x => new SelectListItem
{
Text = x.Name,
Value = x.TownID.ToString()
});
}
else
{
model.TownList = new SelectList(Enumerable.Empty<SelectListItem>());
}
}
This also allows you to generate the correct options and default selections when editing an existing CountrydetailsViewModel.
Then in the view, use
#Html.DropDownListFor(m => m.SelectedCountry, Model.CountryList, "-Select a Country-", new { #class = "ddlist" })
#Html.ValidationMessageFor(m => m.SelectedCountry)
#Html.DropDownListFor(m => m.SelectedTown, Model.TownList, "-Select a Country-", new { #class = "ddlist" })
#Html.ValidationMessageFor(m => m.SelectedTown)
Note that there is no point creating an identical SelectList from the original one you passed to the view by using new SelectList(..) - its just unnecessary extra overhead. Note also that the last parameter in the SelectList constructor is ignored when your binding to a model property (internally the method builds its own SelectList based on the value of the property) - you could put whatever value you wanted as the last parameter and you will see that the option is still correct selected based on the value of the property.

Set selected index of dropdown to zero after form submit in ASP.NET MVC

I am bit to new asp.net mvc and using aps.net mvc 5. I have create the below dropdown using html helpers in aps.net mvc. When i submit(post back) the form i want to set the selected index to zero. Here i am using a optionLabel "--select--". I want to set the selected value to that one ("--select--") after post back. How to achieve this. Please help. Thank you.
#Html.DropDownListFor(model => model.TestCategory, new SelectList(#ViewBag.TestCategories, "value", "text"), "-- Select --", new { #class = "form-control input-sm"})
Controller Code
[HttpGet]
public ActionResult Index()
{
var model = new LaboratoryViewModel {
medicaltestlist = new List<MedicalTest>()
};
PopTestCategory();
PopEmptyDropdown();
return View(model);
}
[HttpPost]
public ActionResult Index(LaboratoryViewModel labvm)
{
var test = PopMedicalTests().Where(x => x.TestSerial == Convert.ToInt32(labvm.TestCode)).FirstOrDefault();
if (labvm.medicaltestlist == null)
labvm.medicaltestlist = new List<MedicalTest>();
if(!labvm.medicaltestlist.Any(x=> x.TestSerial == test.TestSerial))
labvm.medicaltestlist.Add(test);
labvm.TestCategory = "";
PopTestCategory();
return View(labvm);
}
public void PopTestCategory()
{
var categorylist = new List<DropDownItem>
{
new DropDownItem{value="Medical",text="Medical"},
new DropDownItem{value="Animal",text="Animal"},
new DropDownItem{value="Food",text="Food"},
new DropDownItem{value="Water",text="Water"}
};
ViewBag.TestCategories = categorylist;
}
public class DropDownItem
{
public int id { get; set; }
public string value { get; set; }
public string text { get; set; }
}
You return the view in you post method so if you selected (say) Animal then that value will be selected when you return the view because the html helpers use the values from ModelState, not the model property. Setting labvm.TestCategory = ""; has no effect. The correct approach is to follow the PRG pattern and redirect to the GET method, however you can make this work by calling ModelState.Clear(); before setting resetting the value of TestCategory although this will clear all ModelState properties and errors and may have other side effects.
Side note: You DropDownItem class seems unnecessary. MVC already has a SelectListItem class designed to work with dropdownlists, and in any case you can replace all the code in your PopEmptyDropdown() method with
ViewBag.TestCategories = new SelectList(new List<string>() { "Medical", "Animal", "Food", "Water" });
and in the view
#Html.DropDownListFor(m => m.TestCategory, (SelectList)#ViewBag.TestCategories, "-- Select --", new { #class = "form-control input-sm"})
If you set the "value" attribute of the top item in the drop down list to something and then pass back a model containing that for the bound property it should work?

Html.EnumDropdownListFor: Showing a default text

In my view I have a enumdropdownlist (a new feature in Asp.Net MVC 5.1).
#Html.EnumDropDownListFor(m => m.SelectedLicense,new { #class="form-control"})
If I execute the above code I get dropdownlist for my following enum.
public enum LicenseTypes
{
Trial = 0,
Paid = 1
}
but by default I want my dropdownlist to have a value(custom text)
and this is what I tried
#Html.EnumDropDownListFor(m => m.SelectedLicense,"Select a license" ,new { #class="form-control"})
but now the problem is when i run it, my dropdownlist looks like this
So, the default text I want to show doesn't appear by default.
If a user selects "select a license" and tries to submit the form, it does show an error saying "select a license" but it doesn't show as default text.
Something i need to change?
Ps: The image is the screenshot of the page when it loads. By default it'll show Trial as selected option.
Try to change the Index of LicenseTypes start from 1 not 0 like below:
public enum LicenseTypes
{
Trial = 1,
Paid = 2
}
Then you can use Range attribute to validate the selected license type like below:
public class YourViewModel
{
//Other properties
[Range(1,int.MaxValue,ErrorMessage = "Select a correct license")]
public LicenseTypes LicenseTypes { get; set; }
}
Finally, in your view:
#Html.EnumDropDownListFor(m => m.LicenseTypes,"Select a license",new { #class = "form-control"})
#Html.ValidationMessageFor(m => m.LicenseTypes)
By the time your EnumDropDownListFor is rendered SelectedLicense already has the default value for the type, which is 0.
Just change the type of your SelectedLicense property to a nullable enum, like so:
public LicenseTypes? SelectedLicense { get; set; }
This also allows you to continue using the Required attribute, which I think is significantly cleaner. The Required attribute will not allow a null response, so even though your model allows nulls, the form will not.
I have an enum:
public enum Sex
{
Male,
Female
}
In my model I have:
[DisplayName("Sex")]
[Required]
public Sex? Sex { get; set; }
An in the view:
#Html.EnumDropDownListFor(model => model.Sex, "Select sex", new { #class = "form-control", type = "text"})
By this I have a dropdown with default option "Select sex", but validation accepts only options provided by enum ("Male" and "Female").
In MVC3 (without EnumDropDownListFor) I used in model:
[DisplayName("Sex")]
[Required(AllowEmptyStrings=false)]
public Sex? Sex { get; set; }
Sex = null;
Sexes = Repository.GetAutoSelectList<Sex>("");
In view:
#Html.DropDownListFor(model => model.Sex, Model.Sexes, new { #class = "form-control", type = "text" })
The ViewModel class needs to have the default value set on the enum property for it to be the default selected
public
public class Test
{
public Cars MyCars { get; set; }
public enum Cars
{
[Display(Name = #"Car #1")]
Car1 = 1,
[Display(Name = #"Car #2")]
Car2 = 2,
[Display(Name = #"Car #3")]
Car3 = 3
}
}
Controller:
public class EnumController : Controller
{
// GET: Enum
public ActionResult Index()
{
var model = new Test {MyCars = Test.Cars.Car3}; // set default value
return View(model);
}
[HttpPost]
public ActionResult Index(Test model)
{
.....
}
}
View:
#Html.BeginForm()
{
<div class="panel bg-white">
<div class="panel-header fg-white">
Enums
</div>
<div class="panel-content">
<div class="input-control select size3">
#Html.EnumDropDownListFor(model => model.MyCars)
</div>
</div>
<input type="submit" class="button success large" />
</div>
}
Am I a bit late ?
Changing the values of the enum type is not very satisfying.
Neither is changing the model property to render it nullable and then add a [Required] attribute to prevent it to be nullable.
I propose to use the ViewBag to set the default selected value of the dropdown.
The line 4 of the controller just bellow is the only important one.
EDIT : Ah... newbies... My first idea was to use ModelState.SetModelValue because my newbie instinct prevented me to simply try to set the desired value in the ViewBag since the dropdown was binded to the model. I was sure to have a problem: it would bind to the model's property, not to the ViewBag's property. I was all wrong: ViewBag is OK. I corrected the code.
Here is an example.
Model:
namespace WebApplication1.Models {
public enum GoodMusic {
Metal,
HeavyMetal,
PowerMetal,
BlackMetal,
ThashMetal,
DeathMetal // . . .
}
public class Fan {
[Required(ErrorMessage = "Don't be shy!")]
public String Name { get; set; }
[Required(ErrorMessage = "There's enough good music here for you to chose!")]
public GoodMusic FavouriteMusic { get; set; }
}
}
Controller:
namespace WebApplication1.Controllers {
public class FanController : Controller {
public ActionResult Index() {
ViewBag.FavouriteMusic = string.Empty;
//ModelState.SetModelValue( "FavouriteMusic", new ValueProviderResult( string.Empty, string.Empty, System.Globalization.CultureInfo.InvariantCulture ) );
return View( "Index" );
}
[HttpPost, ActionName( "Index" )]
public ActionResult Register( Models.Fan newFan ) {
if( !ModelState.IsValid )
return View( "Index" );
ModelState.Clear();
ViewBag.Message = "OK - You may register another fan";
return Index();
}
}
}
View:
#model WebApplication1.Models.Fan
<h2>Hello, fan</h2>
#using( Html.BeginForm() ) {
<p>#Html.LabelFor( m => m.Name )</p>
<p>#Html.EditorFor( m => m.Name ) #Html.ValidationMessageFor( m => m.Name )</p>
<p>#Html.LabelFor( m => m.FavouriteMusic )</p>
<p>#Html.EnumDropDownListFor( m => m.FavouriteMusic, "Chose your favorite music from here..." ) #Html.ValidationMessageFor( m => m.FavouriteMusic )</p>
<input type="submit" value="Register" />
#ViewBag.Message
}
Without the "ModelState.SetModelValue or ViewBag.FavouriteMusic = string.Empty" line in the model Index action the default selected value would be "Metal" and not "Select your music..."

Html.EditorFor Set Default Value

Rookie question.
I have a parameter being passed to a create view. I need to set a field name with a default value.
#Html.EditorFor(model => model.Id)
I need to set this input field with name Id with a default value that is being passed to the view via an actionlink.
So, how can this input field --#Html.EditorFor(model => model.Id) -- get set with a default value.
Would the following work?? Where the number 5 is a parameter I pass into the text field to set default value.
#Html.EditorFor(c => c.PropertyName, new { text = "5"; })
Here's what I've found:
#Html.TextBoxFor(c => c.Propertyname, new { #Value = "5" })
works with a capital V, not a lower case v (the assumption being value is a keyword used in setters typically) Lower vs upper value
#Html.EditorFor(c => c.Propertyname, new { #Value = "5" })
does not work
Your code ends up looking like this though
<input Value="5" id="Propertyname" name="Propertyname" type="text" value="" />
Value vs. value. Not sure I'd be too fond of that.
Why not just check in the controller action if the proprety has a value or not and if it doesn't just set it there in your view model to your defaulted value and let it bind so as to avoid all this monkey work in the view?
The clean way to do so is to pass a new instance of the created entity through the controller:
//GET
public ActionResult CreateNewMyEntity(string default_value)
{
MyEntity newMyEntity = new MyEntity();
newMyEntity._propertyValue = default_value;
return View(newMyEntity);
}
If you want to pass the default value through ActionLink
#Html.ActionLink("Create New", "CreateNewMyEntity", new { default_value = "5" })
Its not right to set default value in View. The View should perform display work, not more. This action breaks ideology of MVC pattern. So the right place to set defaults - create method of controller class.
Better option is to do this in your view model like
public class MyVM
{
int _propertyValue = 5;//set Default Value here
public int PropertyName{
get
{
return _propertyValue;
}
set
{
_propertyValue = value;
}
}
}
Then in your view
#Html.EditorFor(c => c.PropertyName)
will work the way u want it (if no value default value will be there)
I just did this (Shadi's first answer) and it works a treat:
public ActionResult Create()
{
Article article = new Article();
article.Active = true;
article.DatePublished = DateTime.Now;
ViewData.Model = article;
return View();
}
I could put the default values in my model like a propper MVC addict: (I'm using Entity Framework)
public partial class Article
{
public Article()
{
Active = true;
DatePublished = Datetime.Now;
}
}
public ActionResult Create()
{
Article article = new Article();
ViewData.Model = article;
return View();
}
Can anyone see any downsides to this?
Shouldn't the #Html.EditorFor() make use of the Attributes you put in your model?
[DefaultValue(false)]
public bool TestAccount { get; set; }
Shove it in the ViewBag:
Controller:
ViewBag.ProductId = 1;
View:
#Html.TextBoxFor(c => c.Propertyname, new {#Value = ViewBag.ProductId})
This worked for me
In Controlle
ViewBag.AAA = default_Value ;
In View
#Html.EditorFor(model => model.AAA, new { htmlAttributes = new { #Value = ViewBag.AAA } }
For me I need to set current date and time as default value this solved my issue in View add this code :
<div class="form-group">
#Html.LabelFor(model => model.order_date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.order_date, new { htmlAttributes = new { #class = "form-control",#Value= DateTime.Now } })
#Html.ValidationMessageFor(model => model.order_date, "", new { #class = "text-danger" })
</div>
</div>
This worked for me:
In the controller
*ViewBag.DefaultValue= "Default Value";*
In the View
*#Html.EditorFor(model => model.PropertyName, new { htmlAttributes = new { #class = "form-control", #placeholder = "Enter a Value", #Value = ViewBag.DefaultValue} })*
In the constructor method of your model class set the default value whatever you want.
Then in your first action create an instance of the model and pass it to your view.
public ActionResult VolunteersAdd()
{
VolunteerModel model = new VolunteerModel(); //to set the default values
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult VolunteersAdd(VolunteerModel model)
{
return View(model);
}
This is my working code:
#Html.EditorFor(model => model.PropertyName, new { htmlAttributes = new { #class = "form-control", #Value = "123" } })
my difference with other answers is using Value inside the htmlAttributes array
Instead of using controller or html helper.You can also use Jquery to set default value to model attribute.
$('#PropertyName').val(15);

Categories

Resources