Models not collecting values from View to HttpPost Controller Method - asp.net-mvc

I am currently trying to pass a list of models from my view to my controller. Currently, I am able to grab each model from the view and place them inside of a list and pass the list within the HttpPost controller ActionResult method. However when doing this, none of the models have their data within them as all of the models have their property values set to either 0 or null.
My code is as follows:
View:
#using (Html.BeginForm("SaveCarouselImageData", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="card shadow">
<div class="card-header border-0">
<div class="row align-items-center">
<div class="col">
<h3 class="mb-0">Homepage Carousel</h3>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table align-items-center table-flush">
<thead class="thead-light">
<tr>
<th></th>
<th scope="col">Image</th>
<th scope="col">Order Num</th>
<th></th>
</tr>
</thead>
<tbody id="carousel-content">
#for (int i = 0; i < Model.Count(); i++)
{
<tr>
<th><input type="number" value="#Model[i].getId()" name="id" hidden readonly /></th>
<th scope="row">
<img src="#Url.Content(Model[i].getImgLoc())" name="imgLoc" class="carousel-img-thumbnail" alt="Image" />
</th>
<td>
#Html.TextBoxFor(model => model[i].orderNum, Model[i].getOrderNum().ToString(), new { type = "number", name = "orderNum" })
</td>
<td>
<a class="btn btn-danger btn-lg btn-block openDeleteModal" data-toggle="modal" href="#deleteImageModal" data-id="#Model[i].getId()">
DELETE
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<div class="row form-button-group">
<div class="col-md-6 col-sm-12 form-button-padding">
<button type="button" class="btn btn-success btn-lg btn-block" data-toggle="modal" data-target="#addImageModal">
+ Add New Image
</button>
</div>
<div class="col-md-6 col-sm-12 form-button-padding">
<button type="submit" class="btn btn-primary btn-lg btn-block">
Save Changes
</button>
</div>
</div>
}
Controller:
// POST: Saves Carousel Image Data
[HttpPost]
public ActionResult SaveCarouselImageData(List<CarouselModel> images)
{
if (!checkLoginCredentials())
{
return RedirectToAction("Login", "Home");
}
else
{
List<CarouselModel> updatedModels = new List<CarouselModel>();
foreach (CarouselModel img in images)
{
CarouselModel dbModal = siteServices.getCarouselImageById(img.getId());
dbModal.setOrderNum(img.getOrderNum());
}
int result = siteServices.updateCarouselTable(updatedModels);
return RedirectToAction("HomepageCarousel", "Admin");
}
}
Model:
public class CarouselModel
{
[Display(Name="id")]
private int id;
[Display(Name = "imgLoc")]
private string imgLoc;
[Display(Name = "orderNum")]
public int orderNum;
public int getId()
{
return this.id;
}
public string getImgLoc()
{
return this.imgLoc;
}
public int getOrderNum()
{
return this.orderNum;
}
public void setId(int id)
{
this.id = id;
}
public void setImgLoc(string imgLoc)
{
this.imgLoc = imgLoc;
}
public void setOrderNum(int orderNum)
{
this.orderNum = orderNum;
}
}
Again, the models themselves are currently getting passed from the View and into the List for the SaveCarouselImageData method, but all of their property values are null or 0.
Please assist.

all of their property values are null or 0.
I think that's because your CarouselModel's properties lack get;set; which the ASP.NET MVC Binding makes use of by default.
During POST, the binder will try to bind the form values to that of your model, but since it lacks accessors, it can't set any of the values that comes from the form.
Besides that, they're both specified to be Private which can only be accessed within the class, you should use Public if you want them to be set externally.
The easiest solution is to make them Public and add the accessors get;set;:
[Display(Name="id")]
public int id {get;set;}
[Display(Name = "imgLoc")]
public string imgLoc {get;set;}
[Display(Name = "orderNum")]
public int orderNum {get;set;}
If you still want ID and ImgLoc to remain Private then, you could do something like this;
private int _id {get;set;}
private string _imgLoc {get;set;}
[Display(Name = "orderNum")]
public int orderNum;
[Display(Name="id")]
public int id{
get{
return this._id;
}
set{
this._id = value;
}
}
[Display(Name = "imgLoc")]
public string imgLoc{
get{
return this._imgLoc;
}
set{
this._id = value;
}
}
Then change your HTML input fields to use the public properties.

Related

Razor pages identity profile image

I am learning razor pages and followed several decent examples on Microsoft to create a very rudimentary application. I was wondering if anyone had any resources on how I could add a profile image to razor pages. I am scaffolding identity in Visual studio but nowhere can I find any tutorial on how to integrate an image.
I do have a simple model and controller that will save a file path to the DB but fail to integrate this to the razor templates.
If anyone has any resources for what I am looking to learn I would greatly appreciate it. Apologies if this is very basic, I'm probably not using the correct terminology to search in Google.
identity user in the Db.
I do have a simple model and controller that will save a file path to
the DB but fail to integrate this to the razor templates.
Model:
Let's assume you have Identity base Model as below:
public class IdentityUser
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Now you would like to add profile picture functionality there:
So you could extent your class as below:
public class IdentityExtendedModel : IdentityUser
{
public string ImageName { get; set; }
public string ProfilePictureLocation { get; set; }
}
Identity View Model:
We don't want to manipulate our identity Model directly while submitting our form inputso I will use viewModel
public class IdentityViewModel
{
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Username")]
public string Username { get; set; }
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
[Display(Name = "Profile Picture")]
public byte[] ProfilePicture { get; set; }
}
View Upload Profile Picture:
#model DotNet6MVCWebApp.Models.IdentityViewModel
<form asp-action="CreateIdentityProfilePic" method="post" enctype="multipart/form-data">
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="FirstName"></label>
<input asp-for="FirstName" class="form-control" />
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LastName"></label>
<input asp-for="LastName" class="form-control" />
<span asp-validation-for="LastName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Username"></label>
<input asp-for="Username" class="form-control" />
<span asp-validation-for="Username" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PhoneNumber"></label>
<input asp-for="PhoneNumber" class="form-control" />
<span asp-validation-for="PhoneNumber" class="text-danger"></span>
</div>
<button id="update-profile-button" type="submit" class="btn btn-primary">Save</button>
</div>
<div class="col-md-6">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ProfilePicture" style="width: 100%;"></label>
#if (Model.ProfilePicture != null)
{
<img id="profilePicture" style="width:350px;height:350px; object-fit:cover" src="data:image/*;base64,#(Convert.ToBase64String(Model.ProfilePicture))">
}
else
{
<img id="profilePicture" style="width:350px;height:350px; object-fit:cover" src="">
}
<input type="file"
accept=".png,.jpg,.jpeg,.gif,.tif"
asp-for="ProfilePicture"
class="form-control"
style="border:0px!important;padding: 0px;padding-top: 10px;padding-bottom: 30px;"
onchange="document.getElementById('profilePicture').src = window.URL.createObjectURL(this.files[0])" />
<span asp-validation-for="ProfilePicture" class="text-danger"></span>
</div>
</div>
</div>
</form>
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateIdentityProfilePic(InputModel model, IFormFile profilePicture)
{
if (profilePicture == null || profilePicture.Length == 0)
{
return Content("File not selected");
}
var path = Path.Combine(_environment.WebRootPath, "ImageName/Cover", profilePicture.FileName);
using (FileStream stream = new FileStream(path, FileMode.Create))
{
await profilePicture.CopyToAsync(stream);
stream.Close();
}
if (model == null)
{
return Content("Invalid Submission!");
}
var identityModel = new IdentityModel
{
FirstName = model.FirstName,
LastName = model.LastName,
Username = model.Username,
PhoneNumber = model.PhoneNumber,
ImageName = profilePicture.FileName,
ProfilePictureLocation = path,
};
_context.Add(identityModel);
await _context.SaveChangesAsync();
return RedirectToAction("IdentityProfilePicList");
}
Note: When you would submit Upload profile Picture view this controller should receive the request.
Controller Profile Picture List:
public ActionResult IdentityProfilePicList()
{
var memberList = _context.identityModels.ToList();
return View(memberList);
}
View Profile Picure List:
#model IEnumerable<DotNet6MVCWebApp.Models.IdentityExtendedModel>
#{
ViewData["Title"] = "Index";
}
<div class="form-row">
<table>
<tr>
<td>
<a asp-action="Index" class="btn btn-success">Create New</a>
</td>
<form method="get" action="#">
<td style="padding-right:760px">
</td>
<td>
<input class="form-control" type="text" placeholder="Search for..." name="SearchString" value="#ViewData["CurrentFilter"]" aria-label="Search" aria-describedby="btnNavbarSearch" />
</td>
<td>
<input type="submit" value="Search" class="btn btn-primary" />
</td>
</form>
</tr>
</table>
</div>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.Username)
</th>
<th>
#Html.DisplayNameFor(model => model.ProfilePictureLocation)
</th>
<th>
Member Details
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Username)
</td>
<td>
<img src="~/ImageName/Cover/#item.ImageName"
class="rounded-square"
height="50" width="75"
style="border:1px"
asp-append-version="true" accept="image/*" />
</td>
<td>
<a asp-action="EditMember" class="btn btn-primary" asp-route-memberId="#item.Id">Details</a> | <a asp-action="EditMember" class="btn btn-warning" asp-route-memberId="#item.Id">Edit</a>
</td>
</tr>
}
</tbody>
</table>
Output:
Database:
Note: If you need any further assistance on this please check this GitHub repository.
Thanks for all the help it helped me massively to resolve the issue. At the end of registering the image in the scaffolded razor pages Identity I had to use the code behind file register.cshtl.cs and add it to the Input module there and be consumed by the register.cshtml which now works perfectly.
I hope this helps someone else.
First I extended the ApplicationUser and added the migration.
public class ApplicationUser : IdentityUser
{
[Required]
public string Name { get; set; }
public string? StreetAddress { get; set; }
public string? City { get; set; }
public string? PostalCode { get; set; }
[ValidateNever]
public string? ImageUrl { get; set; }
public int? CompanyId { get; set; }
[ForeignKey("CompanyId")]
[ValidateNever]
public Company Company { get; set; }
}
Then added to the Input model in the Register.cshtml.cs
public class InputModel
{
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
//Added custom fields
[Required]
public string Name { get; set; }
public string? StreetAddress { get; set; }
public string? City { get; set; }
public string? PostalCode { get; set; }
public string? PhoneNumber { get; set; }
[ValidateNever]
public string? ImageUrl { get; set; }
public string? Role { get; set; }
public int? CompanyId { get; set; }
[ValidateNever]
public IEnumerable<SelectListItem> RoleList { get; set; }
[ValidateNever]
public IEnumerable<SelectListItem> CompanyList { get; set; }
}`
Then in the on Post method add the file path to the database.
public async Task<IActionResult> OnPostAsync(IFormFile file, string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = CreateUser();
string wwwRootPath = _hostEnvironment.WebRootPath;
if (file != null)
{
string fileName = Guid.NewGuid().ToString();
var uploads = Path.Combine(wwwRootPath, #"images\companies");
var extension = Path.GetExtension(file.FileName);
if (Input.ImageUrl != null)
{
var oldImagePath = Path.Combine(wwwRootPath, Input.ImageUrl.TrimStart('\\'));
}
using (var fileStreams = new FileStream(Path.Combine(uploads, fileName + extension), FileMode.Create))
{
file.CopyTo(fileStreams);
}
Input.ImageUrl = #"\images\companies\" + fileName + extension;
}
else
{
Input.ImageUrl = #"\images\companies\QPQ-logo.jpg";
}
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
user.Name = Input.Name;
user.StreetAddress = Input.StreetAddress;
user.City = Input.City;
user.PostalCode = Input.PostalCode;
user.PhoneNumber = Input.PhoneNumber;
user.ImageUrl = Input.ImageUrl;
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
if (Input.Role == null)
{
await _userManager.AddToRoleAsync(user, SD.Role_User_Indi);
}
else
{
await _userManager.AddToRoleAsync(user, Input.Role);
}
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}`
One of the first mistakes I made was not declaring the front-end form as a multipart form in the Register.cshtml.
<h1>#ViewData["Title"]</h1><div class="row pt-4">
<div class="col-md-8">
<form id="registerForm" class="row" asp-route-returnUrl="#Model.ReturnUrl" method="post" enctype="multipart/form-data">
<h2>Create a new account.</h2>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-floating py-2 col-12">
<input asp-for="Input.Email" class="form-control" aria-required="true" />
<label asp-for="Input.Email"></label>
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.Name" class="form-control" aria-required="true" />
<label asp-for="Input.Name"></label>
<span asp-validation-for="Input.Name" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.StreetAddress" class="form-control" />
<label asp-for="Input.StreetAddress"></label>
<span asp-validation-for="Input.StreetAddress" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.City" class="form-control" />
<label asp-for="Input.City"></label>
<span asp-validation-for="Input.City" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.PostalCode" class="form-control" />
<label asp-for="Input.PostalCode"></label>
<span asp-validation-for="Input.PostalCode" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.Password" class="form-control" aria-required="true" />
<label asp-for="Input.Password"></label>
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.ConfirmPassword" class="form-control" aria-required="true" />
<label asp-for="Input.ConfirmPassword"></label>
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<select asp-for="Input.Role" asp-items="#Model.Input.RoleList" class=form-select>
<option disabled selected>-Select Role-</option>
</select>
</div>
<div class="form-floating py-2 col-6">
<select asp-for="Input.CompanyId" asp-items="#Model.Input.CompanyList" class=form-select>
<option disabled selected>-Select Company-</option>
</select>
</div>
<div class="form-floating py-2 col-12">
<label asp-for="Input.ImageUrl"></label>
<input asp-for="Input.ImageUrl" type="file" id="uploadBox" name="file" class="form-control" />
</div>
<button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
</form>
</div>
<div class="col-md-4">
<section>
<h3>Use another service to register.</h3>
<hr />
#{
if ((Model.ExternalLogins?.Count ?? 0) == 0)
{
<div>
<p>
There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
about setting up this ASP.NET application to support logging in via external services</a>.
</p>
</div>
}
else
{
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="#Model.ReturnUrl" method="post" class="form-horizontal">
<div>
<p>
#foreach (var provider in Model.ExternalLogins)
{
<button type="submit" class="btn btn-primary" name="provider" value="#provider.Name" title="Log in using your #provider.DisplayName account">#provider.DisplayName</button>
}
</p>
</div>
</form>
}
}
</section>
</div>

One-To-Many relationship between ApplicationUser and an other object

I am struggling trying to implement à create action and an index for my controller.
Basically, I want each user to have multiple pizzas.
I want the connected user to create his own pizzas.
And in the index of my controller I want to show, only the pizzas created by the current connected user.
Here are my models :
1/Pizzas :
public class PizzaModel
{
[Key]
public int PizzaID { get; set; }
[Display(Name = "Nom")]
public string nom { get; set; }
[Display(Name = "Prix(€)")]
public float prix { get; set; }
[Display(Name = "Végétarienne")]
public bool vegetarienne { get; set; }
[Display(Name = "Ingrédients")]
public string ingredients { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
public string ApplicationUserId { get; set; }
}
2/ ApplicationUser :
public class ApplicationUser : IdentityUser
{
public ICollection<PizzaModel> Pizzas { get; set; }
}
3/ This is my Context :
public class AuthDbContext : IdentityDbContext<ApplicationUser>
{
public AuthDbContext(DbContextOptions<AuthDbContext> options) : base(options)
{
}
public DbSet<PizzaModel> Pizzas { get; set; }
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<ApplicationUser>()
.HasMany(p => p.Pizzas)
.WithOne(u => u.ApplicationUser)
.IsRequired()
.HasForeignKey(p => p.ApplicationUserId);
base.OnModelCreating(builder);
}
I want to create a "create action" and an "index action" that shows only the pizzas created by the current connected user. Here is what I have done so far :
1/ Index action method :
public async Task<IActionResult> Index(string searchByName)
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
IEnumerable<PizzaModel> pizzas = new List<PizzaModel>();
pizzas = _context.Pizzas.Where(x => x.ApplicationUserId == userId);
return View(pizzas);
}
2/ Create Action Method :
public async Task<IActionResult> Create(PizzaModel model)
{
_context.ApplicationUsers.Add(model);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index), "Pizza");
}
Could you please help me with these 2 actions (Create and Index) ?
According to your Model and DbContext, I create the actions as below: I'm using the Home Controller and Project name is "WebApplication3"
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly ApplicationDbContext _dbContext;
public HomeController(ILogger<HomeController> logger, ApplicationDbContext dbContext)
{
_logger = logger;
_dbContext = dbContext;
}
public IActionResult Index()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
IEnumerable<PizzaModel> pizzas = new List<PizzaModel>();
pizzas = _dbContext.Pizzas.Where(x => x.ApplicationUserId == userId);
return View(pizzas);
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Create(PizzaModel model)
{
//Note: if you check the ModelState.IsValid, it will return false, because there is no ApplicationID and PizzaID,
//you can create a view model to enter the new value, then, convert it to PizzaModel
//validate the model
//if (ModelState.IsValid)
//{
//get current user id
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (userId != null)
{
//based on the userid to find current user and get its pizzas.
var currentuser = _dbContext.ApplicationUsers.Include(c => c.Pizzas).First(c => c.Id == userId);
List<PizzaModel> pizzas = new List<PizzaModel>();
pizzas = currentuser.Pizzas.ToList();
//add the new item to pizza list
pizzas.Add(new PizzaModel()
{
nom = model.nom,
prix = model.prix,
vegetarienne = model.vegetarienne,
ingredients = model.ingredients
});
//update the pizzas for current user.
currentuser.Pizzas = pizzas;
await _dbContext.SaveChangesAsync();
}
return RedirectToAction(nameof(Index));
//}
//else
//{
// return View();
//}
}
The Index view as below:
#model IEnumerable<WebApplication3.Data.PizzaModel>
#{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.PizzaID)
</th>
<th>
#Html.DisplayNameFor(model => model.nom)
</th>
<th>
#Html.DisplayNameFor(model => model.prix)
</th>
<th>
#Html.DisplayNameFor(model => model.vegetarienne)
</th>
<th>
#Html.DisplayNameFor(model => model.ingredients)
</th>
<th>
#Html.DisplayNameFor(model => model.ApplicationUserId)
</th>
<th></th>
</tr>
</thead>
<tbody>
#if(Model.ToList().Count > 0)
{
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.PizzaID)
</td>
<td>
#Html.DisplayFor(modelItem => item.nom)
</td>
<td>
#Html.DisplayFor(modelItem => item.prix)
</td>
<td>
#Html.DisplayFor(modelItem => item.vegetarienne)
</td>
<td>
#Html.DisplayFor(modelItem => item.ingredients)
</td>
<td>
#Html.DisplayFor(modelItem => item.ApplicationUserId)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
}
else
{
<tr><td colspan="7">Empty</td></tr>
}
</tbody>
</table>
<p>
<a asp-action="Create">Create New Pizza</a>
</p>
The Create View:
#model WebApplication3.Data.PizzaModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>PizzaModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="nom" class="control-label"></label>
<input asp-for="nom" class="form-control" />
<span asp-validation-for="nom" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="prix" class="control-label"></label>
<input asp-for="prix" class="form-control" />
<span asp-validation-for="prix" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="vegetarienne" /> #Html.DisplayNameFor(model => model.vegetarienne)
</label>
</div>
<div class="form-group">
<label asp-for="ingredients" class="control-label"></label>
<input asp-for="ingredients" class="form-control" />
<span asp-validation-for="ingredients" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
The result as below:
Generally, in the HttpPost method such as the Create or Update action method, we need to validte the model is valid or not, then based on the result to show validation message or go the next steps. You can refer the following tutorials:
Model validation in ASP.NET Core MVC and Razor Pages
Tutorial: Implement CRUD Functionality - ASP.NET MVC with EF Core

MVC AD search - Displaying multiple results as .cshtml

I have fully working code that searches active directory and displays it with MVC .cshtml But I have been trying to figure out away to add all the users found to a list then display them. As currently it just displays the first user found.
This is the HomeController that takes a value, Searches AD and returns the results.
public class HomeController : Controller
{
public ActionResult Index(IndexViewModel profile)
{
if (ModelState.IsValid)
{
//List<Principal> users = new List<Principal>();
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.DisplayName = profile.Name + "*";
using (PrincipalSearcher srch = new PrincipalSearcher(qbeUser))
{
if(!(srch.FindAll().Count() < 0))
{
foreach(var found in srch.FindAll())
{
//users.Add(found);
IndexViewModel returnmodel = new IndexViewModel(found);
return View(returnmodel);
}
}
}
}
}
return View(profile);
}
}
The IndexViewModel
public class IndexViewModel
{
public IndexViewModel(Principal found)
{
Name = found.DisplayName;
Email = found.UserPrincipalName;
Description = found.Description;
}
[Required(ErrorMessage = "Please enter a name")]
[Display(Name = "Persons Name")]
public string Name { get; set; }
public string Email { get; set; }
public string Description { get; set; }
//public List<Principal> user { get; set; }
}
Index.cshtml
<div id="content">
#Html.ValidationSummary(true)
#using (Html.BeginForm("Index", "Home"))
{
<fieldset>
<div class="form-group col-md-12">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.EditorFor(modelItem => Model.Name, new { htmlAttributes = new { #class = "form-control", #style = "width:280px" }, })
#Html.ValidationMessageFor(x => x.Name)
</div>
<div class="col-md-2">
<input type="submit" class="btn btn-default" value="Search">
</div>
<div class="col-md-3">
</div>
</div>
</fieldset>
}
<br>
</div>
<table id="historyTable" class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>#Model.Name</td>
<td>#Model.Email</td>
<td>#Model.Description</td>
</tr>
</tbody>
</table>
EDIT-----------
This is one method I tried ----------------
HomeController.cs
public class HomeController : Controller
{
public ActionResult Index(IndexViewModel profile)
{
if (ModelState.IsValid)
{
List<Principal> users = new List<Principal>();
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.DisplayName = profile.Name + "*";
using (PrincipalSearcher srch = new PrincipalSearcher(qbeUser))
{
if(!(srch.FindAll().Count() < 0))
{
foreach(var found in srch.FindAll())
{
users.Add(found);
IndexViewModel returnmodel = new IndexViewModel(users);
return View(returnmodel);
}
}
}
}
}
return View(profile);
}
IndexViewModel.cs
public class IndexViewModel
{
public IndexViewModel(List<Principal> found)
{
user = found;
}
[Required(ErrorMessage = "Please enter a name")]
[Display(Name = "Persons Name")]
public string Name { get; set; }
public string Email { get; set; }
public string Description { get; set; }
public List<Principal> user { get; set; }
}
index.html
<div id="content">
#Html.ValidationSummary(true)
#using (Html.BeginForm("Index", "Home"))
{
<fieldset>
<div class="form-group col-md-12">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.EditorFor(modelItem => Model.Name, new { htmlAttributes = new { #class = "form-control", #style = "width:280px" }, })
#Html.ValidationMessageFor(x => x.Name)
</div>
<div class="col-md-2">
<input type="submit" class="btn btn-default" value="Search">
</div>
<div class="col-md-3">
</div>
</div>
</fieldset>
}
<br>
</div>
<table id="historyTable" class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Description</th>
</tr>
</thead>
<tbody>
#using System.DirectoryServices.AccountManagement
#foreach (Principal prin in Model.user)
{
<tr>
<td>#prin.DisplayName</td>
<td>#prin.UserPrincipalName</td>
<td>#prin.Description</td>
</tr>
}
</tbody>
</table>
The error I get on compile is --
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 37: <tbody>
Line 38: #using System.DirectoryServices.AccountManagement
Line 39: #foreach (Principal prin in Model.user)
Line 40: {
Line 41: <tr>
Source File: C:\Users\hga\Documents\Visual Studio 2015\Projects\Intra AD people searcher\Intra AD people searcher\Views\Home\Index.cshtml Line: 39
You can add if statement to check for null
#if(Model.user !=null)
{
#foreach (Principal prin in Model.user)
{
<!--your code here-->
}
}
In your controller, your return statement is inside your foreach loop. So the first time it goes through the loop, it will return. That means you will only have one result.
Try this:
foreach(var found in srch.FindAll())
{
users.Add(found);
}
IndexViewModel returnmodel = new IndexViewModel(users);
return View(returnmodel);

MVC : Routing route value acting strange?

I have a viewmodel which looks like
public class BusinessUnitDetail
{
public Guid Id { get; set; }
public string Name { get; set; }
public LicensesDetails LicensesDetails { get; set; }
public RepresentativeDetails RepresentativeDetails { get; set; }
}
public class LicensesDetails
{
public string BusinessUnitName { get; set; }
public List<LicensesVM> Licenses { get; set; }
}
public class RepresentativeDetails
{
public string BusinessUnitName { get; set; }
public List<RepresentativeVM> Representatives { get; set; }
}
Currently my view contains two tables which shows List of Representative and List of Licenses.
Here is the view, quite long but anyways
#{
ViewBag.Title = "BusinessUnitDetails";
Layout = "~/Views/shared/_BootstrapLayout.basic.cshtml";
int snor = 1;
int snol = 1;
}
<fieldset>
<legend>#Model.Name <small>Representatives</small></legend>
</fieldset>
<table class="table table-striped table-hover table-bordered">
<caption></caption>
<thead>
<tr>
<th>S.No</th>
<th>Name</th>
</tr>
</thead>
#foreach (var entry in Model.RepresentativeDetails.Representatives)
{
<tr>
#*#Html.HiddenFor(m=>entry.BusinessUnitId)*#
<td>#(snor++)</td>
<td>#entry.UserName</td>
<td>
<div class="btn-group" >
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
Action<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>#Html.ActionLink("Details", "RepresentativeDetails",routeValues:new{entry.BusinessUnitId})</li>
</ul>
</div>
</td>
</tr>
}
</table>
<fieldset>
<legend>#Model.Name <small>Licenses</small></legend>
</fieldset>
<table class="table table-striped table-hover table-bordered">
<caption></caption>
<thead>
<tr>
<th>S.No</th>
<th>Kind</th>
</tr>
</thead>
#foreach (var entry in Model.LicensesDetails.Licenses)
{
<tr>
#* #Html.HiddenFor(m=>entry.BusinessUnitId)*#
<td>#(snol++)</td>
<td>#entry.LicenseName</td>
<td>
<div class="btn-group" >
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
Action<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>#Html.ActionLink("Details", "LicenseDetails",routeValues:new{entry.BusinessUnitId})</li>
</ul>
</div>
</td>
</tr>
}
</table>
When i click the Details action of some row in License table, I expected it to redirect me to LicenseDetails action method as stated in Html.ActionLink, but it doesn't.
The route when i click the details action is
Customer/businessunit/LicenseDetails?BusinessUnitId=25c70a1d-0ed5-406f-8cae-1c6c1bf7d0da
I don't know how the LicenseDetails?BusinessUnitId= part is being generated, and why the action doesn't forward to the LicenseDetails method in controller.
What can I do so that, when i click the details action in second table, I get forwarded to proper controller action which proper Guid id
Any idea?
Edit 1: License Detail Action Methode
public ActionResult LicenseDetails(Guid licenseId)
{
var licenseDetails = _businessUnitRepository.GetLicenseDetail(licenseId);
return View(licenseDetails);
}
Your ActionLink seems fit to some routes. Type it more strongly with controllername:
#Html.ActionLink("Details", "LicenseDetails", "Home", new { licenseId = entry.BusinessUnitId }, null)
Try with this syntax:
#Html.ActionLink("Details", "LicenseDetails", new { licenseId = entry.BusinessUnitId })

How to get data from partial in form

I have
ASP.NET MVC Form in popup with some controls and partial (data grid) with his own Model.
here is popup:
<div id="AddEditDialog" class="none">
#using (Ajax.BeginForm("Save", "Templates", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "AddEditPlaceHolder",
OnSuccess = "OnSaveSuccess",
HttpMethod = "Post"
}))
{
<div>
<div id="AddEditPlaceHolder"></div>
<div id="PopupButtons" class="btn-holder-centered">
<input type="submit" value="Save" name="SaveButton" />
<input type="button" value="Cancel" name="SaveCancelButton" id="CancelEditHandler" />
</div>
</div>
}
</div>
here is form which I render in AddEditPlaceHolder via js:
#model TemplatesViewModel
<div class="form-field-plain overflow">
<div class="forRow narRow float-left">
#Html.LabelFor(x => x.Revocable)
#Html.CheckBoxFor(x => x.Revocable)
</div>
</div>
<div class="form-field-plain overflow">
<div class="forRow narRow float-left">
#Html.LabelFor(x => x.HtmlTemplate)
#Html.TextAreaFor(x => x.HtmlTemplate)
</div>
</div>
#Html.Partial("_VariablesGridView", Model.Variables)
_VariablesGridView.cshtml:
#model List<TemplateVariableViewModel>
<table id="TemplateVariablesGrid">
<thead>
<tr>
<td>Tag/Code</td>
<td>Prompt</td>
<td>Action</td>
</tr>
</thead>
<tbody>
#foreach (var i in Model)
{
<tr>
<td>
#Html.TextBox("txtTag", #i.Tag, new {})
</td>
<td>
#Html.TextBox("txtPrompt", #i.Prompt, new { })
</td>
<td>
#Html.HiddenFor(x => x.First(s => s.Id == #i.Id).Id)
<label class="delete-variable">delete</label>
</td>
</tr>
}
</tbody>
</table>
<br />
<input type="button" name="btnAddTemplateVariable" value="add new variable"/>
<br />
My problem is :
in Controller 'save form' method public ActionResult Save(TemplateViewModel model)
my model contains all data from form but TemplateViewModel.Variables is empty
Is there any way to fill it in there?
Models:
public class TemplateViewModel
{
public int Id { get; set; }
public string HtmlTemplate { get; set; }
public List<TemplateVariableViewModel> Variables { get; set; }
}
public class TemplateVariableViewModel
{
public int Id { get; set; }
public string Tag { get; set; }
public string Prompt { get; set; }
}
I believe it is because the ASP.Net MVC binding is not putting these fields in context, have a look at your field names delivered to the browser, what is txtTag prefixed by when it gets to the browser and what is is after you do the following:
#Html.Partial("_VariablesGridView", Model)
_VariablesGridView.cshtml:
#model TemplatesViewModel
...
#for (int i = 0; i < Model.Variables.Count; i++)
#Html.TextBox("txtTag", #Model.Variables[i].Tag, new {})
Forgive me if this fails miserably (again), I'm shooting from the hip.

Resources