I am working on my first .NET MVC project and I am to a point where I want my form to iterate through a loop of exercises in a workout (can be any number of exercises). I have a foreach loop, and everything seems to work out ok, but when I check the database, it took only the entries for the first exercise, and applies that to all the entries. I am not sure where I am messing up, or misunderstanding my issue. Any advice in the right direction would be greatly appreciated.
VIEW:
@model WorkoutGenerator.ViewModels.AddRecordViewModel
<h1>Add Exercise Record</h1>
<form asp-controller="Record" asp-action="Add" method="post">
@foreach (var exercise in Model.Exercises)
{
<h4>@exercise.Exercise.Name</h4>
<div class="form-group">
<label asp-for="@Model.Sets"></label>
<input class="form-control" asp-for="@Model.Sets" />
<span asp-validation-for="@Model.Sets"></span>
</div>
<div class="form-group">
<label asp-for="@Model.Reps"></label>
<input class="form-control" asp-for="@Model.Reps" />
<span asp-validation-for="@Model.Reps"></span>
</div>
<div class="form-group">
<label asp-for="@Model.Weight"></label>
<input class="form-control" asp-for="@Model.Weight" />
<span asp-validation-for="@Model.Weight"></span>
</div>
<input type="hidden" name="ExerciseID" value="@exercise.ExerciseID" />
<input type="hidden" name="WorkoutID" value="@exercise.WorkoutID" />
<input type="hidden" name="OwnerID" value="@exercise.Exercise.OwnerId" />
}
<input type="submit" value="Add Exercise Record" />
</form>
VIEWMODEL:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using WorkoutGenerator.Models;
namespace WorkoutGenerator.ViewModels
{
public class AddRecordViewModel
{
[Required(ErrorMessage = "Please enter number of sets")]
public string Sets { get; set; }
[Required(ErrorMessage = "Please enter number of reps")]
public string Reps { get; set; }
[Required(ErrorMessage = "Please enter amount of weight")]
public string Weight { get; set; }
//Date Created
public DateTime DateCreated { get; set; }
//Link to user
public string OwnerId { get; set; }
//Link to Exercise
public int ExerciseID { get; set; }
//Link to Workout
public int WorkoutID { get; set; }
public IList<ExerciseWorkout> Exercises { get; set; }
public Workout Workout { get; set; }
public AddRecordViewModel() { }
}
}
Controller:
public IActionResult Add(int id)
{//Create form for each exercise to have sets reps and weight to submit
//!!!!!!!!!!!!!!TAKEN FROM WORKOUT CONTROLLER!!!!!!!!! MAY NEED CHANGING!!!!!!!!!!!!!!!!
string user = User.Identity.Name;
ApplicationUser userLoggedIn = context.Users.Single(c => c.UserName == user);
List<ExerciseWorkout> exercises = context
.ExerciseWorkouts
.Include(item => item.Exercise)
.Where(cm => cm.WorkoutID == id && cm.Workout.OwnerId == userLoggedIn.Id)//cm.Workout.OwnerId == userLoggedIn.Id returns list of owner specific workouts
.ToList();
Workout workout = context.Workouts.Single(m => m.WorkoutID == id);
AddRecordViewModel viewModel = new AddRecordViewModel
{
Workout = workout,
Exercises = exercises
};
return View(viewModel);
}
[HttpPost]
public IActionResult Add(AddRecordViewModel addRecordViewModel, int id)
{//Create records of exercise sets reps and weights to be added to database.
if (ModelState.IsValid)
{
string user = User.Identity.Name;
ApplicationUser userLoggedIn = context.Users.Single(c => c.UserName == user);
//exercises hopefully returns list of exercises from 'int id' parameter,
//which can then be used to iterate over each exercise put into record table
List<ExerciseWorkout> exercises = context
.ExerciseWorkouts
.Include(item => item.Exercise)
.Where(cm => cm.WorkoutID == id && cm.Workout.OwnerId == userLoggedIn.Id)
.ToList();
foreach (var exercise in exercises)
{
Record newRecord = new Record
{
Sets = addRecordViewModel.Sets,
Reps = addRecordViewModel.Reps,
Weight = addRecordViewModel.Weight,
DateCreated = DateTime.Now,//TODO Make this show only day not time of day
OwnerId = userLoggedIn.Id,//TODO Not Sure if creation of newRecord is correct.
WorkoutID = addRecordViewModel.WorkoutID,
FK_ExerciseID = addRecordViewModel.ExerciseID//TODO ExerciseID not entering into table.
};
context.Records.Add(newRecord);
context.SaveChanges();
}
return Redirect("/Record/Index");
}
else
{
return View(addRecordViewModel);
}
}
foreachloop to generate form controls for a collection- refer Post an HTML Table to ADO.NET DataTable. Second your binding to the same property (Setsetc) in each iteration - the ModelBinder will only bind the first value with a matching name. Its hard to understand what your trying to achieve here.Sets,RepsandWeightplus one for theExerciseWorkoutID (they are the only form controls you need), plus any other properties ofExerciseWorkoutyou want to display in the view, and you pass a collection of that model to the view, and post back the collection (refer also Post an HTML Table to ADO.NET DataTable