DropDownListFor null value, reference used - c#

So I've looked around StackOverflow a lot and found a nice solution to my problem.
MVC3 DropDownListFor - a simple example?, this ought to do it for me. BUT, it returned a null value... Somehow I have no idea how to go around this so assistance will be appreciated.
Model AccountModel.cs
...
public string Address { get; set; }
public string City { get; set; }
public class StateList
{
public int StateID { get; set; }
public string Value { get; set; }
}
public IEnumerable<StateList> StateListOptions = new List<StateList>
{
//new StateList { StateID = -1, Value = "Select State" },
new StateList { StateID = 0, Value = "NY" },
new StateList { StateID = 1, Value = "PO" }
};
public string State { get; set; }
public string Zip { get; set; }
...
Register.cshtml
#Html.DropDownListFor(m => m.State, new SelectList(Model.StateListOptions, "StateID", "Value", Model.StateListOptions.First().StateID))
I thought maybe my StateID = -1 made it output a null for some reason... but it didn't, you can see it commented out here. What did I do wrong?!
Get Action
public ActionResult Register()
{
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View();
}

Create an object of your Model/ ViewModel and send that to view.
public ActionResult Register()
{
AccountModel vm=new AccountModel();
//Not sure Why you use ViewData here.Better make it as a property
// of your AccountModel class and pass it.
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View(vm);
}
Now your View should be strongly typed to this Model
So in your Register.cshtml view,
#model AccountModel
#using(Html.BeginForm())
{
//Other form elements also
#Html.DropDownListFor(m => m.State, new SelectList(Model.StateListOptions,
"StateID", "Value")"Select")
<input type="submit" />
}
To Get the Selected State in POST, You can check the State Property value.
[HttpPost]
public ActionResult Register(AccountModel model)
{
if(ModelState.IsValid)
{
// Check for Model.State property value here for selected state
// Save and Redirect (PRG Pattern)
}
return View(model);
}

Related

BeginForm dont sent model to Controller method POST why?

I have problem i dont now how to fix it.
I have ViewModel
public class CartOrderViewModel
{
public Cart Carts;
public Order Orders;
public string name;
}
in CartController method
public async Task<IActionResult> Index()
{
CartOrderViewModel vm = new CartOrderViewModel
{
Carts = _cart,
Orders = new Order() { User = applicationUser },
};
return View(vm);
}
View Index from CartController
#using (Html.BeginForm("Test", "Cart", FormMethod.Post))
{
#Html.TextBoxFor(m => m.name)
<input type="submit" value="Submit" />
}
and in my method Test in CartController i dont see ViewModel CartOrderViewModel
[HttpPost]
public IActionResult Test(CartOrderViewModel test)
{
string komentarz = test.name;
return View();
}
Model: test in null.
Please help me. Thx a lot
How to send form from Index View from CartController my ViewModel CartOrderViewModel
example Model.Order.name ??
Why my name from CartOrderViewModel dont sent to method Test from index View ?
printscreen
enter image description here
Why my name from CartOrderViewModel dont sent to method Test from index View ?
ASP.NET binds to properties, not fields. Change all of the public fields in class CartOrderViewModel to be mutable properties with { get; set; }.
Like so:
public class CartOrderViewModel
{
public Cart? Carts { get; set; }
public Order? Orders { get; set; }
public String? Name { get; set; }
}
Don't forget to add validation attributes, such as [Required], as per your project's needs.

MVC Html.DropDownListFor returns complex type with null values

I have the following controller create action in my controller. It takes care that the displayname in the select list will be 'StoreName - StoreAddress'. The Store complexType is stored in Store.
// GET: Purchases/Create
public ActionResult Create()
{
ViewBag.Stores = db.Stores.Select(s => new { DisplayName = s.StoreName.ToString() + " - " + s.Address.ToString(), Store = s});
return View();
}
In the Create View the following code takes care that it will be displayed correctly.
<div class="form-group">
#Html.LabelFor(model => model.Store.StoreName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Store.StoreName, new SelectList(ViewBag.Stores, "Store", "DisplayName"), new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Store.StoreName, "", new { #class = "text-danger" })
</div>
</div>
It will go to the post method of the controller (if I am correct).
// POST: Purchases/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Store,Price,Date")] Purchase purchase)
{
if (ModelState.IsValid)
{
Store store = purchase.Store;
db.Purchases.Add(purchase);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchase);
}
However the Store store = purchase.Store will now give a complex Store type with the values for anything other than StoreName set to null. StoreName will be a string.
How can I get a complex type returned that is equal to the selected Store object?
Edit 1:
public class Purchase
{
public int Id { get; set; }
public Store Store { get; set; }
public string Type { get; set; }
[DataType(DataType.Currency)]
[Column(TypeName = "money")]
public decimal Price { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime Date { get; set; }
}
public class PurchaseDBContext : DbContext
{
public DbSet<Purchase> Purchases { get; set; }
public DbSet<Store> Stores { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
public class Store
{
public int StoreId { get; set; }
public string StoreName { get; set; }
public string Address { get; set; }
public string City { get; set; }
[DataType(DataType.PhoneNumber)]
[RegularExpression(#"^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9])((\s|\s?-\s?)?[0-9])((\s|\s?-\s?)?[0-9])\s?[0-9]\s?[0-9]\s?[0-9]\s?[0-9]\s?[0-9]$", ErrorMessage = "This is not a valid phonenumber")]
public string PhoneNumber { get; set; }
}
Do I need to use additional annotations to set the navigation properties?
There's alot going on in your example, but there isn't a need to pass a complex object if you're using Navigation Properties and your model is setup in a certain way. If you setup a navigation property for Store/StoreId, Entity Framework should infer things for you, so it will simplify your view.
public class Purchase
{
public int StoreId { get; set; }
[ForeignKey("StoreId")]
public Store Store { get; set;}
}
I'd pass a model to the view similar to below. IMO, it's cleaner than using ViewBag.
public class CreatePurchaseModel
{
//To populate a list
public List<Store> AvailableStores { get; set; }
//The actual object to be created
public Purchase Purchase { get; set; }
}
Controller Method:
// GET: Purchases/Create
public ActionResult Create()
{
var vm = new CreatePurchaseModel()
{
AvailableStores = db.Stores.ToList(),
Purchase = new Purchase()
};
return View(vm);
}
Populate a dropdownlist with the AvailableStores property to set the Purchase.StoreId
#Html.DropDownListFor(m => m.Purchase.StoreId, Model.AvailableStores.Select(x => new SelectListItem { Text = x.StoreName.ToString(), Value = x.StoreId.ToString() }))
If it's setup properly, all you need in the post method parameters is the purchase
[ValidateAntiForgeryToken, HttpPost]
public ActionResult Create(Purchase purchase)
{
//purchase.StoreId should have a non zero value
db.Purchases.Add(purchase);
db.SaveChanges();
}

Pass parameter from Htmldropdownlistfor to controller

I have a model passed from controller to view in my asp.net mvc5 website. Then I show the dropdownlist using the model and I want to pass an id back when submitting the form. Here is my model :
public class SiteDirectionModel
{
public int id { get; set; }
public string name { get; set; }
}
Then in the model, I use a List<SiteDirectionModel> to which I add new instances of each item I need. I fill up both these lists and then pass my model to the view.
#model List<SiteDirectionModel>
#using (Html.BeginForm("GetSiteRF", "Create", FormMethod.Post))
{
#Html.DropDownListFor(x => x.name,new SelectList(Model.name,"Sites"));
<input type="button" value="Selectionner" class="btn btn-primary"/>
}
Then how to retrieve the ids for each name ? And how to pass it as a parameter to my controller? Such that I would have :
public ActionResult GetSiteRF(int id)
{
int newId = id;
//Call method to searchId ...
return View("CreateADUser");
}
I have given how to bind and get value from dropdown. Please use your own BL in this.
Your model should be like this.
public class Something
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SiteDirectionModel
{
public SelectList MyDropDown { get; set; }
public int SelectedValue { get; set; }
}
You BL should be like this.
public List<Something> GetListofSomething()
{
//your logic.
}
Your Get method should be like this.
public ActionResult MyGetMethod()
{
SiteDirectionModel model = new SiteDirectionModel();
model.MyDropDown = new SelectList(GetListofSomething(), "key_field_name", "value_field_name", "default_value");
}
Then finally HTML
#Html.DropDownListFor(x => x.SelectedValue,Model.MyDropDown)

Problems with passing two models to one view

i want to pass two models to one view using a ViewModel
My models :
public class Candidat
{
public int Id { set; get; }
public string num_cin { set; get; }
public ICollection<Poste> postes { get; set; }
}
public class Poste
{
public int Id { set; get; }
public string poste_name {set;get}
public List<Candidat> candidats {set;get;}
}
public class PosteCandidatViewModel
{
public Candidat candidat { get; set; }
public Poste poste { get; set; }
}
the controller action :
[HttpPost]
public ActionResult Index( Poste poste,string num_cin)
{
if (ModelState.IsValid)
{
var v = (from c in _db.Candidats
where c.num_cin == num_cin
&& c.postes.Any(p => p.Id == poste.Id)
select c)
.SingleOrDefault();
if (v != null)
{
return RedirectToAction("Inscription", "Candidat");
}
else
{
return RedirectToAction("index", "BureauOrdre");
}
}
return View();
the view :
#model ProcRec.Models.PosteCandidatViewModel
<td>#Html.TextBoxFor(model => model.candidat.num_cin)</td>
<td><p>#Html.DropDownListFor(model => model.poste.Id,new
SelectList(ViewBag.Postes, "Id", "intitule_poste"),"choisir le poste")
</p></td>
my problem is that the linq query is not giving the result i want
(but if i gave to num_cin an poste.id some values it's work )
so the problem is that num_cin not have a valu from dropdownlist ...it's like having an empty value!!!!!!!!!
Change the POST method signature to accept the model, and the access the model properties
[HttpPost]
public ActionResult Index(PosteCandidatViewModel model)
{
Poste poste = model.Poste;
string num_cin = model.Candidat.num_cin;
The reason that parameter string num_cin is null is because #TextBoxFor(model => model.candidat.num_cin) generates <input type="text" name="candidat.num_cin" ... />which is trying to map to property candidat that contains property num_cin. Alternatively upi can use
[HttpPost]
public ActionResult Index( Poste poste, [Bind(Prefix="candidat")]string num_cin)
{
Note, if ModelState is invalid, you need to reassign the the value of ViewBag.Postes that you use in DropDownListFor()
if (ModelState.IsValid)
{
....
}
ViewBag.Postes = // set the value here before returning the view
return View(model);

MVC - Controller with multiple select lists

Are there any good ways to keep my controllers simpler when they have models that depend on a lot of select lists? I try to keep most of my controller actions as simple as possible (hopefully no more than 10 or so lines) but on pages that require a lot of dropdowns my actions usually exceed this:
public class Model
{
public IEnumerable<SelectListItem> AllLocations { get; set; }
public IEnumerable<SelectListItem> TopLocations { get; set; }
public IEnumerable<SelectListItem> AllTemplates { get; set; }
public IEnumerable<SelectListItem> TopTemplates { get; set; }
// ...
}
[HttpGet]
public ActionResult Index(int id)
{
var domain = Repository.Get(id);
var model = Mapper.Map<Domain, ViewModel>(item);
// any way to abstract this type of code?
model.AllLocations = new SelectList(repository.GetAllLocations(), "Value", "Text");
model.TopLocations = new SelectList(repository.GetTopLocations(), "Value", "Text");
model.AllTemplates = new SelectList(repository.GetAllTemplates(), "Value", "Text");
model.TopTemplates = new SelectList(repository.GetTopTemplates(), "Value", "Text");
// etc. etc.
return View(model);
}
[HttpPost]
public ActionResult Index(ViewModel model)
{
// any way to abstract this type of code?
model.AllLocations = new SelectList(repository.GetAllLocations(), "Value", "Text");
model.TopLocations = new SelectList(repository.GetTopLocations(), "Value", "Text");
model.AllTemplates = new SelectList(repository.GetAllTemplates(), "Value", "Text");
model.TopTemplates = new SelectList(repository.GetTopTemplates(), "Value", "Text");
// etc. etc.
return View(model);
}
As you say keeping controller actions small is great. As Jimmy Bogard says put your controllers on a diet!
I use an IModelEnricher combined with Automapper. I return an entity etc using a specific ActionResult that then automaps my entity to a ViewModel and enriches with data required for select lists (and any additional data required). This method keeps your code DRY and controllers thin like a super model :-)! Also keeping the select list data as part of your ViewModel keeps your controller, model, and view responsibilities clear.
Defining a ViewModel ernicher means that anywhere that ViewModel is used it can use the same enricher to get its properties. So you can return the ViewModel in multiple places and it will just get populated with the correct data.
In my case this looks something like this in the controller:
public virtual ActionResult Edit(int id)
{
return AutoMappedEnrichedView<PersonEditModel>(_personRepository.Find(id));
}
[HttpPost]
public virtual ActionResult Edit(PersonEditModel person)
{
if (ModelState.IsValid){
//This is simplified (probably don't use Automapper to go VM-->Entity)
var insertPerson = Mapper.Map<PersonEditModel , Person>(person);
_personRepository.InsertOrUpdate(insertPerson);
_requirementRepository.Save();
return RedirectToAction(Actions.Index());
}
return EnrichedView(person);
}
This sort of ViewModel:
public class PersonEditModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public int FavouriteTeam { get; set; }
public IEnumerable<SelectListItem> Teams= new List<SelectListItem>();
}
With this sort of Enricher:
public class PersonEditModelEnricher :
IModelEnricher<PersonEditModel>
{
private readonly ISelectListService _selectListService;
public PersonEditModelEnricher(ISelectListService selectListService)
{
_selectListService = selectListService;
}
public PersonEditModelEnrich(PersonEditModel model)
{
model.Teams = new SelectList(_selectListService.AllTeams(), "Value", "Text")
return model;
}
}
One other option is to decorate the ViewModel with attributes that define how the data is located to populate the select list. Like:
public class PersonEditModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public int FavouriteTeam { get; set; }
[LoadSelectListData("Teams")]
public IEnumerable<SelectListItem> Teams= new List<SelectListItem>();
}
Now you can decorate an appropriate method in your select service with an attribute like:
[ProvideSelectData("Teams")]
public IEnumerable Teams()
{
return _teamRepository.All.ToSelectList(a => a.Name, a => a.TeamId);
}
Then for simple models with no complex enrichment just the generic enrichment process can handle it. If you want to do anything more complex you can define an enricher and it will be used if it exists.
See this question. Also this blog post and this. Also this question on Automapper forum
You can setup a helper class, put every select list in a static method.Then,in view you can get every select list by htmlhelper. Controller will clear. At the same time, other view can use these select list too.
eg:
public class SelectHelper
{
public static List<SelectListItem> AllLocations()
{
//TODO repository.GetAllLocations()
}
public static List<SelectListItem> TopLocations()
{
//TODO repository.GetTopLocations()
}
...
}
view code:
#Html.DropDownList("selectname", SelectHelper.AllLocations())
Sure, just refactor it to a method, like so:
public class Model
{
public IEnumerable<SelectListItem> AllLocations { get; set; }
public IEnumerable<SelectListItem> TopLocations { get; set; }
public IEnumerable<SelectListItem> AllTemplates { get; set; }
public IEnumerable<SelectListItem> TopTemplates { get; set; }
// ...
}
[HttpGet]
public ActionResult Index(int id)
{
var domain = Repository.Get(id);
var model = Mapper.Map<Domain, ViewModel>(item);
InitializeSelectLists(model);
return View(model);
}
[HttpPost]
public ActionResult Index(ViewModel model)
{
InitializeSelectLists(model);
View(model);
}
private void InitializeSelectLists(Model model)
{
model.AllLocations = new SelectList(repository.GetAllLocations(), "Value", "Text");
model.TopLocations = new SelectList(repository.GetTopLocations(), "Value", "Text");
model.AllTemplates = new SelectList(repository.GetAllTemplates(), "Value", "Text");
model.TopTemplates = new SelectList(repository.GetTopTemplates(), "Value", "Text");
// etc. etc.
}
Or you can even do it in a constructor for your model or a facade service if you want to.

Categories

Resources