In a Razor View's #html.ActionLink, how do we pass the value of a dropdown list? - asp.net-mvc

So what I am trying to do is that I have a database table of Roles, that I want to display in a dropdown list, and send the value to a different controller function. However, when I try to do this, I do not receive the value of the new Role that is selected from my dropdown list, but instead the value that was previously in my Model.
Here is my code for CSHTML:
#model IEnumerable<OnlineStoreData.Model.User>
<h4>List of Users: </h4>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.UserName)
</th>
<th></th>
</tr>
#foreach (var user in Model) {
if (user.Role.RoleName.TrimEnd(' ') == "User")
{
<tr>
<td>
#Html.DisplayFor(modelItem => user.UserName)
</td>
<td>
#Html.DropDownListFor(modelItem => user.Role.RoleName, new SelectList(ViewBag.RoleList)) //Here I need to select a new Role, for example "Admin"
#Html.ActionLink("Promote", "Promote", new { id = user.UserId, role = user.Role.RoleName }) |
#Html.ActionLink("Delete", "Delete", new { id = user.UserId })
</td>
</tr>
}
}
</table>
and this is the code for my Controller
public ActionResult ManageUsers()
{
ViewBag.RoleList = storeDBEntities.Roles.Select(role => role.RoleName).ToList();
return View(storeDBEntities.Users.ToList());
}
public ActionResult Promote(int id, string role)
{
//Here I should get the new role selected in the dropdown list, but I keep getting "User", which is the old role.
User toPromUser = storeDBEntities.Users.Find(id);
Role newRole = storeDBEntities.Roles.FirstOrDefault(r => r.RoleName == role);
if(toPromUser != null && newRole != null)
{
toPromUser.Role = newRole;
toPromUser.UserRole = newRole.RoleId;
storeDBEntities.SaveChanges();
}
return RedirectToAction("ManageUsers", "Users");
}
I am not sure how I should fix this to get the code to perform what is intended. Thank you.

The problem is, that you cannot dynamically append the selected value of your dropdown to your action link without JavaScript.
I think a more elegant approach would be to put your drop down and action buttons in a <form>. That way the method can also be a post, which is a bit more nice in a way, as get operations should not manipulate data.
<td>
<form method="post" action="#Url.Action("Promote", new { id = user.UserId })">
#Html.DropDownList("role", new SelectList(ViewBag.RoleList))
<button type="submit">Promote</button>
|
#Html.ActionLink("Delete", "Delete", new { id = user.UserId })
</form>
</td>
Note that the name of your drop down list should match the name of the role parameter of your controller.
When that works, you could then add the [HttpPost] attribute to your Promote action, to clarify that this method changes something.
For your delete action you could do something similar. Either make a second <form> with a different URL, or make it a submit button, too, in the same form, and give the buttons each a name and value.
The value of the button you clicked on will be sent to the server - note that I changed the form action URL:
<td>
<form method="post" action="#Url.Action("Update", new { id = user.UserId })">
#Html.DropDownList("role", new SelectList(ViewBag.RoleList))
<button type="submit" name="operation" value="promote">Promote</button>
|
<button type="submit" name="operation" value="delete">Delete</button>
</form>
</td>
And then decide what to do in the controller:
[HttpPost]
public ActionResult Update(int id, string operation, string role)
{
...
Lastly, you probably want a confirmation message on the delete action, which can be done so:
<button type="submit" name="operation" value="delete" onclick="return confirm('Do you really want to delete this user?');">Delete</button>

Related

The edit button is deleting from the database instead of saving to it MVC

I have posted the relevant code to this issue below. My problem is, let's say, the database is displaying NA, I want to edit it and put in 1.1, or some number. Instead of updating and saving this new number, it deletes NA and does not update or save anything, so I know it is doing something, but I'm not sure where I have gone wrong. If I change the type in the model to int or object, it gives an error for conversion to string. Can someone help please? Thank you!
Controller:
public ActionResult Edit ()
{
return View ();
}
[HttpPost]
public ActionResult Edit(MyIssue issues)
{
var model = new Test.Models.Tables();
if (ModelState.IsValid)
{
db.Entry(issues).State = EntityState.Modified;
issues.Number = model.Number;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(issues);
}
Model:
namespace Test.Models
{
public class Tables: DbContext
{
public string Number { get; set; }
}
}
View:
<td>
#if (issue.Number != null)
{
#Html.HiddenFor(modelItem => issue.Number)
#Html.DisplayFor(modelItem => issue.Number)
<text>|</text>
<h5 id="editclass">
#Html.ActionLink("Edit", "Page1", new { id = issue.Number })
</h5>
using (Html.BeginForm())
{
#Html.ValidationSummary(true) {
<fieldset>
#Html.HiddenFor(modelItem => issue.Number)
<div class="editor-field">
#Html.EditorFor(modelItem => issue.Number)
#Html.ValidationMessageFor(modelItem => issue.Number)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
}
}
</td>
From your code, I assume that the code you shown is inside a loop where issue is the loop iterator variable. So razor will generate an input field with name "issue.Number". When the form is submitted, model binder cannot bind this form value to the Number property of your MyIssue object ! So it gets the default null value and your code is assigning the null value as the Number property and saving it.
You should generate an input field with name="Number". You may use the Html.TextBox helper method to do so.
#foreach (var issue in SomeCollection)
{
<tr>
<td>
#using (Html.BeginForm())
{
<!-- Other fields also goes here -->
#Html.TextBox("Number",issue.Number)
<input type="submit" />
}
</td>
</tr>
}

using the same partial view with different buttons

I have the following partial view, which lists users in a table. Each row has an Enroll button, which enrolls the user to the selected course.
I need almost the same view for another task. However, I need to add users to Discussions (instead of enrolling them to a course). I know I can create another view and change the Enroll buttons to Add buttons.
However, I wonder if there is a more effective way of doing this. My approach does not seem to be easy to maintain.
#model IEnumerable<ApplicationUser>
<h4>Search results:</h4>
<table class="table-condensed" style="font-size:smaller">
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
<input class="btn_Enroll" data-userid="#item.Id" value="Enroll" type="button" />
</td>
</tr>
}
</table>
<script>
$(".btn_Enroll").click(function () {
//code for enrolling
var course_id = $("#hdn_SelectedCourseId").val();
})
</script>
One way to do it is by setting an attribute of your calling action method that will render this view
<table class="table-condensed" style="font-size:smaller" data-module="#ViewData["module"]"></table>
and then use it in your JS code
<script>
$(".btn_Enroll").click(function () {
//code for enrolling
var course_id = $("#hdn_SelectedCourseId").val();
var moduleType = $(this).closest("table").attr("data-module");
if (moduleType === "enroll")
{
//Do enrollment
}
else if (moduleType === "discussion")
{
//discuss here
}
})
For example on home page you have links like
#Html.ActionLink("enrollment", "Index", new { module = "enroll" })
#Html.ActionLink("Discussion", "Index", new { module = "discussion" })
and your ApplicationUserController has index action like this
public ActionResult Index(string module)
{
ViewData["module"] = module;
return View();
}
However if scope of project or requirements can change for enrollment and/or discussion then better to keep then separate to avoid complexity of code in single page as well as in single JS code.

MVC Form Form element not going to controller action

I think I must be doing something silly, but if anyone can point out what I'm doing wrong, that would be great!
I have a form in a Razor view like:
#using (Html.BeginForm("Index", "SiteKPIs", FormMethod.Get))
{
<table>
<tr>
<td style="margin-right: 25px;">Site</td>
<td>#Html.DropDownList("siteId", new SelectList(ViewBag.Sites, "SiteID", "SiteDisplayName"))</td>
</tr>
<tr>
<td>Range</td>
<td>
<select id="rangeId"><option value="0">test</option></select>
<input class="btn btn-primary btn-xs" type="submit" value="Go" />
</td>
</tr>
</table>
}
The signature of my controller action looks like:
public ActionResult Index(int? siteId, int? rangeId)
I get a value for siteId, but rangeId is null. What am I doing wrong?
If you want to use plain html select, then you need to set Name attribute of your select to expected param name in your Controller Action:
<select id="rangeId" name="rangeId"><option value="0">test</option></select>
But my suggestion is to use DropDownList helper either way.
I believe it would be better to use a HtmlHelper for a dropdownlist, like so:
<div class="form-group">
<label for="range">Range</label><br />
#Html.DropDownList("RangeID", null, "Please select...",
new { #class = "form-control", id = "rangeID" })
</div>
Then, inside your controller, you can populate the dropdownlist there, with something like:
List<SelectListItem> options = new List<SelectListItem>();
options.Add(new SelectListItem { Text = "Test", Value = "0"});
// and so on...
You can then use a ViewBag in your controller and make it equal to this list, like so:
ViewBag.RangeID = options;

Data not loading on partial view, MVC

I am doing work on form where user can enter a customer record....View is scaffold with Create controller.
On 'Create' View, user can enter 'engineNo' to check its details which passes to another action "CheckRecord",,it can be seen from view...
<form>
<input type="text" id="enginNo" />
<input type="button" value="search" id="btnSearch" />
</form>
#using (Html.BeginForm("Index","Home",FormMethod.Get))
{
#Html.AntiForgeryToken()
<div id="info">
#{Html.RenderAction("CheckRecord","Sales");}
</div>
some create fields
}
The Create and "CheckRecord" actions are,,
public ActionResult Create()
{
ViewBag.CustomerId = new SelectList(db.CustomersDMs, "CustomerId", "Name");
ViewBag.SMClientBranchId = new SelectList(db.SMClientBranchesDMs, "SMClientId", "Name");
ViewBag.EngineNumber = new SelectList(db.StockDMs, "EngineNumber", "ChasisNumber");
return View();
}
public ActionResult CheckRecord(string enginNo)
{
var results = db.StockDMs.Where(c=>c.EngineNumber ==enginNo);
return PartialView("_part",results);
}
And my partialview,,,
#model IEnumerable<SM.CRM.AutosLoan.Models.Core.DomainModels.StockDM>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.AutoCompanyBrand.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.SMClientBranch.Name)
</td>
}
My problem is, the partial view is rendered correctly but the Model of partial view doesn't have value,,,Why is that, i am doing something wrong...Please help,,,Thanks for your time
(Posting this as answer since I mentioned it in comments and that's not the correct place)
Your action CheckRecord(string enginNo) takes an argument of enginNo, but you're calling it without any argument. That in turn means your db lookup will most likely not return any results, unless you get results on..
var results = db.StockDMs.Where(c => c.EngineNumber == null);
Make sure the action gets a valid argument, for example:
#{ Html.RenderAction("CheckRecord", "Sales", new { enginNo = "abc123" }); }

ASP.NET MVC FormCollection TextArea

I have a textarea that represents a description field. The descriptions have commas so when trying to split the field's descriptions the data is not parsed correctly. How can I get each row's description correctly.
var DescList = FormValues["Item.Description"].Split(',').Select(item => item).ToList<string>();
//will not work for obvious reasons. Comma delimited FormCollection has commas to identify separate row data.
It seems like Microsoft designed the FormsCollection without the textarea control in mind. A text area with commas will not work when trying to access each value. What is interesting is that the _entriestables property has it in the perfect format but they chose to make it a private property. Very frustrating.
`
Here is the important part of my viewmodel.
public class TenantViewModel
{
public Tenant Tenant { get; set; }
public Site Site { get; set; }
}
My view is populated like this:
if (Model != null && Model.Tenant != null && Model.Tenant.Site != null && Model.Tenant.Site.Count() > 0)
{<div class="detailsbox_view">
<table id="tblTenantSites">
<tr>
<th>#Html.LabelFor(item => item.Site.Title)</th>
<th>#Html.LabelFor(item => item.Site.Description)</th>
</tr>
#foreach (var Item in Model.Tenant.Sites)
{
<tr>
#Html.HiddenFor(modelItem => Item.SiteId)
<td>
#Html.EditorFor(modelItem => Item.Title)
</td>
<td>
#Html.TextAreaFor(modelItem => Item.Description, new {#width="400" })
</td>
</tr> }
</table>
As you see this site table is a child of Tenant object. This child record does not get automatically updated using this method but the Tenant data does automatically get updated. This is the reason I tried the FormColelction instead.
Is there something I am missing to make this work?
try with this useful function
ValueProviderResult Match=FormCollection.GetValue("ValueProvider");
When you have multiple fields with the same name attribute, they'll come back into your FormCollection as an array. So upon posting a view like this:
<form action="/Home/MyAction">
<textarea id="row_one_description" name="description">
First row's description
</textarea>
<textarea id="row_two_description" name="description">
Second row's description
</textarea>
<input type="submit" value="Submit" />
</form>
you could do something like this in your action
[HttpPost]
public ActionResult MyAction(FormCollection collection)
{
var descriptionArray = collection["description"];
string firstRowDescription = descriptionArray[0];
string secondRowDescription = descriptionArray[1];
}
I must note that this is not the recommended way of dealing with posted data. You should instead be building your view using data from a view model and using strongly typed html helpers to render your controls. That way when you post, your action can take the ViewModel as a parameter. Its properties will be automatically bound and you will have a nice object to play with.
[HttpPost]
public ActionResult MyAction(MyViewModel viewModel)
{
foreach (var row in viewModel.Rows)
{
string description = row.Description;
}
}
EDIT
I'm still assuming a lot about your ViewModel but perhaps try this:
<table id="tblTenantSites">
<tr>
<th>#Html.LabelFor(model => model.Site.Title)</th>
<th>#Html.LabelFor(model => model.Site.Description)</th>
</tr>
#for (var i = i < Model.Tenants.Sites.Count(); i++) {
<tr>
#Html.HiddenFor(model => model.Tenants.Sites[i].SiteId)
<td>
#Html.EditorFor(model => model.Tenants.Sites[i].Title)
</td>
<td>
#Html.TextAreaFor(model => model.Tenants.Sites[i].Description, new { #width="400" } )
</td>
</tr>
}
</table>
You could also try ,
string Match=FormCollection.GetValue("ValueProvider").AttemptedValue;

Resources