How to change data in a table with a button click - c#

I would like to have a button where on click it changes a string value from null to any other text (for example 'yes'). I have a button in the view that calls an update method in the controller but I don't know what to put inside that update method.
In the view, this is the button ('item' refers to model, 'Activated' refers to a string column in that model which has a default value of null)
#model IEnumerable<ProjectMVC2.Models.ApplicationUser>
#foreach (var item in Model)
{
#if (item.Activated == null)
{
using (Html.BeginForm("Update", "User", new { id = item.Id })){
#Html.AntiForgeryToken()
<td class="button btn-default" align="center">
<input type="submit" value="Activate" class="btn btn-default" />
</td>
}
}
else if (item.Activated == "Yes")
{
<td class="c" align="center">
Activated already
</td>
}
I have the method in my controller
public class UserController : BaseController
{
ApplicationDbContext context;
public UserController()
{
context = new ApplicationDbContext();
}
// GET: User
public ActionResult Index()
{
var User = context.Users.ToList();
return View(User);
}
public ActionResult Update()
{
return RedirectToAction("Index")
}
}
In the method what should I put that will allow for the change to happen? Please if anyone can help, I will really appreciate it.

You'll have to updated your model itself:
public ActionResult Update()
{
var users = context.Users.ToList();
foreach(var user in users)
{
user.Activated = "Yes";
}
context.SaveChanges();
return RedirectToAction("Index")
}
However, this changes are persisted so if you come back to the page again you'll still see user.Activated = "Yes". If you need something temporary, I would suggest to use following code.
public ActionResult Update()
{
var users = context.Users.ToList();
foreach(var user in users)
{
user.Activated = "Yes";
}
return View("Index", users);
}

Use a for loop to take advantage of model binding to a collection.
This is accomplished by using an indexing expression e.g. Model[i].
Add a HiddenFor to tell the controller which users need to be activated.
#for (var i = 0; i < Model.Length; i++)
{
var item = Model[i];
#if (item.Activated == null)
{
using (Html.BeginForm("Update", "User", new { id = item.Id }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(_ => Model[i].Id)
<td class="button btn-default" align="center">
<input type="submit" value="Activate" class="btn btn-default" />
</td>
}
}
else if (item.Activated == "Yes")
{
<td class="c" align="center">
Activated already
</td>
}
The update method now accepts a collection of users to activate.
public ActionResult Update(IEnumerable<ApplicationUser> usersToActivate)
{
// Are there any user to active?
if (usersToActivate == null)
{
return View();
}
var userIds = usersToActivate.Select(u => u.Id).ToList();
var usersToUpdate = context.Users.Where(user => userIds.Contains(user.Id));
foreach (var userToUpdate in usersToUpdate)
{
userToUpdate.activated = "Yes";
}
context.SaveChanges();
return RedirectToAction("Index")
}

Related

HttpPost method is not called

when I run my code it seems that my HttpGet method works fine. But when I try to return the value to my HttpPost action it just runs my HttpGet method and then I get an "NullReferenceException" error.
Here is my code.
My Actions in my controller:
[HttpGet]
public IActionResult AddMovie(int? id)
{
List<Movie> movieList = new List<Movie>();
movieList = dbContext.Movies.ToList();
AddMovieViewModel viewModel = new AddMovieViewModel()
{
movies = movieList,
customer = dbContext.Customers.Where(s => s.CustomerId == id).FirstOrDefault()
};
return View(viewModel);
}
[HttpPost]
public IActionResult AddMovie (int id,int cid)
{
Customer customer = dbContext.Customers.Where(s => s.CustomerId == cid).FirstOrDefault();
Movie movie = dbContext.Movies.Where(s => s.MovieId == id).FirstOrDefault();
customer.BorrowingMovies.Add(movie);
customer.BorrowingMovies.Add(movie);
return View();
}
And here my view
#model MovieExampleAppDotNetCore.ViewModels.AddMovieViewModel
#{
ViewData["Title"] = "AddMovie";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>AddMovie</h1>
<label>Add movie for: #Model.customer.Name</label>
<table style="width:100%" class="table table-bordered table-hover">
<tr>
<th>Name</th>
</tr>
#foreach (var movie in Model.movies)
{
<tr>
<td>#movie.Name</td>
<td>
#Html.ActionLink("Select", "AddMovie", new { id = movie.MovieId, cid = Model.customer.CustomerId })
</td>
</tr>
}
</table>
I hope someone can help me with my problem.
The HTML ActionLink does not work with POST so is hitting your GET method. See this question.
To overcome and resolve your issue, wrap the button in a Form, something like this should do the trick.
#using (Html.BeginForm("AddMovie", "ControllerName", new { id = movie.MovieId, cid = Model.customer.CustomerId }, FormMethod.Post))
{
<button class="btn btn-primary" type="submit">
Add Movie
</button>
}

C# .Net MVC- How to get button onclick to updata string data in database

I want to create a button that changes a string data in the database. Currently in the database, there is a table for AspNetUsers which contains a string column called Activated (default value is null). What I would like to happen is when a button is clicked, that null value becomes the value 'Yes' on buttonclick. How do I go about doing this with MVC? Thanks
This is the button Im calling in the view
#model IEnumerable<ProjectMVC2.Models.ApplicationUser>
#foreach (var item in Model)
{
#if (item.Activated == null)
{
using (Html.BeginForm("Update", "User", new { id = item.Id })){
#Html.AntiForgeryToken()
<td class="button btn-default" align="center">
<input type="submit" value="Activate" class="btn btn-default" />
</td>
}
}
else if (item.Activated == "Yes")
{
<td class="c" align="center">
Activated already
</td>
}
This is the controller. There is a method called Update which I am calling for the button in the view
public class UserController : BaseController
{
ApplicationDbContext context;
private SqlConnection update;
private SqlCommand set;
public UserController()
{
context = new ApplicationDbContext();
}
// GET: User
public ActionResult Index()
{
var User = context.Users.ToList();
return View(User);
}
public ActionResult Update(int id)
{
update = new SqlConnection(#"Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=C:\Users\WinUser\Desktop\ProjectMVC2\ProjectMVC2\App_Data\aspnet-ProjectMVC2-20180223023456.mdf;Initial Catalog=aspnet-ProjectMVC2-20180223023456;Integrated Security=True");
update.Open();
set = new SqlCommand("UPDATE AspNetUsers SET Activated = 'Yes' ");
return RedirectToAction("Index");
}
}
This Update method does not work. How can i fix this please? I appreciate any help. Thank you very much

How to update the model data changed inside the view

I have a list of categories that I want to edit. I can successfully pass them into the controller.
public ActionResult Edit()
{
if (!IsAdmin) return RedirectToAction("Index", "Home");
ViewBag.IsAdmin = IsAdmin;
var categories = _model.Categories.ToList();
return View(categories);
}
Then I have a view to edit the names of the categories.
#using MyStructures.Models
#model List<MyStructures.Models.Category>
#using (Html.BeginForm("Update", "Categories", FormMethod.Post))
{
foreach (var category in Model)
{
var categoryToEdit = #category;
<table>
<tr>
<td>Category Number: #category.Id</td>
<td>#Html.EditorFor(x => categoryToEdit.Name)</td>
<td colspan="2">
<input type="submit" value="Save"
formaction=#Href("~/Categories/Update/") />
</td>
</tr>
</table>
}
<input type="submit" value="Cancel"
formaction=#Href("~/Home/Index/") />
}
How do I pass the updated value to the "Update" controller? The value of Category obj is null.
[HttpPost]
public ActionResult Update(Category obj)
{
var existing = _model.Categories.Find(obj.Id);
existing.Name = obj.Name;
_model.SaveChanges();
return RedirectToAction("Index", "Home");
}

IEnumerable<Model> returning null

I am having difficulty passing an IEnumerable as a model. The data is populating a form on one page - and doing so correctly. Upon submission the model returns as null.
I've seen various posts on this and they mostly reference naming-conventions so I have attempted different methods of naming the parameters to try to avoid any confusion in the model binding.
I have also tried various models and helpers to try and pass the data and all have the same result.
Current implementation:
Models:
public class UserProfileListModel
{
public IEnumerable<UserProfileViewModel> UserProfileViewModels { get; set; }
}
public class UserProfileViewModel
{
public UserProfile UserProfile { get; set; }
public Role UserRole { get; set; }
public Team UserTeam { get; set; }
public Scope UserScope { get; set; }
}
View:
#model Project.WebUI.Models.UserPRofileListModel
SNIP
<fieldset>
<legend>Administrate Users:</legend>
<table class="adminTbl">
<thead>
<tr>
<th>UserName:</th>
<th>Role:</th>
<th>Team:</th>
<th>Scope:</th>
<th>Update:</th>
<th>Delete:</th>
</tr>
</thead>
<tbody>
#{foreach (var user in Model.UserProfileViewModels)
{
<tr>
<td>
<p>#user.UserProfile.UserName
#{if (!user.UserProfile.Membership.IsConfirmed)
{
using (Html.BeginForm("Confirm", "Account", FormMethod.Post, null)){
#Html.AntiForgeryToken()
#Html.Hidden("Token", user.UserProfile.Membership.ConfirmationToken)
#Html.Hidden("Name", user.UserProfile.UserName)
}
<input type="submit" value="Confirm" />}
}
</p>
</td>
#{using (Html.BeginForm("SaveUserChanges", "Account", FormMethod.Post, null))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(u => user.UserProfile)
if (user.UserProfile.UserName != User.Identity.Name && user.UserProfile.Membership.IsConfirmed)
{
<td>
#Html.DropDownListFor(u => user.UserRole, Project.WebUI.Controllers.AccountController.RoleList, new { #class = "formdrop" })
</td>
<td>
#Html.DropDownListFor(u => user.UserTeam, Project.WebUI.Controllers.AccountController.TeamList, new { #class = "formdrop" })
</td>
<td>
#Html.DropDownListFor(u => user.UserScope, Project.WebUI.Controllers.AccountController.ScopeList, new { #class = "formdrop" })
</td>
<td>
<input type="submit" value="Save Changes" onclick="return confirm('Are you sure you wish to update this user? ')" />
</td>
}
else
{
/*If user is self or not yet confirmed these are here to buffer the delete button into the last cell*/
<td></td>
<td></td>
<td></td>
<td></td>
}
}
}
<td>
#Html.ActionLink("Delete", "Delete", new { user.UserProfile.UserId }, new
{
onclick = "return confirm('Warning: Action cannot be undone. Are you sure you wish to permanently delete this entry?')"
})
</td>
</tr>
}
}
</tbody>
</table>
</fieldset>
Controller:
Populate View:
public ActionResult AdministrateUsers()
{
populateLists();
var query = repository.UserProfiles.OrderBy(e => e.UserName);
List<UserProfileViewModel> list = new List<UserProfileViewModel>();
foreach(UserProfile up in query)
{
UserProfileViewModel vm = new UserProfileViewModel() { UserProfile = up };
list.Add(vm);
}
UserProfileListModel models = new UserProfileListModel()
{
UserProfileViewModels = list.OrderBy(up => up.UserProfile.UserName)
};
return View(models);
}
Accept Post:
public ActionResult SaveUserChanges(UserProfileListModel model)
{
foreach (UserProfileViewModel upvm in model.UserProfileViewModels)
{
UserProfile up = new UserProfile()
{
UserId = upvm.UserProfile.UserId,
UserEmail = upvm.UserProfile.UserName,
UserName = upvm.UserProfile.UserName
};
if (ModelState.IsValid)
{
repository.SaveUserProfile(up);
}
else
{
return View(model);
}
}
return RedirectToAction("Index", "Admin");
}
The code does still need a lot of work but I can't get past getting the model back to the controller on post. I have also tried returning the UserProfileViewModel instead of the entire list.
Can anyone tell what I am doing wrong?
Thanks!
You have a lot of invalid html including form elements as child elements of tr elements and duplicate id attributes. If you want to post back UserProfileListModel then you need a single form element and use an EditorTemplate or a for loop (not foreach) to render the controls so they are correctly named with indexers.
You are also trying to bind your dropdown lists to complex objects (for example UserProfile, Role etc.). <select> elements (and all form controls) only post back key/value pairs so you need to bind to a value type (for example UserProfile.UserId).
Your SaveUserChanges() post method is also trying access properties of UserProfile but you don't even have controls for properties of UserProfile in the form that post back to this method (for example UserId = upvm.UserProfile.UserId, UserEmail = upvm.UserProfile.UserName, ...) so they will always be null.
You probalby need to bind properties in POST method like here:
public ActionResult Create([Bind(Include = "Id,Subject,Text,IsImportant")] Announcment announcment) {... }
So it will be:
public ActionResult SaveUserChanges([Bind(Include = "UserProfile,Role,UserTeam,UserScope")]UserProfileListModel model)
Have you specified your action method is for HTTP Post? And change your action method to accept UserProfileViewModels instead.
[HttpPost]
public ActionResult SaveUserChanges(UserProfileViewModels model)
{
You are also only posting back one model: UserProfileViewModels.
You have your form in your foreach loop, so each UserProfileViewModels has its own form. If you want to change it to post back your UserProfileListModel, move
#{using (Html.BeginForm("SaveUserChanges", "Account", FormMethod.Post, null))
outside of your foreach.

TempData Message in different tab

i want in my page after an action like RegisterUser give a message to client for result.so i use TempData(becase i use RedirectToAction method i cant use viewbag).my problem is that if user open another tab in same time message will show in another tab(any page it can be).how can i solve that??
#using (#Html.BeginForm("RegisterUser", "UserManagement", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.Partial("_RegisterPagesMessage")
<table class="Registertbl">
<tr>
<td>نام*</td>
<td> #Html.TextBoxFor(m => m.FName, new { maxlength = 20})<br />
</td>
<td>سمت*</td>
<td>#Html.TextBoxFor(m => m.Post, new { maxlength = 200})</td>
</tr>
</table>
<br />
<input type="submit" value="Insert" class="insertBtn" />
#Html.ActionLink("back", "ViewUserList", "UserManagement")
}
//_RegisterPagesMessage
#if (TempData["MessageResult"] == null)
{
<div id="ErrorContent" class="msg-Red" style="display: none;"></div> <br />
}
else
{
<div id="ErrorContent" class="#TempData["cssClass"]" >
#Html.Label(TempData["MessageResult"] as string)
</div> <br />
}
//Controller
[HttpGet]
public ActionResult RegisterUser()
{
return View(new User());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult RegisterUser(Common.UsersManagement.Entities.User model)
{
SetUserManagement();
var Result = userManagement.RegisterUser(model);
SetMessage(Result.Mode.ToString());
if (Result.Mode == Common.Extensions.ActionResultMode.Successfully)
{
return RedirectToAction("RegisterUser");
}
// if not Successfull
return View(model);
}
protected void SetMessage(string Mode)
{
var messageResult = XmlReader.FindMessagekey(Mode);
TempData["MessageResult"] = messageResult.MessageContent;
TempData["cssClass"] = messageResult.cssClass;
}
Easy solution. In your RegisterUser controller method check for a value in TempData and transfer it to ViewData, then have the View check the ViewData, which only survives for that one view.
[HttpGet]
public ActionResult RegisterUser()
{
if( TempData.ContainsKey( "MessageResult" )
{
ViewData["MessageResult"] = TempData["MessageResult"];
ViewData["cssClass"] = messageResult.cssClass;
}
return View(new User());
}
Now in the view use ViewData instead of TempData.

Categories

Resources