ASP.NET MVC passing value from selectlist - asp.net-mvc

I have a .NET Core 2 MVC project, and I am very new to this. I have a Details View that contains data for a selected user. I am using a ViewModel to display that selected user's data and it is all working fine. What I am trying to ultimately do is allow for the cloning of one user's Identity Roles to the current selected user. So I have in my View a panel that contains a SelectList to where you can select some other user (I'm calling them the source user) to get their currently assigned Identity Roles. I then want to have a button that says "Clone" and on click remove all Roles for the current user and then add them to the Roles that the selected source user is assigned to.
Here is what I am using for my drop-down list:
View:
<select asp-for="EmployeeList" class="form-control"
asp-items="#Model.EmployeeList"
onchange="this.form.submit()">
<option disabled selected>-</option>
</select>
Controller:
var employeeList = _employeeRepository.GetEmployeeList().Select(e => new
{
Id = e.EmployeeId,
Value = e.PreferredFirstName != null ? e.LastName + ", " + e.PreferredFirstName : e.LastName + ", " + e.FirstName
});
ViewModel:
public SelectList EmployeeList { get; set; }
In my View I have a form for the post:
#using (Html.BeginForm("Details", "ApplicationUser", FormMethod.Post))
So I need to pass the selected id (string) of my source user drop-down list to the [HttpPost] Details action in my controller. I also want to maintain the selected option in my drop-down list in the View. I also want to maintain all of the data that is being displayed in my ViewModel.
Your help is greatly appreciated.

As said before, your view is really not making sense. However we can fix that.
Viewmodel:
public List<SelectListItem> EmployeeList { get; set; }
View:
<select id="employee-select" asp-for="EmployeeId" class="form-control" onchange="postEmployee()" asp-items="#Model.EmployeeList">
<option disabled selected>-</option>
</select>
<script>
function postEmployee() {
var id = $('#employee-select').val();
if (id !== '' && id != null) {
var url = '../CONTROLLERNAME/UpdateUser/' + id;
var settings = {
"url": url,
"method": 'POST'
};
$.ajax(settings);
}
}
</script>
Controller:
[HttpPost("UpdateUser/{id}")]
public IActionResult UpdateUser([FromRoute] int id)
{
// Do things with id
}

Related

How to make binding work on additional parameter with a Get request

My model has property
public int SelectedContactId { get; set; }
The view has a dropdown, and on changing selection it refreshes the view:
<select asp-for="SelectedContactId"
asp-items="Model.Contacts"
class="form-control ddlContractorContacts bg-light"
onchange="location.reload(true)">
</select>
The HttpGet Edit() action method is called:
// GET: Contractors/Edit/5
public ActionResult Edit(int? contractorId, int SelectedContactId)
But SelectedContactId is not bound, and I cannot figure out how it can be done.
P.S. If I use [FromBody] attribute before int SelectedContactId, I get an error on the first loading the page: HTTP ERROR 415.
First of all, from reading your comment
// GET: Contractors/Edit/5
public ActionResult Edit(int? contractorId, int SelectedContactId)
it looks like the contractor id is 5 and it will be always there with the URL since you're editing a contractor. So you should change it to
// GET: Contractors/Edit/5
public ActionResult Edit(int Id, int SelectedContactId)
to take advantage of MVC default route mapping - the integer after / should automatically bind to Id.
And then I think your SelectedContactId should be marked as optional, because when you first land on the edit page, there shouldn't be any selected contact. And you want to automatically bind the selected contact as additional parameter on the URL as soon as the user picks a contact. That's what you want to accomplish right?
If that's the case, you should change SelectedContactId to optional:
// GET: Contractors/Edit/5
public ActionResult Edit(int Id, int? SelectedContactId)
Now let's get to the point. There are many ways to manipulate query parameters. Some of them are:
Submit a GET form
Use JavaScript to manually append inputs to the URL
Here I am showing you the #1 - Submit a GET form.
Submit a GET form
If you want the server to get the input selections on the view, you need submit the <form /> that contains all the inputs.
Whenever the user makes the selection and hits the submit button, the form will send the input data back to the server. And your MVC will bind the request to your action's parameters.
Using JavaScript location.reload(true) will only refresh the current page. It doesn't send the inputs back to the server.
View Model
public class EditContractorViewModel
{
[Display(Name = "Contact")]
public int? SelectedContactId { get; set; }
public IEnumerable<SelectListItem> Contacts { get; set; }
}
Controller
public class ContractorController : Controller
{
public IActionResult Edit(int id, int? selectedContactId)
{
// In theory, you need to take the id and look that up in your persistent storage
// and then you build up the view model and pass it back to the View
var vm = new EditContractorViewModel
{
SelectedContactId = selectedContactId,
Contacts = new List<SelectListItem>
{
new SelectListItem("Contact 1", "1"),
new SelectListItem("Contact 2", "2"),
new SelectListItem("Contact 3", "3"),
new SelectListItem("Contact 4", "4"),
new SelectListItem("Contact 5", "5")
}
};
return View(vm);
}
}
View
<!-- The View -->
#model EditContractorViewModel
<form asp-area="" asp-controller="ContractorController" asp-action="Edit"
method="get">
<div class="form-group">
<label asp-for="SelectedContactId"></label>
<select asp-for="SelectedContactId"
asp-items="Model.Contacts"
class="form-control ddlContractorContacts bg-light">
<option value="">- select -</option>
</select>
</div>
<button type="submit" class="btn btn-success">Submit</button>
</form>
Result
First landing on the page:
Select a contact and hit submit:
If you want the form to be automatically submitted on the dropdown change, you can get rid of the submit button on the view and write some JavaScripts to submit the form manually:
$(function() {
$('select.ddlContractorContacts').change(function() {
let $form = $(this).closest('form');
$form.submit();
return false;
});
});
Since you use location.reload(true),it will always redirect to /Contractors/Edit/5 and your SelectedContactId on Edit action will always be 0 since you do not pass the selected Id in query string or somewhere else.
A simple way is that you could pass the Id using query string like /Contractors/Edit/5?SelectedContactId=1.
Refer to below code of the view:
<select asp-for="SelectedContactId"
asp-items="Model.Contacts"
id="mySelectedContactId"
class="form-control ddlContractorContacts bg-light"
onchange="reload()"></select>
#section Scripts{
<script>
function reload() {
var selectedContactId = document.getElementById("mySelectedContactId").value;
var url = window.location.href.split(/[?#]/)[0];
url += '?SelectedContactId=' + selectedContactId
window.location.href = url;
}
</script>
}

AngularJS select doesn't honour selected value

I have an ASP.NET MVC controller with a VideModel I serialize into JSON and send to the client, along with a view and marry those two together using AngularJS. The view model has a lookup (array) that is bound to a Select element. It all works fine and I can select a value, post the form back to the MVC controller and get the selected value there.
When I send back a view model with a value already selected, the value is not selected in the Select element. I can see that the value is correctly set on the client side (using Batarang) but the Select still doesn't show that as selected. Here's a link to the sample showing this. Any ideas?
Here's the code snippets to give you some ideas (angular controller doesn't have any code, just a mean to add new item with default values to the destination array).
MVC Controller:
public Sources {
First,
Second,
Third
}
public ActionResult MyGetAction()
{
var vm = new MyViewModel {
Sources = SelectListExtension.ToSelectListItem<Sources>() //Would return IEnumerable<SelectListItem> (with proper Value and Text for all enum entries)
Destination = new List<DestinationViewModel>(); //Has int? SourceId
};
vm.ClientData = SerializeToJson(vm);
return View(vm);
}
[HttpPost]
public ActionResult MyPostAction(MyViewModel model)
{
var selectedSource = model.Destination[0].SourceId; //works fine
model.Sources = SelectListExtension.ToSelectListItem<Sources>(); //populate the lookup again
//model still has 1 destination with the SourceId correctly set
return View(model);
}
My View bound to the JSON object:
#using (Html.BeginForm())
{
<div ng-repeat="destination in Destinations">
<select ng-init="destination.Source = { Value: destination.SourceId }"
ng-model="destination.Source"
ng-options="s.Text for s in Source track by s.Value"
ng-change="destination.SourceId = destination.Source.Value"
name="Destinations[{{$index}}].SourceId"
id="Destinations_{{$index}}__SourceId">
<option value=""></option>
</select>
</div>
<input type="submit">
}

Validation for dropdown list in MVC

I am new to MVC and I am still trying to explore stuffs inside MVC. Now I came to place where I need to implement validation for drop-down list.
I spent fair amount of time searching for validation in MVC. But I dint get any result which suits to my problem. Please bear in mind that I am new to MVC so correct me if I my approach is wrong for implementing validation.
I created model class like this :
public class ValidationModel
{
[Required(ErrorMessage = "Please select atleast one option")]
public string dropdown { get; set; }
}
And In my controller I am using this code :
#using (Html.BeginForm("selectIndex", "Home", FormMethod.Post))
{
<select name ="dropdwnMode" id="dropdwnMode" class="textbox">
<option selected="selected" value="">Select Mode
#Html.ValidationMessageFor(Model => Model.dropdown) </option>
<option value="1"> Add or Update Customer </option>
<option value="2">Update Customer Credit</option>
</select>
}
Here what I want is when my drop-down list is in Select Mode that is default, it should show please select at least one option error and if user selects 1st or 2nd option then this error message should not display.
But Now it is not displaying any message if I use above approach. So can someone guide me in achieving this?
I would recommend replacing your "Dropdown" property on the model with two properties - one that would hold the selected value and one that would hold all possible values. Then you can use an HTML helper (Razor syntax) to create your dropdown in the view, and MVC would take care of the validation.
Model:
public class ValidationModel
{
/// <summary>
/// ID of the selected option
/// </summary>
[Required(ErrorMessage = "Please select at least one option")]
public int SelectedOptionID { get; set; }
/// <summary>
/// Possible values for the dropdown
/// </summary>
public IEnumerable<OptionModel> Options { get; set; }
}
OptionModel:
public class OptionModel
{
/// <summary>
/// ID (key that uniquely identifies this option)
/// </summary>
public int ID { get; set; }
/// <summary>
/// Name (value or display text that will be shown in the UI)
/// </summary>
public string Name { get; set; }
}
You could also store other properties on the option model if needed. Alternatively, you could use a key-value-pair or dictionary instead of the option model to avoid creating a class, which would be faster but less clear.
View:
#model ValidationModel
#using (Html.BeginForm("selectIndex", "Home", FormMethod.Post))
{
#Html.ValidationMessageFor(Model => Model.SelectedOptionID)
#Html.DropDownListFor(m => m.SelectedOptionID,
new SelectList(Model.Options, "ID", "Name"),
"Select an option") // Default text before the user has selected an option
}
You could name Options and SelectedOptionID a bit better to clarify their usage depending on your implementation.
With this implementation you would (and should) populate the options list from your controller. The selected option would be populated when the user selects an option. Like this:
Controller:
public ActionResult YourAction()
{
ValidationModel model = new ValidationModel();
model.Options = new List<OptionModel> {
new OptionModel { ID = 1, Value = "Add or Update Customer" },
new OptionModel { ID = 2, Value = "Update Customer Credit" }
}
return View("YourViewName", model);
}
As a side note, I would recommend naming your ValidationModel after what the view that uses it is doing (e.g. HomepageModel if your view is the entire homepage or NavigationFormModel if your view is just a partial view that contains a dropdown for navigation).

MVC3 using CheckBox with a complex viewmodel

Right guys. I need your brains as I can't find a way to do this properly.
I have a view model:
public class EditUserViewModel
{
public User User;
public IQueryable<ServiceLicense> ServiceLicenses;
}
User is unimportant as I know how to deal with it.
ServiceLicenses has the following implementation:
public class ServiceLicense
{
public Guid ServiceId { get; set; }
public string ServiceName { get; set; }
public bool GotLic { get; set; }
}
Getting a checked list of users is cool. It works like a charm.
<fieldset>
<legend>Licenses</legend>
#foreach (var service in Model.ServiceLicenses)
{
<p>
#Html.CheckBoxFor(x => service.GotLic)
#service.ServiceName
</p>
}
</fieldset>
The problem I'm having is getting the updated ServiceLicenses object with new checked services back to the HttpPost in my controller. For simplicity lets say it looks like this:
[HttpPost]
public ActionResult EditUser(Guid id, FormCollection collection)
{
var userModel = new EditUserViewModel(id);
if (TryUpdateModel(userModel))
{
//This is fine and I know what to do with this
var editUser = userModel.User;
//This does not update
var serviceLicenses = userModel.ServiceLicenses;
return RedirectToAction("Details", new { id = editUser.ClientId });
}
else
{
return View(userModel);
}
}
I know I am using CheckBox the wrong way. What do I need to change to get serviceLicenses to update with the boxes checked in the form?
i understand that ServiceLicenses property is a collection and you want MVC binder to bind it to you action parameters property. for that you should have indices attached with inputs in your view e.g
<input type="checkbox" name = "ServiceLicenses[0].GotLic" value="true"/>
<input type="checkbox" name = "ServiceLicenses[1].GotLic" value="true"/>
<input type="checkbox" name = "ServiceLicenses[2].GotLic" value="true"/>
Prefix may not be mandatory but it is very handy when binding collection property of action method parameter. for that purpose i would suggest using for loop instead of foreach and using Html.CheckBox helper instead of Html.CheckBoxFor
<fieldset>
<legend>Licenses</legend>
#for (int i=0;i<Model.ServiceLicenses.Count;i++)
{
<p>
#Html.CheckBox("ServiceLicenses["+i+"].GotLic",ServiceLicenses[i].GotLic)
#Html.CheckBox("ServiceLicenses["+i+"].ServiceName",ServiceLicenses[i].ServiceName)//you would want to bind name of service in case model is invalid you can pass on same model to view
#service.ServiceName
</p>
}
</fieldset>
Not using strongly typed helper is just a personal preference here. if you do not want to index your inputs like this you can also have a look at this great post by steve senderson
Edit: i have blogged about creating master detail form on asp.net mvc3 which is relevant in case of list binding as well.

Understanding how DropDownListFor is working in MVC3

I'm new to MVC3 and have been working on a small site using EF and 'Code First'. I'm trying to do a few things in a view dealing with a drop down list and am wondering what the best way to go about them is. I want a user to be able to select a rule from the dropdownlist, and depending upon which rule was selected, I would like a label on the page to show the rule name (without posting). I also need to be able to send the selected rule onto the next page. I haven't added all of the necessary fields to the view yet because I'm really at a loss on how it should work. How should I go about trying to do this?
I've got my model:
public class D1K2N3CARule
{
public int ID { get; set; }
[Required]
public int Name { get; set; }
[Required]
public string Rule { get; set; }
public D1K2N3CARule(int name, string rule)
{
Name = name;
Rule = rule;
}
public D1K2N3CARule()
{
Name = 0;
Rule = "";
}
}
My ViewModel:
public class D1K2N3CARuleViewModel
{
public string SelectedD1K2N3CARuleId { get; set; }
public IEnumerable<D1K2N3CARule> D1K2N3CARules { get; set; }
}
My Controller:
public ActionResult Index()
{
var model = new D1K2N3CARuleViewModel
{
D1K2N3CARules = db.D1K2N3DARules
};
return View(model);
}
and my View:
'#model CellularAutomata.Models.D1K2N3CARuleViewModel
#{
ViewBag.Title = "Index";
}
<asp:Content id="head" contentplaceholderid="head" runat="server">
<script type="text/javascript">
</script>
</asp:Content>
<h2>Index</h2>
<table>
<tr>
<td>
#Html.DropDownListFor(
x => x.D1K2N3CARules,
new SelectList(Model.D1K2N3CARules, "ID","Rule")
)
</td>
</tr>
</table>'
I want a user to be able to select a rule from the dropdownlist, and depending upon which rule was selected, I would like a label on the page to show the rule name (without posting)
You will need javascript here. jQuery would be perfect for the job. I would start by providing a deterministic id for the dropdown because if you run this view inside a template there could be prefixes added to the id which would ruin our javascript id selectors (see below):
#Html.DropDownListFor(
x => x.D1K2N3CARules,
new SelectList(Model.D1K2N3CARules, "ID", "Rule"),
new { id = "ruleDdl" }
)
then provide some container which will receive the selected value:
<div id="ruleValue" />
and finally in a separate javascript file subscribe for the change event of the dropdown list and update the container with the selected value/text:
$(function() {
// subscribe for the change event of the dropdown
$('#ruleDdl').change(function() {
// get the selected text from the dropdown
var selectedText = $(this).find('option:selected').text();
// if you wanted the selected value you could:
// var selectedValue = $(this).val();
// show the value inside the container
$('#ruleValue').html(selectedText);
});
});
I also need to be able to send the selected rule onto the next page.
You could put your dropdown inside a form
#using (Html.BeginForm("NextPage", "Foo"))
{
#Html.DropDownListFor(
x => x.D1K2N3CARules,
new SelectList(Model.D1K2N3CARules, "ID","Rule")
)
<input type="submit" value="Go to the next page" />
}
and the NextPage controller action will receive the selected value.

Resources