How to pass value of htmlhelper from table (view) to controller? - c#

I want to update only a single value from a row on click of the update button against a specific ID, the id is available at controller but the updated value field is not available so when I press the update button controller updated the database but store a null value in DB.
//View Code
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(m => item.Id)
</td>
<td>
#Html.DisplayFor(m => item.TagName)
</td>
<td>
#Html.DisplayFor(m => item.TagCategory)
</td>
<td>
#Html.TextBoxFor(m => item.TagValue, new { htmlAttributes = new { #class = "form-control" } })
</td>
<td>
#Html.ActionLink("Update", "Update", new { id = item.Id}, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</tbody>
//Controller Code
public ActionResult Update([Bind(Include = "Id,TagValue") ]Tag tags)
{
var data = db.Tags.Where(x => x.Id == tags.Id).FirstOrDefault();
if (data!=null)
{
data.TagValue = tags.TagValue;
db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}

So right now your update button makes a GET request. The database is storing null because when you create the ActionLink you are not passing in the TagValue as a parameter because with GET requests the variables need to be passed in the url. You could add the TagValue as a parameter like this
#Html.ActionLink("Update", "Update", new { id = item.Id, TagValue = item.TagValue }, new { htmlAttributes = new { #class = "form-control" } })
I don't think this will work the way you want though because I am assuming you want the user to be able to change the value of TagValue and then hit update to save the value. What this solution does is it just grabs the original value of whatever TagValue is.
If you want to be able to edit the TagValue and then submit you could change your html to look like this
<tbody>
#foreach (var item in Model)
{
<tr>
<form action="#Url.Action("Update")" method="post">
<td>
#Html.HiddenFor(m => item.Id, new { #Name = "Id" })
#Html.DisplayFor(m => item.Id)
</td>
<td>
#Html.DisplayFor(m => item.TagName)
</td>
<td>
#Html.DisplayFor(m => item.TagCategory)
</td>
<td>
#Html.TextBoxFor(m => item.TagValue, new { #class = "form-control", #Name="TagValue" })
</td>
<td>
<input type="submit" value="Update" />
</td>
</form>
</tr>
}
This will create a form for every Tag you have and then generate a POST request when you hit submit.
vikscool's comment is also another good solution you could use.

You just need to Bind your View with The table from which you are sending and receiving data and you can do it with Html Begin Forms

Related

why Html.checkboxfor is not sends or changes the boolean value of the model?

I have a simple project that allows the customer to select multiple users and remove them from the DataBase. But the check boxes are not change or sent the isSelected variable which is tells the server that which users are have to be removed.
Client Side:
#model IEnumerable<WebApplication1.Models.User>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#{
int i;
}
<h2><strong>جدول</strong></h2>
#using (Html.BeginForm())
{
<p>
#Html.ActionLink("اضافه کردن کاربر", "Create"
, null, new { #class = "btn btn-success", #id = "btnCreate" })
<input type="submit" class="btn btn-danger hidden" id="btnMultipleDelete" value="حذف کاربران" />
</p>
<table class="table" border="0" style="user-select: none;">
<tr>
<th>
<strong>نام</strong>
</th>
<th>
<strong>نام خانوادگی</strong>
</th>
<th>
<strong>سنّ</strong>
</th>
<th></th>
</tr>
#for(i = 0; i < Model.Count(); i++)
{
<tr class="mainList" id="tr-#i"
onmouseover="changeCurrentRow(this.id), mouseIn(true)"
onmouseout="clearCurrentRow(), mouseIn(false)">
<td class="IndexInfo">
<span class="IndexText">
#Html.DisplayFor(modelItem => modelItem.ToList()[i].User_FirstName)
</span>
</td>
<td class="IndexInfo">
<span class="IndexText">
#Html.DisplayFor(modelItem => modelItem.ToList()[i].User_LastName)
</span>
</td>
<td class="IndexInfo">
<span class="IndexText persianNumber">
#Html.DisplayFor(modelItem => modelItem.ToList()[i].User_Age)
</span>
</td>
<td align="center" style="flex:3">
#Html.ActionLink("ویرایش", "Edit", new { id = Model.ToList()[i].User_Id },
new { #class = "btn btn-primary", #id = "btnEdit-" + i })
#Html.ActionLink("مشاهده", "Details", new { id = Model.ToList()[i].User_Id },
new { #class = "btn btn-secondary", #id = "btnDetail-" + i })
#Html.ActionLink("حذف", "Delete", new { id = Model.ToList()[i].User_Id },
new { #class = "btn btn-warning", #id = "btnDelete-" + i })
#Html.HiddenFor(u => u.ToList()[i].User_Id)
#Html.HiddenFor(u => u.ToList()[i].User_FirstName)
#Html.HiddenFor(u => u.ToList()[i].User_LastName)
#Html.HiddenFor(u => u.ToList()[i].User_Age)
#Html.CheckBoxFor(u => u.ToList()[i].IsSelected, new { #class = "", #id = "cb-" + i })
</td>
</tr>
}
</table>
}
Server Side:
[HttpPost]
public ActionResult Index(IEnumerable<User> users)
{
if (users.Count() == 0)
{
return RedirectToAction("Index");
}
else
{
User user;
foreach (User u in users)
{
if (u.IsSelected)
{
user = db.Users.ToList().Find(x => x.User_Id == u.User_Id);
db.Users.Remove(user);
}
}
db.SaveChanges();
return RedirectToAction("Index");
}
}
notice that i have tried to put a hidden input for isSelected Variable But that still doesn't work
so, while no one cares about my question i found the solution by my self.
Controller can not receive the IsSelected property from the View because i have changed the identity of the checkbox like id,name and ...
that means if i dont change the the identities of the checkbox, the code will works but even this solution leads us to another problem that you no longer can find the checkbox by JS. so i dont give this answer green tick until someone finds a better way to fix it.
for this solution something that you have to do is change the check box code from this:
#Html.CheckBoxFor(u => u.ToList()[i].IsSelected, new { #class = "", #id = "cb-" + i })
to this:
#Html.CheckBoxFor(u => u.ToList()[i].IsSelected})
and it basically will works.

Create a button in MVC to hide labels and replace it with '******'

brief background, my application is a password manager and has a page where it displays labels,a services e.g. gmail and the password saved associated to it. I am trying to find a way to have a button or checkbox to show or hide the passwords so the user can see their passwords and then press a button to hide the passwords and mask them with a series of asterisks. I was hoping someone might have a jquery or razor idea to help em out?
<div class="panel-body">
<table class="table table-bordered table-responsive table-hover">
<tr>
<th>
#Html.DisplayNameFor(model => model.Website)
</th>
<th>
#Html.DisplayNameFor(model => model.Password)
</th>
<th>
#Html.DisplayNameFor(model => model.DateSaved)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Website)
</td>
<td>
#Html.DisplayFor(modelItem => item.Password)
</td>
<td>
#Html.DisplayFor(modelItem => item.DateSaved)
</td>
<td>
#Html.ActionLink(" Edit", "Edit", new { id = item.Id }, new { #class = "btn btn-primary btn-sm glyphicon glyphicon-pencil" })
#Html.ActionLink(" Details", "Details", new { id = item.Id }, new { #class = "btn btn-info btn-sm glyphicon glyphicon-eye-open" })
#Html.ActionLink(" Delete", "Delete", new { id = item.Id }, new { #class = "btn btn-danger btn-sm glyphicon glyphicon-trash" })
#Html.ActionLink(" Strength Check", "Index", "StrengthCheck", new { id = item.Id }, new { #class = "btn btn-warning btn-sm" })
</td>
</tr>
}
</table>
You could use a simple JQuery function to toggle the field between asterisks and the real password value. The trick will be making sure each password field is uniquely identifiable so you only show that one password when you click the button (I'm assuming there will be multiple passwords on the screen since it's in a table)
Change the foreach loop to this so you have access to the index. This gives you easy access to a unique value for each row:
foreach (var item in Model.Select((value, i) => new { i, value }))
{
// You access the values like this now
var value = item.value;
var index = item.i;
}
See this answer for reference (the answer beneath the approved answer): How do you get the index of the current iteration of a foreach loop?
In the html table put:
<td>
<span id="password-#item.i" style="display: none;">#item.value.Password</span>
<span id="hidden-password-#item.i">*******</span>
<button onclick="showHidePassword('#item.i')">Toggle</button>
</td>
And somewhere on the page add the following javascript:
function showHidePassword(i) {
if ($('#password-' + i).is(':visible')) {
$('#password-' + i).hide();
$('#hidden-password-' + i).show();
} else {
$('#password-' + i).show();
$('#hidden-password-' + i).hide();
}
}
Hope this helps. To make reusability easier you could move this to a html extension method depending on how you're building out the rest of the page

MVC viewmodel is null on post (editing multiple rows)

I'm new to ASP.NET MVC and have been searching for a solution to this problem for hours. I'm trying to create a site where admins can go in and approve registration user requests and also assign them to a user group. I'm getting data from multiple tables so I created a viewmodel.
I finally have the GET Edit controller working to display the data, but can't figure out how the POST Edit should work. When I was debugging, I realized that the viewmodel I was trying to return had only null values.
I'm not sure if there are many things wrong with this code or just one. On postback, I need to update some values in the Access table. If you need more information from me, just let me know. Thanks!
ViewModel:
using System.Collections.Generic;
using AccessRequests.Models;
using System.ComponentModel.DataAnnotations;
namespace AccessRequests.ViewModels
{
public class UserAccessData
{
public IEnumerable<User> Users { get; set; }
public IEnumerable<Access> Accesses { get; set; }
public IEnumerable<UserGroup> UserGroups { get; set; }
}
}
Controller:
// GET: Accesses/Edit/5
public ActionResult Edit(string brand, string group)
{
var viewModel = new UserAccessData();
viewModel.Users = db.Users
.Include(i => i.Accesses)
.OrderBy(i => i.UserName);
viewModel.UserGroups = db.UserGroups
.Where(i => i.Groups.Contains(group));
if (brand != null)
{
viewModel.Accesses = db.Accesses
.Include(x => x.User)
.Where(x => x.Brand.ToUpper() == brand);
}
return View(viewModel);
}
// POST: Accesses/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Access access, UserAccessData editaccess)
{
//code here
}
View:
#model AccessRequests.ViewModels.UserAccessData
#{
ViewBag.Title = "Edit";
}
<h2>Edit Access</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<table class="table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Company</th>
<th>Role</th>
<th>Region</th>
<th>User Group</th>
<th>Approve</th>
<th>Deny Reason</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Accesses)
{
#Html.HiddenFor(modelItem => item.UserName)
<tr>
<td>
#Html.DisplayFor(modelItem => item.User.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Company)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Role)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Region)
</td>
<td>
#Html.DropDownListFor(m => m.UserGroups, new SelectList(Model.UserGroups, "Groups", "GroupDescription"), "Please select a User Group")
</td>
<td>
#Html.DropDownListFor(modelItem => item.Approved, new SelectList(
new List<Object>{
new { value = 0 , text = "" },
new { value = "YES" , text = "YES" },
new { value = "NO" , text = "NO"}
},"value","text", 2))
</td>
<td>
#Html.EditorFor(modelItem => item.DenyReason, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(modelItem => item.DenyReason, "", new { #class = "text-danger" })
</td>
</tr>
}
</tbody>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
In order to post to a collection property, your field names need to be in the form of something like: Accesses[0].Approved, Accesses[1].Approved, etc. In order to achieve that, you need to use a for loop rather than foreach. You'll also need to change your property's type from IEnumerable<Access> to List<Access>.
#for (var i = 0; i < Model.Accesses.Count(); i++)
{
...
#Html.DropDownListFor(m => Model.Accesses[i].Approved, ...)
}
Also, bear in mind that only those properties which have actual HTML fields that participate in the the post will have values in your post action. Everything else will either be null or whatever default value the property may have. If you save an entity with properties that have been nulled out because they weren't posted, you will overwrite those properties in your database. You need to take care to either make sure all the necessary data comes through in the post or that you repopulate said data from the the database before attempting to save anything.

Passing the values of the DisplayFor to the model binder

I have the following view , which mainly view a list of items as a displayfor:-
#foreach (var item in Model.Resources) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.SystemInfo.MODEL)
</td>
<td>
#Html.DisplayFor(modelItem => item.RESOURCENAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.ResourceLocation.SiteDefinition.AccountDefinition.SDOrganization.NAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.ResourceLocation.SiteDefinition.SDOrganization.NAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.ComponentDefinition.COMPONENTNAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.ResourceState.STATEDESC)
</td>
<td id = "#item.RESOURCEID">
#using (Ajax.BeginForm("CreateOn","VirtualMachine", new AjaxOptions {
InsertionMode = InsertionMode.Replace,
UpdateTargetId = item.RESOURCEID.ToString() ,
LoadingElementId = "progress",
HttpMethod = "POST"})){
<span class="f">Hypervisor Server</span>
#Html.DropDownListFor(model =>model.VirtualMachine.ServerID, ((IEnumerable<t.Models.Server>)ViewBag.Servers).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.Technology.Tag),
Value = option.ServerID.ToString(),
Selected = (Model != null) && (Model.VirtualMachine != null) && (option.ServerID == Model.VirtualMachine.ServerID)
}), "Choose...")
#Html.ValidationMessageFor(model =>model.VirtualMachine.ServerID)
#Html.Hidden("IT360id", item.RESOURCEID)
#Html.Hidden("CustomerName",item.ResourceLocation.SiteDefinition.AccountDefinition.SDOrganization.NAME)
#Html.Hidden("SiteName",item.ResourceLocation.SiteDefinition.SDOrganization.NAME)
#Html.Hidden("ResourceName",item.RESOURCENAME)
<input type="submit" value="Add To" class="btn btn-primary"/>
}
But the user can read the data and chose to create the item on our database, using the ajax.beginform. But to do so I need to pass some values of the DisplayFor to the model binder. Currently I have added all the needed data as a hiddenfields, and then I will pass these values to my action method as follow:-
[HttpPost]
public ActionResult CreateOn(VirtualMachineOnIT360Only vm, long IT360id, string CustomerName, string SiteName, string ResourceName)
{
I found that my approach is not very reliable and I am trying to figure out a more reliable solution. So can anyone advice please?
Thanks
To post DisplayFor back, I prefer to use hiddenfor instead of hidden like this:
#Html.HiddenFor(item => item.RESOURCENAME);

Using one view for index and create in mvc4

I am trying to use one view in which I display current results which has the ability to add a new record. I looked at this post and also this post and pieced together something I think should work but it will not save to the database. Here is my view model:
public class LabIndexViewModel
{
public Lab Lab { get; set; }
public IEnumerable<Lab> Labs { get; set; }
}
And in my controller I have this in my index:
public ActionResult Index(int patid = 0, Lab lab = null)
{
ViewBag.Finalize = PatientSubmitted(patid);
ViewBag.DispPatientId = patid;
ViewBag.CheckButtonStatus = ButtonSubmitted(patid);
var labs = db.Labs.Where(l => l.PatientId == patid && l.Active);
LabIndexViewModel model = new LabIndexViewModel();
model.Labs = labs.ToList();
model.Lab = lab;
SetViewBagLists();
return View(model);
}
Then in my post where it will not save:
[HttpPost]
public ActionResult Create(LabIndexViewModel labindex)
{
ViewBag.DispPatientId = labindex.Lab.PatientId;
Lab lab = labindex.Lab;
try
{
lab.Active = true;
db.Labs.Add(lab);
db.SaveChanges();
return RedirectToAction("Index", "Lab", new { patid = lab.PatientId });
}
catch
{
ViewBag.Phase = new SelectList(StatusList(), "Text", "Value");
ViewBag.Name = new SelectList(db.LabOptions, "Test", "Value", lab.Name);
return View(lab);
}
}
Here is my partial where I submit the data in my view:
#model PamperWeb.Models.LabIndexViewModel
#using (Html.BeginForm("Create", "Lab")) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Lab</legend>
<tr>
<td>
#Html.DropDownList("Name", String.Empty)
#Html.ValidationMessageFor(model => model.Lab.Name)
</td>
<td>
#Html.EditorFor(model => model.Lab.Value)
#Html.ValidationMessageFor(model => model.Lab.Value)
</td>
<td>
#Html.EditorFor(model => model.Lab.Given)
#Html.ValidationMessageFor(model => model.Lab.Given)
</td>
<td>
#Html.EditorFor(model => model.Lab.TimeGiven)
#Html.ValidationMessageFor(model => model.Lab.TimeGiven)
</td>
<td>
#Html.DropDownList("Phase", String.Empty)
#Html.ValidationMessageFor(model => model.Lab.Phase)
</td>
#Html.HiddenFor(model => model.Lab.PatientId)
<td>
<input type="submit" value="Create" />
</td>
</tr>
</fieldset>
}
Anybody have any idea on how to make this work or have a good example?
I didn't realy understand all the question, but I saw something wrong there:
1) Yours PartialView must post a Lab, so make It strongly typed for Lab, because HTML Helpers will generate HTML that the default ModelBinder cannot process to build the model back in the server using LabIndexViewModel:
#model PamperWeb.Models.Lab
#using (Html.BeginForm("Create", "Lab")) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Lab</legend>
<tr>
<td>
#Html.DropDownList("Name", String.Empty)
#Html.ValidationMessageFor(model => model.Name)
</td>
<td>
#Html.EditorFor(model => model.Value)
#Html.ValidationMessageFor(model => model.Value)
</td>
<td>
#Html.EditorFor(model => model.Given)
#Html.ValidationMessageFor(model => model.Given)
</td>
<td>
#Html.EditorFor(model => model.TimeGiven)
#Html.ValidationMessageFor(model => model.TimeGiven)
</td>
<td>
#Html.DropDownList("Phase", String.Empty)
#Html.ValidationMessageFor(model => model.Phase)
</td>
#Html.HiddenFor(model => model.PatientId)
<td>
<input type="submit" value="Create" />
</td>
</tr>
</fieldset>
}
2) Change the controller Action Create to receive as parameter the posted Lab:
[HttpPost]
public ActionResult Create(Lab lab)
{
ViewBag.DispPatientId = Lab.PatientId;
try
{
lab.Active = true;
db.Labs.Add(lab);
db.SaveChanges();
return RedirectToAction("Index", "Lab", new { patid = lab.PatientId });
}
catch
{
ViewBag.Phase = new SelectList(StatusList(), "Text", "Value");
ViewBag.Name = new SelectList(db.LabOptions, "Test", "Value", lab.Name);
return View(lab);
}
}
3) Use the ViewModel created to display the labs! Thats the ViewModel master purpose, display complex types in the view! Any other opperation requires creation of a custom ModelBinder to interate throught the request and build the model back in the server.
Hopes this help you! I really got this from the question!
With the help of the comments I was able to figure out the issue. When I added the parameters to the html.beginform it no longer sent my url parameters with the patientid. Not sure why? My normal create view had this so my hidden parameter picked up the value. I ended up setting the value in my controller so the hidden parameter in the form was able to pick it up. Here is what my get index is now which resolved the issue:
public ActionResult Index(int patid = 0, Lab lab = null)
{
ViewBag.Finalize = PatientSubmitted(patid);
ViewBag.DispPatientId = patid;
ViewBag.CheckButtonStatus = ButtonSubmitted(patid);
var labs = db.Labs.Where(l => l.PatientId == patid && l.Active);
LabIndexViewModel model = new LabIndexViewModel();
if (lab == null)
lab = new Lab();
lab.PatientId = patid;
model.Labs = labs.ToList();
model.Lab = lab;
SetViewBagLists();
return View(model);
}
I also found this post helpful in that I found out I can specify a model to send to a partial.

Categories