using html helpers and doing model binding in ASP.Net MVC for collections - asp.net-mvc-2

I have tried the sample in the following link
But when i bind the obtained data to a model using TryUpdateModel or using simple UpdateModel, i am getting null values for the properties that are left out from the UI.
For example, I left the DatePublished property in the UI for the Book Model, and i have some value that i got retrieved from the db. when i bind the posted data, i am not getting the existing value after using the updatemodel, instead i am getting null values.
why is this happening so?
EDIT
Model:
public class Book
{
public string Title { get; set; }
public List<Dates> mydates { get; set; }
}
public class Dates
{
public int datepub { get; set; }
public int daterel { get; set; }
}
Controller:
private Book init()
{
return new Book { Title = "Curious George", mydates = new List<Dates> { new Dates { datepub = 1, daterel = 2 }} };
}
public ActionResult Create(Book b1)
{
Book myBook = init();
TryUpdateModel(myBook);
return View("Index");
}
View
<!--
<form method="post" action="/Home/Create">
<input type="text" name="Title" value="Curious George" />
<input type="text" name="mydates[0].datepub" value="3" />
<input type="text" name="mydates[0].daterel" value="5" />
<input type="submit" />
</form>
-->
In this case [view having all fields, the try-update model works, but when i remove the
<!--<input type="text" name="mydates[0].daterel" value="5" />-->
and post the model binding removes the existing value of 2 set in the init method of the controller. how to avoid this problem.

Related

Data storing to the associated table in ASP.NET Core 5 MVC

I have three tables 'Movie', 'Genre' and it's associated table 'MovieGenre'. What I have to do is to combine the value of Movie table with Genre table and to display the Name value from Genre table with Movie table values, Using the third associated table MovieGenre.
public partial class Movie
{
public int MovieId { get; set; }
[Required]
public string Title { get; set; }
public string Description { get; set; }
public string Storyline { get; set; }
public int? Year { get; set; }
[DataType(DataType.Date)]
[Validators(ErrorMessage = "Date must be after or equal to current date")]
[Display(Name = "Release Date")]
public DateTime? ReleaseDate { get; set; }
public int? Runtime { get; set; }
[Display(Name = "Movie Type")]
[Column(TypeName = "nvarchar(20)")]
public MovieType MovieType { get; set; }
public ICollection<Genre> Genres { get; set; }
}
public class Genre
{
[Key]
public int GenreId { get; set; }
[Display(Name="Genre name")]
public string Name { get; set; }
public ICollection<Movie> Movies { get; set; }
}
public class MovieGenre
{
public int MovieGenreId { get; set; }
public int MovieId { get; set; }
public Movie Movie { get; set; }
public int GenreId { get; set; }
public Genre Genre { get; set; }
}
This is the Context page for this
public partial class MovieContext : DbContext
{
public MovieContext(DbContextOptions<MovieContext> options)
: base(options)
{
}
public virtual DbSet<Movie> Movies { get; set; }
public virtual DbSet<Genre> Genre { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasAnnotation("Relational:Collation", "SQL_Latin1_General_CP1_CI_AS");
modelBuilder.Entity<MovieGenre>().HasKey(mg => new { mg.MovieId, mg.GenreId });
modelBuilder.Entity<Movie>()
.HasMany(p => p.Genres)
.WithMany(p => p.Movies)
.UsingEntity(j => j.ToTable("MovieGenre"));
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
I used database-first method. I have created all other part and it is working correctly when I enter values to the table from database. But what I want to achieve is to store the foreign key values 'MovieId' and 'GenreId' to the 'MovieGenre' table when creating a new movie.
This is the code in create action method. How can I store the 'MovieId' and 'GenreId' to the 'MovieGenre' table from this code?
public async Task<IActionResult> Create([Bind("Title,Description,Storyline,Year,ReleaseDate,Runtime,MovieType")] Movie movies)
{
if (ModelState.IsValid)
{
_context.Add(movies);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewBag.GenreId = new MultiSelectList(_context.Genre.ToList(), "GenreId", "Name");
return View(movies);
}
Below is the create Action view code, here I used check boxes for the Genre, I want to enter the GenreId from this somehow to the associate table too .
<div class="row">
<div class="col-md-4 center">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Storyline" class="control-label"></label>
<input asp-for="Storyline" class="form-control" />
<span asp-validation-for="Storyline" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Year" class="control-label"></label>
<input asp-for="Year" class="form-control" />
<span asp-validation-for="Year" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Runtime" class="control-label"></label>
<input asp-for="Runtime" class="form-control" />
<span asp-validation-for="Runtime" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MovieType" class="control-label"></label>
<select asp-for="MovieType" class="form-control" asp-items="Html.GetEnumSelectList<MovieType>()"></select>
<span asp-validation-for="MovieType" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">Movie Genre</label>
<div class="col-md-10">
<div class="checkbox">
#foreach (var item in (MultiSelectList)ViewBag.GenreId)
{
<input type="checkbox" value="#item.Value" id="GenreId" name="GenreId" />#item.Text
}
</div>
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
<a class="btn btn-primary" asp-action="Index">Back to List</a>
</div>
</form>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
But what I want to achieve is to store the foreign key values 'MovieId' and 'GenreId' to the 'MovieGenre' table when creating a new movie.
You don't really need MovieId because it is auto-generated when SaveChanges{Async} is called, thus is available only after that call.
But you have explicit join entity MovieGenre with both navigation and explicit FK properties, so you can utilize the navigation property to specify the new Movie and let EF Core handle the actual FK for you. For the other FK you can just assign the existing Genre key (Id).
There are two ways to add a new MovieGenre record in this scenario. Both require creating MovieGenre instance and filling in the navigation and FK properties:
var movieGenre = new MovieGenre
{
Movie = movies,
GenreId = genreId // ?? - see below
};
Just not sure where GenreId is coming from here
Create([Bind("MovieId,Title,Description,Storyline,Year,ReleaseDate,Runtime,MovieType, GenreId")] Movie movies
since the Movie class doesn't have GenreId property, so you have to figure that out (eventually revisit the DTO/ViewModel used for communication with the controller).
Once you have that, you either add it to the Movie.MovieGenre collection:
movies.MovieGenre = new List<MovieGenre> { movieGenre };
_context.Add(movies);
or just add it directly to the db context (set):
_context.Add(movies);
_context.Add(movieGenre);
For this particular scenario the explicit join entity helps a lot. If you instead used the EF Core 5.0+ implicit join entity, implementing that operation would require a different approach.
I have found the right code for this. If anyone need the solution here my GitHub repo with the complete code.
https://github.com/Iam-Goku/ProjectMovie.git

How to View data from a different table and Insert data in to another table from one view page using Asp.net core MVC?

Scenario
I am going to create a simple interface to insert donations from students using Asp.net Core MVC and EF. According to the App, before entering donation I have to display student information (from Student table) by using student's Admission number (Add_No).
Then I have to insert donation data using the same view but using a different submit button to the table Welfare.
Problem:
I tried it as following ways as shown in my code blocks. Insertion is successful. But I couldn't show student info on the screen even though I retrieve them.
Code for View.cshtml
#model Student_Management.Models.Welfare
#{
ViewData["Title"] = "Wcreate";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h4 align="center">Welfare Donation Entry Form</h4>
<hr />
<table>
<tr>
<td>
#using (Html.BeginForm("getStudentInfo", "Payments", FormMethod.Post))
{
<div class="form-group">
<label asp-for="Add_No" class="control-label"></label>
<input asp-for="Add_No" class="form-control" id="t1" name="A" />
<span asp-validation-for="Add_No" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="getStudentInfo" class="btn btn-primary" id="btngetStudentInfo" />
</div>
}
</td>
<td>
<ul>
#if (ViewBag.students != null)
{
#foreach (var std in ViewBag.students)
{
<li>
#std
</li>
}
}
</ul>
</td>
</tr>
</table>
Code for Controller
public class PaymentsController : Controller
{
private readonly ConnectionString _context;
public PaymentsController(ConnectionString context)
{
_context = context;
}
public IActionResult Wcreate(Welfare welfare, string A)
{
int maxR_No = _context.Welfare.Max(p => p.R_No);
maxR_No = maxR_No + 1;
welfare.R_No = maxR_No;
if (ModelState.IsValid)
{
_context.Add(welfare);
_context.SaveChanges();
ModelState.Clear();
}
return View();
}
public ActionResult getStudentInfo(string A)
{
var items = _context.Students.Where(x => x.Add_No == A)
.Select(x => new
{
P1 = x.F_Name,
P2 = x.Class + "-" + x.ClassNo
});//.ToList();
ViewData["students"] = items;
return RedirectToAction("Wcreate");
}
namespace Student_Management.Models
{
public class Welfare
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int R_No { get; set; }
[Required]
public string Add_No { get; set; }
[Required]
public string Grade { get; set; }
[Required]
public string STClassNo { get; set; }
[Required]
public string Month { get; set; }
[Required]
public string Year { get; set; }
[Required]
public string Rs { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime Date { get; set; }
public string cmt { get; set; }
As hint given by Jasen, I modified my action method and view as follows.
<td>
#using (Html.BeginForm("getStudentInfo", "Payments", FormMethod.Post))
{
<div class="form-group">
<label asp-for="Add_No" class="control-label"></label>
<input asp-for="Add_No" class="form-control" id="t1" name="A" />
<span asp-validation-for="Add_No" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="getStudentInfo" class="btn btn-primary" id="btngetStudentInfo" />
</div>
}
</td>
<p>#TempData["info"]</p>
<p>#(TempData["info2"] +"-" + TempData["info3"])</p>
Action method
public ActionResult getStudentInfo(string A)
{
var items = _context.Students.Where(x => x.Add_No == A)
.Select(x => new
{
P1 = x.F_Name,
P2 = x.Class,
p3=x.ClassNo
}).ToList();
TempData["info"] = items[0].P1;
TempData["info2"] = items[0].P2;
TempData["info3"] = items[0].p3;
return RedirectToAction("Wcreate");
}

DropDown list [asp.net core 2.0]

Hi im having problem with my view
as you can see there is an input and a select option
<div class="form-group">
<label asp-for="DomWasLoc" class="control-label"></label>
<input asp-for="DomWasLoc" class="form-control" id="firstname" name="firstname" />
<select class="form-control"
id="name" name="name"
asp-items="#(new SelectList(ViewBag.LocationList, "LocName","LocName"))">
<option value="">- Select -</option>
</select>
<span asp-validation-for="DomWasLoc" class="text-danger"></span>
</div>
every time you select an item on on the dropdownList the selected item value is inputted in the input box.
but every time i click submit i always shows this error
AspNetCore._Views_DomesticWastes_Create_cshtml+d__25.MoveNext() in Create.cshtml
+
asp-items="#(new SelectList(ViewBag.LocationList, "LocName","LocName"))">
what should i do so that when i click submit it ignore the select and just add the data from input box?
thanks in advance guys! :)
There is no need to combine input and select for the same field DomWasLoc. You could bind the DomWasLoc directly to select.
A demo code like below:
<div class="form-group">
<label asp-for="DomWasLoc" class="control-label"></label>
#*<input asp-for="DomWasLoc" class="form-control" />*#
<select class="form-control"
asp-for="DomWasLoc"
asp-items="#(new SelectList(ViewBag.LocationList, "LocName","LocName"))">
<option value="">- Select -</option>
</select>
<span asp-validation-for="DomWasLoc" class="text-danger"></span>
</div>
Update:
Complte Code:
1. Model
public class Location
{
public int Id { get; set; }
public string LocName { get; set; }
}
public class DomesticWaste
{
public int Id { get; set; }
public string DomWasLoc { get; set; }
}
Controller
// GET: DomesticWastes/Create
public IActionResult Create()
{
List<Location> locationlist = new List<Location>() {
new Location{ Id = 1, LocName = "L1" },
new Location{ Id = 2,LocName = "L2" },
new Location{ Id = 3,LocName = "L3" }
};
ViewBag.LocationList = locationlist;
return View();
}
// POST: DomesticWastes/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,DomWasLoc")] DomesticWaste domesticWaste)
{
if (ModelState.IsValid)
{
_context.Add(domesticWaste);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(domesticWaste);
}

Dynamically updating html based on viewmodel

I'm currently trying to update some HTML based on two properties in my viewmodel which are hooked to two form groups. This is ASP.NET Core.
The two form groups:
<div class="form-group">
<label asp-for="ParticipantAmount">Antal deltagere</label>
<input asp-for="ParticipantAmount" onchange="Group()" class="form-control" type="number" max="1000" min="1" />
</div>
<div class="form-group">
<label asp-for="GroupAmount">Antal grupper</label>
<input asp-for="GroupAmount" class="form-control" onchange="Group()" type="number" max="20" min="1" />
<p id="GroupSize">0</p>
</div>
The properties in the viewmodel:
public int ParticipantAmount { get; set; }
public int GroupAmount { get; set; }
The javascript method in the script section:
function Group() {
var groups = Number('#Model.GroupAmount');
var participants = Number('#Model.ParticipantAmount');
var size = participants / groups;
document.getElementById('GroupSize').innerHTML = size;
}
When I try to log stuff to the console, it seems like the properties are not being updated in the viewmodel when the form inputs change.
I think I'm missing some basic knowledge about an important aspect of what I'm trying to do, but I can't seem to google the correct keywords. Hope you can lead me in the right direction.
Just to illustrate how AJAX calls work I made a minimalistic web application using jQuery which should be used for learning only, the code is not production-ready.
Razor view. I've added default values and IDs to your the input fields to identify them easier in JavaScript code.
<div class="form-group">
<label asp-for="ParticipantAmount">Antal deltagere</label>
<input asp-for="ParticipantAmount" onchange="Group()" class="form-control" type="number" max="1000" min="1" value="#Model.ParticipantAmount" id="ParticipantAmount" />
</div>
<div class="form-group">
<label asp-for="GroupAmount">Antal grupper</label>
<input asp-for="GroupAmount" class="form-control" onchange="Group()" type="number" max="20" min="1" value="#Model.GroupAmount" id="GroupAmount" />
<p id="GroupSize">0</p>`enter code here`
</div>
JavaScript code using jQuery to get/update values and make AJAX request.
<script>
function Group() {
$.ajax({
method: "POST",
url: "/Home/UpdateAmounts",
data: { ParticipantAmount: $('#ParticipantAmount').val(), GroupAmount: $('#GroupAmount').val() }
}).done(function (response) {
$('#GroupSize').html(response);
});
}
</script>
Controller and view model. Added a method for AJAX calls.
public class HomeController : Controller
{
public IActionResult Index()
{
return View(new ParticipantViewModel());
}
// You can call this method via AJAX to update your view model
[HttpPost]
public IActionResult UpdateAmounts(int participantAmount, int groupAmount)
{
// Validation for negative numbers, and groupAmount cannot be zero
if (participantAmount < 0 || groupAmount <= 0)
{
return Ok(0);
}
return Ok(participantAmount / groupAmount);
}
}
public class ParticipantViewModel
{
public int ParticipantAmount { get; set; }
public int GroupAmount { get; set; }
}
To make things nicer and more modern you could look into following example of a simple web application using database to store data, and using Knockout for UI.

asp.net mvc LabelFor. Error at use array in the name

public class ScheduleModel
{
[DisplayName("Example")]
public bool DayOff { get; set; }
}
public class SimpleModel
{
public List<ScheduleModel> ScheduleContainer { get; set; }
}
html
<%= Html.CheckBoxFor(m => m.ScheduleContainer[a].DayOff)%>
<%= Html.LabelFor(m => m.ScheduleContainer[a].DayOff)%>
It's code generate
<input id="ScheduleContainer_2__DayOff" name="ScheduleContainer[2].DayOff" type="checkbox" value="true" />
<input name="ScheduleContainer[2].DayOff" type="hidden" value="false" />
<label for="ScheduleContainer[2]_DayOff">Example</label>
Why lablefor == ScheduleContainer[2]_DayOff though should be ScheduleContainer_2__DayOff
This is a bug in MVC 2. It has been fixed in MVC 3.