.net mvc select list handle on selected item event - c#

I am quite new to this net mvc thing. to understand what I am trying to do I will put an example.
Example
I have a list of clients that contains data and in my view I used a <select> with a foreach that goes through all the clients to show the data. What I am trying to do is, when a user selects a client name he would be redirected to another page where that page would get the client name as a parameter & do stuff with that.
I tried this but I am stuck in a part
<select>
#foreach (var item in Model.clients)
{
<option>
#Html.Encode(item.name)
</option>
}
</select>
I know how to redirect from page A to page B like this RedirectToAction(...) what I want to do is handle that select action to call the method in my controller & use that method to send a parameter to page B
UPDATE
<script type="text/javascript">
function Fct() {
var v = arguments[0]; //get The ID in the parameter
window.location.href = "#Url.Action("Index", "PageB")?client_id=" + v;
}
</script>
I tried both lists and the one proposed by #Shyui is easier but i wanted to try something with this one
<select id="clients_list" onchange="Fct(this.value)">
<option class="placeholder" selected disabled value="-1">Select Name</option> <!-- Can't be selected -->
#foreach (var item in Model.clients)
{
<option value="#item.ID">
#Html.Encode(item.name)
</option>
}
<option value="0">New Client</option>
</select>

Listen to the change event of the dropdown, get the selected option and redirect the user to the next action.
<select id="Clients">
#foreach (var item in Model.clients)
{
<option value="#item.name">#Html.Encode(item.name)</option>
}
</select>
Here is the javascript(using jQuery) to handle the change event
$(function(){
$("#Clients").change(function(){
var v=$(this).val();
window.location.href="#Url.Action("Details","Clients")?clientName="+v;
});
});
I am using the Url.Action helper method to generate the correct relative path to the action method. This will work if your code is inside a razor view. But if it is inside an external js file, try the solution explained in this answer.
Assuming your Details action method in ClientsController accepts a client name
public ActionResult Details(string clientName)
{
// to do : Return something
}
You might also consider using the html helper methods like Html.DropdownList to generate the dropdown element instead of doing a foreach.
#Html.DropDownList("Clients",new SelectList(Model.clients, "name", "name"))
Also you might consider passing the unique client Id(numeric) instead of the client name. There are limitations of query string value length in some browsers.

Related

C# MVC: List<string> becomes System.Collections.Generic.List`1[System.String] which becomes System.String[]

I am currently working on a c# MVC mvc webapplication but i have some troubles finding out why a property in my model goes from List to System.Collections.Generic.List`1[System.String] when passed from view to controller.
In my view I am listing some of my models and provides for each of them a Actionlink which calls the method ContinueStatus() providing the model object (status) in the parameter:
#foreach (DriftsstatusModel status in (List<DriftsstatusModel>)ViewData["activeStatuses"])
{
#Html.ActionLink("Fortsæt", "ContinueStatus", "Home", status, new { #class = "btn btn-success btn-lg", #style = "font-size:x-large" })
}
When I place a breakpoint in the above and scroll over status (the model object) I correctly see that the property SelectedProducts is a List<string> with count 1. However, when the actionlink button is clicked and the controller method ContinueStatus() is called, the property SelectedProducts has changed to System.Collections.Generic.List`1[System.String] and instead of count = 1 it says {string[1]}. The controller method:
public ActionResult ContinueStatus(DriftsstatusModel driftsStatus)
{
// Does something, but not with SelectedProducts
return RedirectToAction("Index", driftsStatus);
}
After the controler method Index returns to the view now SelectedProducts has changed to "System.String[]" from System.Collections.Generic.List`1[System.String] and still shows {string[1]}. Because SelectedProducts has changed I do not know how to access the one string which should currently be in it. This means the else branch is always chosen in the following part of my view:
<optgroup label="DLI">
#foreach (string product in dliProducts)
{
if (Model.SelectedProducts.Contains(product))
{
<option selected>#product</option>
}
else
{
<option>#product</option>
}
}
</optgroup>
Why does this happen and how can I correct it?
I wil try to answer some of the questions.
I am making an web application that should be used to send email and sms to notify ppl when a product has an error, for example that you cannot login to a service (product). This status email/sms can be started, continued and ended and thus there is a need to load earlier information of that specific status so you can change it and then either continue it (error is net yet fixed) or close it (error is fixed, so last status update).
The model DriftsstatusModel holds all of this information and have the property
public List<string> SelectedProducts { get; set; }
which holds selected options in a multiselect. In my view I then show all the active statusses like so (removed button styling etc.):
#foreach (DriftsstatusModel status in (List<DriftsstatusModel>)ViewData["activeStatuses"])
{
#Html.ActionLink("Fortsæt", "ContinueStatus", "Home", status, new { #class = "btn btn-success btn-lg", #style = "font-size:x-large" })
#Html.ActionLink("Afslut", "EndStatus", "Home", status, new { #class = "btn btn-danger btn-lg", #style = "font-size:x-large" })
}
here it should be noted that ViewData["activeStatuses"] is populated in the controller where i get the DriftsstatusModel instances from a DB and its works as expected getting all the prior information. It is also here that status (a instance of DriftsstatusModel) have the "right" SelectedProducts where i in debug mode can see the count and acces the elements.
So when the continue button is clicked the two following controller actions is called:
public ActionResult ContinueStatus(DriftsstatusModel driftsStatus)
{
// Does something, but not with SelectedProducts
return RedirectToAction("Index",driftsStatus);
}
public ActionResult Index(DriftsstatusModel driftsStatus)
{
// Does something, but not with driftsStatus.SelectedProducts
ViewData["activeStatuses"] = statusDbHandler.GetActiveStatusses();
return View(driftsStatus);
}
Now all the information in the status should be loaded into the fields in the view where one is a multiselect:
<select class="selectpicker w-100" data-val="true" id="SelectedProducts" name="SelectedProducts" multiple>
<optgroup label="DLI">
#foreach (string product in dliProducts)
{
if (Model.SelectedProducts.Contains(product))
{
<option selected>#product</option>
}
else
{
<option>#product</option>
}
}
</optgroup>
In this multiselect i check if SelectedProducts already contains some of the options and if it does i want these options to already be selected and thus:
<option SELECTED>#product</option>
However, because SelectedProducts somehow is not the same here as in place where i list the active statusses this is never true. Why SelectedProducts has changed i just can't figure out...
Hope this helps

How can i send back a model containing two lists from the view to my controller

I have a cshtml view, for which I am sending a viewmodel. That viewmodel consist of two list of albums (music albums). I then ask the user to check up to 3 of these albums (a checkbox next to the title) to vote for his favorite music. I use javascript to ensure he doesn't check anymore than 3 (The security is a detail right now, I'm more concerned about getting it to work, but I'm open to suggestion if people have a better solution).
Since all albums are displayed in a table, I would love to send back to the controller through the submit button, the same model after updating it.
Basically, one of the list contain the current vote the user has made before loading the page (can be empty), and the second one should be empty until sent back to the controller containing the list of votes that are currently selected. I then use these two lists to compare them and update the database, removing the votes he removed, and adding the vote he added.
But I am unable to create a proper form to return these informations as I am not used to forms.
I tried to put the whole list in a form, but it didn't work. My reserach when I look for "sending model back to controller" usually do just that and get it to work.
View model
public class CategoryVotesUserViewModels
{
public CategoryVoteViewModels categoryVoteViewModels;
public List<int> listVotesEntry = new List<int>();
public List<int> listVotesOutput = new List<int>();
}
Relevant CSHTML and javascript
#section Header{
<script>
var MAX_VOTES = 3;
function checkNumberVotes($this) {
console.log($("input[name='listVoteOutput']:checked"));
if ($("input[name='listVoteOutput']:checked").length > MAX_VOTES) {
$this.checked = false;
}
}
</script>
}
#using (Html.BeginForm("VoteInCategory", "Votes", new { SearchModel = Model }, FormMethod.Post))
{
<ul>
#foreach (var av in Model.categoryVoteViewModels.listVotes)
{
<li>
#av.album.Title | #av.votes |
<input type="checkbox"
name="listVoteOutput"
value=#av.album.ID
onclick="checkNumberVotes(this)"
#if (Model.listVotesEntry.Contains(av.album.ID))
{ <text> checked </text> } />
</li>
}
</ul>
<div class="form-group text-center">
<input type="submit" class="btn btn-primary" value="Submit" />
</div>
}
Controller
[HttpPost]
public ActionResult VoteInCategory(CategoryVotesUserViewModels categoryVotesUserViewModels)
{
if (ModelState.IsValid)
{
List<int> toAdd = categoryVotesUserViewModels.listVotesOutput.Except(categoryVotesUserViewModels.listVotesEntry).ToList();
List<int> toRemove = categoryVotesUserViewModels.listVotesEntry.Except(categoryVotesUserViewModels.listVotesOutput).ToList();
VoteService.updateVoteUserCategory(User.Identity.GetUserId(), toRemove, toAdd, categoryVotesUserViewModels.categoryVoteViewModels.categoryID);
//TODO Redirect to success
return RedirectToAction("Index", "Home");
}
return View(categoryVotesUserViewModels);
}
If the user already had voted, all album whose ID is in "ListVotesEntry" should begin checked. If the user hasn't voted, or voted for nothing previously, "ListVotesEntry" should be empty.
When the User press the submit button, if an album is checked, the album's id should be added to the "ListVotesOutput" list. Also, both "ListVotesEntry" and "ListVotesOutput" should be sent back to the controller. The list with the names of the albums and their titles/ID is no longer necessary for the rest of the treatment
Found the solution. The problem was that my model needed to use "{get; set;}" on its attributes, otherwise the binding doesn't work, which mean that it send back an empty model.

Generate list based on selected option

I'm trying to create a page for admins to create new users. When these users are created they will also have a role that they are assigned, and each of those roles will have a list of associated rights. The role will be selected from a dropdownlist of roles. After the role has been selected, but before the user has been created I want the list of associated roles to be displayed on the side of the form so they can see if the rights associated with that role are what they want.
I'm not entirely sure how to approach this problem without reloading the entire page after selecting a role. I'm sure how to have a refresh on a div when the information changes
You can use AJAX to achieve this.
Here is a simple solution to start with. First, create an action method in your controller which accepts the roleId value and get's the rights associated with that role and return that as JSON array.
In the below example, I am simply hard coding 2 rights. You can replace this implementation with however you want to get the data(may be from a database) using the roleId param value.
public JsonResult RoleDetails(int roleId)
{
// Hard coded data.
// Replace with data from your db using roleId value
var rightList = new List<string> { "Admin", "Editor" };
return Json(rightList);
}
So when called with a request URL like /Home/RoleDetails?roleId=2, this action method will return data like this(JSON array).
["Admin","Editor"]
Now, Render your select element with the Roles as options. We shall store the path to the action method in a data attribute on the SELECT element. For example, your rendered HTML should be like this.
<select id="selectedRole" data-url="/Home/RoleDetails">
<option>Select one</option>
<option value="1">Role A</option>
<option value="2">Role B</option>
<option value="3">Role C</option>
</select>
<div id="role-details"></div>
We also rendered a div to show the details.
I am assuming you know how to render a SELECT element. If you do not, please refer
Select Tag Helper in ASP.NET Core MVC post
You can use the Url.Action helper to generate the correct path to the RoleDetails action method, for example,
<select id="selectedRole" data-url="#Url.Action("RoleDetails","Home")">
Now you can use JavaScript to listen to the change event of the SELECT element, read the selected option value, make an ajax call to the action method we created above, get the data ,which is a JSON array, and update the UI using that.
Here is a working sample using jQuery for DOM manipulations.
$(function () {
// Listen to "change" event on SELECT
$("#selectedRole").change(function () {
//Get the value of selected option
var roleId = $(this).val();
var url = $(this).data("url") + "?roleId=" + roleId;
//Make the AJAX call
$.getJSON(url).then(function (data) {
var list = "";
// Loop through the JSON array and build HTML for a list of P tags.
$.each(data, function (index, item) {
list += "<p>" + item + "</p>";
});
// Update the Div content
$("#role-details").html(list);
}).fail(function (e) {
$("#role-details").html("Error getting data");
console.warn(e);
});
});
})

Can't pass select values to controller

I'm having a difficult time passing my Select values on my View to the controller.
I have two items on my view which I wish to return to the model, where I can call in the controller. Heres what I have so far.
<label for="phone" class="ui-hidden-accessible">Phone Number:</label>
#Html.TextBoxFor(m => m.PhoneNumber, new { #class = "field-margin", id="phone", type="tel", placeholder="Phone Number"})
<p></p>
<div><p>Save phone for future purchases?</p>
<select name="SavePhone"id ="SavePhone" class="SavePhone" data-role="select">
<option value="false" #(Model.SavePhone == false ? "selected" : "")>No</option>
<option value="true" #(Model.SavePhone == true ? "selected" : "")>Yes</option>
</select><
I'm not exactly sure how to call the second part for the select options. However the top part of my code which accepts the phone number works. My naming in the model, controller, and view all are the same so I'm not sure what to do next. If you have any suggestions it would be appreciated.
Thanks!
Edit
I figured out a part of my problem, Since I am loading this as
#Html.Partial("MobilePhoneView", Model)
after I click continue on the first page, it loads the view with my two options and hits the select block before it even displays. Is there some kind of work around for this?
You can do this using AJAX. If you have following HTML
<select name="SavePhone"id ="SavePhone" class="SavePhone" data-role="select">
<option value="false">No</option>
<option value="true">Yes</option>
</select>
Then , you can simply use following to sent your choice to controller:
<script language="javascript" type="text/javascript">
$(document).ready(function () {
$("form").submit(function () {
$.ajax({
url: '#Url.Action("MethodName","ControllerName")',
type: 'POST',
cache: false,
data: { Selected: $("#SavePhone").val() },
success: function (data) {
//
}
});
});
)};
</script>
You will get this value in the controller
private string MethodName (string Selected)
{
String value = Selected;
return "OK";
}
The only possible problem with your code might be with selected attribute. Not all browsers understand just selected (I believe this is HTML5 way of setting such attributes), though all should understand selected="selected". So what you can try is:
<select name="SavePhone"id ="SavePhone" class="SavePhone" data-role="select">
<option value="false" #(Model.SavePhone == false ? "selected=\"selected\"" : "")>No</option>
<option value="true" #(Model.SavePhone == true ? "selected=\"selected\"" : "")>Yes</option>
</select>

Retrieving value from a select tag in asp.net from controller to view

I am working on an asp.net webapp, and in a view I have a drop down list that the user can select a value from. The drop down list works just fine-the right text is displayed in the menu. But, when I try and use some basic JS to capture the value, I get
"Uncaught TypeError: Object # has no method 'GetElementById' " in the JS Console in Chrome. Here is my code:
<select id="stop" onchange="sendInfo();">
#foreach(var blah in ViewBag.foobar)
{
<option value=#blah>#blah</option>
}
</select>
<script>
function sendInfo() {
var stopId = document.GetElementById("stop").value;
}
</script>
Any help would be appreciated, I am very new to MVC and asp.net stuff.
Thanks,
Amanda
JavaScript is a case sensitive language and the method what your are looking for is getElementById
So you should write:
var stopId = document.getElementById("stop").value;
You don't need to call getElementById function, you can access html element by event object which passed to each event handler:
<select id="stop" onchange="sendInfo(event);">
#foreach(var blah in ViewBag.foobar)
{
<option value=#blah>#blah</option>
}
</select>
<script>
function sendInfo(event) {
var stopId = event.target.value;
}
getElementById...
Case sensitivity matters.

Categories

Resources