Unable to pass view model list in CSHTML to controller - asp.net

I have a View Model, View, and Controller that works great displaying the data, but I cannot get the data entered in the form to save to the controller. I've tried using a list, array, and a list for the view model.
Here's my view model:
public class AssignedHostData
{
public int HostID { get; set; }
public string HostName { get; set; }
public bool Assigned { get; set; }
[DisplayName("Additional Details")]
[DataType(DataType.MultilineText)]
public string AddDetails { get; set; }
}
Here's the section of my view that displays the data:
<table class="table">
<tr>
#{
int cnth = 0;
List<Support_Web.Models.ViewModels.AssignedHostData> hosts = ViewBag.Hosts;
foreach (var host in hosts)
{
if (cnth++ % 1 == 0)
{
#:</tr><tr>
}
#:<td>
<input type="checkbox"
name="selectedHosts[#cnth].HostID"
id="selectedHosts_[#cnth]_HostID"
value="#host.HostID"
#(Html.Raw(host.Assigned ? "checked=\"checked\"" : "")) />
#host.HostID #: #host.HostName
#:</td>
#:<td>
<input type="text" name="selectedHosts[#cnth].AddDetails" id="selectedHosts_[#cnth]_AddDetails" value="#host.AddDetails" />
#:</td>
}
#:</tr>
}
</table>
And here're the parameters from my Edit controller. The selectedProducts list returns an empty list every time:
public async Task<IActionResult> Edit(int? id, string[] selectedProducts, List<HostCheckListItem> selectedHosts)

My view needed to be formatted this way:
#{
int cnth = 0;
List<Support_Web.Models.ViewModels.AssignedHostData> hosts = ViewBag.Hosts;
for (int i = 0; i < hosts.Count; i++)
{
<tr>
<td>
<input type="checkbox"
name="selectedHosts[#i].HostID"
value="#hosts[#i].HostID"
#(Html.Raw(hosts[#i].Assigned ? "checked=\"checked\"" : "")) />
#hosts[#i].HostName
</td>
<td>
<input type="text" name="selectedHosts[#i].AddDetails" value="#hosts[#i].AddDetails" />
</td>
</tr>}
}

If you want to transfer the modified model data list in the view to
the controller, you can use "asp-for" to bind in the form.
Please refer to the following for details:
public class ProductController : Controller
{
private readonly MydbContext _context;
public ProductController (MydbContext context)
{
_context = context;
}
public IActionResult Index()
{
ViewBag.Hosts = _context.AssignedHostData.ToList();
return View();
}
public async Task<IActionResult> Edit(List<AssignedHostData> Hosts)
{
List<AssignedHostData> selectedHosts = Hosts.Where(x => x.Assigned == true).ToList();
return View();
}
}
Edit.cshtml:
<form asp-controller="Product" asp-action="Edit" method="post">
<table class="table">
<tr>
#{
int cnth = 0;
List<WebApplication_core.Models.AssignedHostData> Hosts = ViewBag.Hosts;
for (int i = 0; i < Hosts.Count; i++)
{
<tr>
<td>
<input id="Hidden1" type="hidden" asp-for="#Hosts[i].HostID" />
<input type="checkbox" asp-for="#Hosts[i].Assigned" />
#Hosts[i].HostName
<input id="Hidden2" type="hidden" asp-for="#Hosts[i].HostName" />
</td>
<td>
<input type="text" asp-for="#Hosts[i].AddDetails" />
</td>
</tr>
}
}
</table>
<input id="Button1" type="submit" value="Edit" />
</form>
Here is the debug result:

Related

Trying to update my users password using AddPasswordAsync but passwords always left as null

So i've got 2 tables one for Editing user roles and one fore editing user accounts the roles table allows me to delete and add roles perfectly fine but i'm having trouble with updating passwords. The passwords seem to be getting deleted but not updated with the new one specified.
<br>
<h3>Roles Table</h3>
<table class="table table-striped">
<thead>
<tr> <th>Id</th><th>User Roles</th> </tr>
</thead>
<tbody>
#foreach (var roles in Model.roles)
{
<tr>
<td> #roles.Id</td>
<td> #roles.Name</td>
<td><a class="btn btn-sm btn-danger order-button float-right" asp-page="/ManageRoles" asp-route-id="Delete" asp-page-handler="Delete">Delete Roles </a></td>
</tr>
}
</tbody>
</table>
<form method="post">
<div class="flex">
<div>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
<div class="input-group">
<input type="text" class="form-control" asp-for="#Model.roleName">
</div>
</div>
</form>
<table class="table table-striped" style="margin-top: 100px;">
<thead>
<tr> <th>Id</th><th>User Account</th> </tr>
</thead>
<tbody>
#foreach (var users in Model.users)
{
<tr>
<td> #users.Id</td>
<td> #users.Email</td>
<td><a class="btn btn-sm btn-danger order-button float-right" asp-page="/ManageRoles" asp-route-id="DeleteUser" asp-page-handler="DeleteUser">Delete User </a></td>
<td>
<form method="post" asp-page-handler="Update" asp-route-id="#users.Id">
<div class="input-group">
<input type="text" class="form-control" asp-for="#Model.NewPassword">
<button type="submit" class="btn btn-default">Update Password </button>
</div>
</form>
</td>
</tr>
}
</tbody>
</table>
So heres my Page model class below
{
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<AppUser> _userManager;
public List <IdentityRole> roles { get; set; }
[BindProperty(SupportsGet = true)]
public string Id { get; set; }
[BindProperty]
public string roleName { get; set; }
public List <AppUser> users { get; set; }
[BindProperty]
public string userId { get; set; }
[BindProperty]
public string NewPassword { get; set; }
public AdminDashboardModel(RoleManager<IdentityRole> roleManager, UserManager<AppUser> userManager)
{
_roleManager = roleManager;
_userManager = userManager;
}
public void OnGet()
{
roles = _roleManager.Roles.ToList();
users = _userManager.Users.ToList();
}
public async Task <IActionResult> OnGetDeleteAsync()
{
var role = await _roleManager.FindByIdAsync(Id);
await _roleManager.DeleteAsync(role);
return RedirectToPage("/AdminDashboard");
}
public async Task<IActionResult> OnPostAsync()
{
if (roleName != null)
await _roleManager.CreateAsync(new IdentityRole(roleName.Trim()));
return RedirectToPage("/AdminDashboard");
}
public async Task<IActionResult> OnGetDeleteUserAsync()
{
var user = await _userManager.FindByIdAsync(Id);
await _userManager.DeleteAsync(user);
return RedirectToPage("/AdminDashboard");
}
public async Task<IActionResult> OnPostUpdateAsync()
{
var user = await _userManager.FindByIdAsync(Id);
await _userManager.RemovePasswordAsync(user);
await _userManager.AddPasswordAsync(user, NewPassword);
return RedirectToPage("/AdminDashboard");
}
}
}
Try it with this bit of code:
public async Task<IActionResult> OnPostUpdateAsync()
{
var user = await _userManager.FindByIdAsync(Id);
var token = await _userManager.GeneratePasswordResetTokenAsync(user)
var result = await _userManager.ResetPasswordAsync(user, token, NewPassword);
//validate result
if(result.Succeeded) {
return RedirectToPage("/AdminDashboard");
}
//handle errors
throw new Exception()
}
You don't actually need to validate the result, but things tend to go sideways. Always better to have error handling.
The generated token validates the action of changing the password against the system. If a user would want to reset their password, because they forgot theirs, you would also need to generate such a token, send it to their email and then let the user choose a new password.
Alternatively you could just use UserManager.UpdatePasswordHash:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.usermanager-1.updatepasswordhash?view=aspnetcore-5.0
var result = await _userManager.UpdatePasswordHash(user, NewPassword, true|false);

How to get data from multiple elements

i have next ViewModel:
public class ChatCreationViewModel
{
public string ChatName { get; set; }
public List<User> Friends { get; set; }
public List<(string, bool)> Partcipants { get; set; }
}
and next view:
#model GalkaNet.ViewModels.ChatCreationViewModel
<form method="post" asp-action="NewChat" asp-controller="Chat" enctype="multipart/form-data">
<h2>Создание беседы</h2>
<label>Chat name: </label> <input type="text" name="ChatName" placeholder="Name" value=""></p>
<p><input type="submit" value="Create" /></p>
#for(int i = 0; i < Model.Friends.Count; i++)
{
<div>
<label>#Model.Friends[i].NickName</label>
<p> #Html.EditorFor(Model => Model.Partcipants[i].Item2)</p>
</div>
}
</form>
</div>
The thing is: i want to create a list of elements with user names and checkboxes. Once the form is submitted i want to place values from each checkbox combined with id of user (in front of whose name this checbox was) into Partcipants list of my view model that is going to server. How should i do this?
Create a Model Like this
public string ChatName { get; set; }
public List<UserModel > Friends { get; set; }
Your User Model Properties(ID,UserName,isSelected)
Fill these properties from your db
Id = friend.Id;
UserName = friend.Name,
isSelected = false
Your View
#for (int i = 0; i < Model.Friends.Count; i++)
{
<div class="form-check m-1">
<input type="hidden" asp-for="#Model.Friend[i].Id " />
<input type="hidden" asp-for="#Model.Friend[i].Username" />
<input asp-for="#Model.Friend[i].isSelected" class="form-check-
input" />
<label class="form-check-label" asp-
for="#Model.Friend[i].isSelected">
#Model.Friend[i].Username </label>
</div>
}

Asp.net core razor pages [BindProperty] doesnt work on collections

Im trying to use [BindProperty] annotation in asp.net core razor pages in order to Bind an Ilist<T> collection of one of my model classes so i can edit some of them at once, but it doesnt work at all, every time in OnPostAsync function the collection is empty, and neither the changes that i made on data nor it default values wont post back to the server, but when its a singel object [BindProperty] works fine and the values post back and can be changed, i also tried wraping a collection (i.e list<T>) in an object but it didnt work either way, so is there any way for doing so or i should lets say send a edit request for every object in that collection and edit them one by one(which cant be done in razor pages easilly and need some ajax calls)??
For binding IList between RazorPage and PageModel, you will need to use Product[i].Name to bind property.
Here are complete steps.
Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
PageModel
public class IndexModel : PageModel
{
private readonly CoreRazor.Data.ApplicationDbContext _context;
public IndexModel(CoreRazor.Data.ApplicationDbContext context)
{
_context = context;
}
[BindProperty]
public IList<Data.Product> Product { get; set; }
public async Task OnGetAsync()
{
Product = await _context.Product.ToListAsync();
}
public async Task OnPostAsync()
{
var product = Product;
}
}
View
<form method="post">
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Product[0].Name)
</th>
<th></th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Product.Count(); i++)
{
<tr>
<td>
<input hidden asp-for="Product[i].Id" class="form-control"/>
<input asp-for="Product[i].Name" class="form-control" />
</td>
<td>
<a asp-page="./Edit" asp-route-id="#Model.Product[i].Id">Edit</a> |
<a asp-page="./Details" asp-route-id="#Model.Product[i].Id">Details</a> |
<a asp-page="./Delete" asp-route-id="#Model.Product[i].Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>

How to add a value to a class if the class contains LIST<class>

I have a class graf.
public class Graf
{
public List<Point> first { get; set; }
public List<Point> second { get; set; }
}
This class contains List
public class Point
{
public int x { get; set; }
public int y { get; set; }
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
I need to add a Point into class Graf from index.cshtml:
#model WebApplication2.Models.Graf
<table>
<tr>
<td></td>
<td>
<div class="item">
<label>Y</label>
<input name="Y11" value="#Model.first" /> --------??
</div>
</td>
</tr>
</table>
<input type="submit" value="Send" />
But i dont now how i can input into Graf class Point?
How can I do it?
Ok. So let's start from a client-side code.I suppose that you have a next Index.cshtml view.
<!-- You use this code to display data from your model -->
<table>
<tr>
<td></td>
<td>
<div class="item">
<label>Y</label>
<input name="Y11" value="#Model.first" /> --------??
</div>
</td>
</tr>
</table>
Than you need a code that post new Point object from your view to controller.It could be like something like this:
<form asp-controller="Home" asp-action="InsertPoint" method="post">
X value: <input type="text" name="x"><br>
Y value: <input type="text" name="y"><br>
<input type="submit" value="Submit">
</form>
In your controller you should create action with following signature
[HttpPost]
public async Task<IActionResult> InsertPoint(Point point)
{
//Validation and insertion to list
return View();
}
NB
It's not an ideal solution. You could perform this task in many different ways. My aim, is just to show you the basic idea how it could be done. If you need more information you could start from this article
And of course, keep in mind that google is your good friend.

Parameter not passed to controller - Spring

I am at wits end here. This seems a very trivial thing but its not working. I have a jsp page that passes back to the server a Long (tournamentId) and a list of Objects.
When I post the form, the list is passed properly, but the Long member comes back as null, even though I can see it was sent.
The jsp:
<form:form method="post" action="addBets" modelAttribute="gwbCollection">
<c:choose>
<c:when test="${gwbCollection.tournamentState == 'CLOSED_FOR_BETS'}">
<br>
</c:when>
</c:choose>
<input name="tournamentId" value="${gwbCollection.tournamentId}" type="hidden"/>
<table>
<tr>
<td>Side A:</td>
<td>Score A:</td>
<td>Side B:</td>
<td>Score B:</td>
</tr>
<c:forEach var="gwb" items="${gwbCollection.games}" varStatus="status">
<tr>
<td><input name="games[${status.index}].game.gameId" value="${gwb.game.gameId}" type="hidden"/>
<input name="games[${status.index}].userId" value="${gwb.userId}" type="hidden"/>
<input name="games[${status.index}].game.tournamentId" value="${gwb.game.tournamentId}" type="hidden"/>
<input name="games[${status.index}].bet.betId" value="${gwb.bet.betId}" type="hidden"/>
${gwb.game.sideA}</td>
<td><input name="games[${status.index}].bet.scoreA" value="${gwb.bet.scoreA}"/></td>
<td>${gwb.game.sideB}</td>
<td><input name="games[${status.index}].bet.scoreB" value="${gwb.bet.scoreB}"/></td>
</tr>
</c:forEach>
</table>
<c:choose>
<c:when test="${gwbCollection.tournamentState == 'OPEN_FOR_BETS'}">
<input type="submit" />
</c:when>
</c:choose>
</form:form>
The Controller:
#Controller
#SessionAttributes
public class BetController {
...
#RequestMapping(value = "/addBets", method = RequestMethod.POST)
public String addBet(#ModelAttribute("gwbCollection") GamesWithBetsCollection gwbCollection) {
List<Bet> bets = gwbUtil.getBets(gwbCollection);
...
}
And finally, GamesWithBetsCollection:
public class GamesWithBetsCollection {
private TournamentState tournamentState;
private Long tournamentId;
private List<GameWithBet> games;
public GamesWithBetsCollection() {
}
public List<GameWithBet> getGames() {
return games;
}
public void setGames(List<GameWithBet> games) {
this.games = games;
}
public TournamentState getTournamentState() {
return tournamentState;
}
public void setTournamentState(TournamentState tournamentState) {
this.tournamentState = tournamentState;
}
public Long getTournamentId() {
return tournamentId;
}
public void setTournamentId(long tournamentId) {
this.tournamentId = tournamentId;
}
}
Nickdos - WOW! That is the answer! Beautiful catch!
To recap - the field "tournamentId" is defined as Long (object), but the setter has long (primitive) as parameter. Changing it to Long(object) did the trick.
Thanks again Nickdos!

Resources