Model binding null values in MVC Core - asp.net-mvc

I am trying to post data from form to action method but looks like model binder is not binding to the entities and I am getting null values in the action method, your help is much appreciated
please find the code snippet below.
Action method
[HttpPost]
public async Task<IActionResult> Update(EditRoleViewModel model)
{
if (ModelState.IsValid)
{
var role = await _Roleame.FindByIdAsync(model.Id);
if (role == null)
{
return RedirectToAction("notfound");
}
role.Name = model.RoleName;
var result = await _Roleame.UpdateAsync(role);
if (result.Succeeded)
{
return RedirectToAction("getAllRoles", "Administrator");
}
}
else {
ModelState.AddModelError(string.Empty, "Error");
}
return View();
}
Model
public class EditRoleViewModel
{
public EditRoleViewModel()
{
names = new List<string>();
}
public string Id;
[Required]
public string RoleName;
public List<string> names;
}
View
#model FirstCoreApplication.Model.EditRoleViewModel
<script src="~/twitter-bootstrap/js/bootstrap.js"></script>
<link href="~/twitter-bootstrap/css/bootstrap.css" rel="stylesheet" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
#{
ViewBag.Title = "Edit Role";
}
<h1>Edit Role</h1>
<form method="post" class="mt-3" asp-controller="Administrator" asp-action="Update">
<div class="form-group row">
<label asp-for="Id" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Id" disabled class="form-control">
</div>
</div>
<div class="form-group row">
<label asp-for="RoleName" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="RoleName" class="form-control">
<span asp-validation-for="RoleName" class="text-danger"></span>
</div>
</div>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-primary">Update</button>
<a asp-action="ListRoles" class="btn btn-primary">Cancel</a>
</div>
</div>
<div class="card">
<div class="card-header">
<h3>Users in this role</h3>
</div>
<div class="card-body">
#if (Model.names.Any())
{
foreach (var user in Model.names)
{
<h5 class="card-title">#user</h5>
}
}
else
{
<h5 class="card-title">None at the moment</h5>
}
</div>
<div class="card-footer">
Add Users
Remove Users
</div>
</div>
</form>
enter code here

I've spent couple of hours looking for an error in my code and the reason for the binder not working in my case was using a model with public fields rather than public properties:
public class EditRoleViewModel
{
public EditRoleViewModel()
{
names = new List<string>();
}
public string Id { get; set; }
[Required]
public string RoleName { get; set; }
public List<string> names { get; set; }
}

The form data you post does not match the property of your model.You do not post the parameter names .
public List names { get; set; }

Related

Why does EF insert the same Guid Ids in different fileds?

I've a Page entity dependent on Investor entity as investor creates a page:
public class Page
{
[Required, DatabaseGenerated(DatabaseGeneratedOption.Identity), Key]
public Guid Id { get; set; }
[Required(ErrorMessage = "Describe the idea of your blog")]
public string Description { get; set; }
[Display(Name = "Тags")]
[Required(ErrorMessage = "Let people know what content you're going to share")]
public ICollection<Investor> Subscribers { get; set; }
public Guid AuthorId { get; set; }
public Investor Author { get; set; }
}
That's what in my dbContext:
modelBuilder.Entity<Page>()
.HasOne(b => b.Author)
.WithOne(u=> u.Page)
.HasForeignKey<Page>(b => b.AuthorId)
.OnDelete(DeleteBehavior.Restrict);
Here's the controller for creating a Page:
public class PageController : Controller
{
private readonly DataManager _dataManager;
private readonly UserManager<ApplicationUser> _userManager;
public PageController(UserManager<ApplicationUser> userManager, DataManager dataManager)
{
_userManager = userManager;
_dataManager = dataManager;
}
public IActionResult Create(Guid Id)
{
var investor = _dataManager.Investors.GetInvestorById(Id);
if (investor.Page != null)
{
return RedirectToPage("/Account/ProfileCheck", new { Id = investor.User.Id, area = "Identity"});
}
var page = new Page();
return View(page);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Page page)
{
if (ModelState["Description"].Errors.Count() == 0)
{
var currentUserId = _userManager.GetUserId(HttpContext.User);
var investor = _dataManager.Investors.GetInvestorByUserId(currentUserId);
page.Author = investor;
_dataManager.Pages.SavePage(page);
return RedirectToPage("/Account/ProfileCheck", new { Id = investor.User.Id, area = "Identity" });
}
return View(page);
}
}
However, In HttpPost Action that page has an Id and this Id equals to author's Id who created it.
This's the view for Create action
#model FinicWebApp.Domain.Entitites.Page
<form method="post" asp-action="Create">
<div class="border p-3">
<div asp-validation-summary="ModelOnly" class="text-danger" ></div>
<div class="form-group row">
<h2 class="text-black-50 pl-3">Create Page</h2>
</div>
<div class="row">
<div class="col-12">
<div class="form-group row">
<div class="col-6">
<label asp-for="Description"></label>
</div>
</div>
<div class="form-group row">
<div class="col-4">
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<div class="col-8 text-center row">
<div class="col">
<input type="submit" class="btn btn-info w-75" value="Create"/>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
In my db all the pages' ids equal to investors' ids who created them. But I need pages' ids to be different. How can I get it?

Passing partial view select2 controls value to parent view contoller

I have a partial view which will be used by multiple views. I am using a select 2 control to populate the account number then when user clicks go button it gets account information.
When the user selects the account and clicks go the value in the controller of the parent comes
as null
Please see the below code.
if you see the below code //accountInfo.AccountSearch.AccountNumber -- this value comes in as null
Partial view - view model
public class AccountSearchViewModel
{
[Display(Name = "Account Number"), DataType(DataType.Text)]
public string AccountNumber { get; set; }
}
Parent view Model
public class AccountInfoViewModel
{
public string AccountName { get; set; }
public string Adddress { get; set;}
public string City { get; set;}
public string Zip { get; set; }
public string PhoneNumber { get; set;}
public AccountSearchViewModel AccountSearch
{
get
{
if(_accountSearch == null)
{
_accountSearch = new AccountSearchViewModel();
}
return _accountSearch;
}
set { _accountSearch = value; }
}
private AccountSearchViewModel _accountSearch;
}
Partial view cshtml
#model Vfs.PsfProWeb.Models.Fleet.AccountSearchViewModel
<div class="col-md-10">
<label> Account Number</label>
<div class="input-group">
<select id="mySelect2" class="form-control accountSelect" text=#Model.AccountNumber></select>
</div>
</div>
Parent view - which calls the partial view
#model Vfs.PsfProWeb.Models.Fleet.AccountInfoViewModel
<div class="card-body pt-2">
<div class="form-row">
<div class="col-11">
#(await Html.PartialAsync("~/Views/Fleet/_AccountNumberSearch.cshtml",Model.AccountSearch))
</div>
<div class="col-md-1">
<button type="submit" class="btn btn-primary" asp-action="GetAccountInformation" asp-controller="AccountInfo">Go</button>
</div>
</div>
</div>
Parent controller
public IActionResult GetAccountInforAccountInfoViewModel accountInfo)
{
try
{
//accountInfo.AccountSearch.AccountNumber -- this value comes in as null
var acctInfo = _accountDomain.GetAcctInfoEntity(accountInfo.AccountSearch.AccountNumber));
return View("~/Views/Fleet/AccountOtb.cshtml", acctInfo);
}
catch (Exception ex)
{
throw;
}
}
The select name should match the model property name like below:
<select id="mySelect2" class="form-control accountSelect" name="AccountSearch.AccountNumber"
text=#Model.AccountNumber></select>
Here is the whole working demo:
View:
#model AccountInfoViewModel
<form asp-action="GetAccountInformation" asp-controller="Home">
<div class="card-body pt-2">
<div class="form-row">
<div class="col-11">
#(await Html.PartialAsync("_partial",Model.AccountSearch))
</div>
<div class="col-md-1">
<button type="submit" class="btn btn-primary" >Go</button>
</div>
</div>
</div>
</form>
#section Scripts
{
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<script>
$(document).ready(function () {
$("#mySelect2").select2();
})
</script>
}
Partial View:
#model AccountSearchViewModel
<div class="col-md-10">
<label> Account Number</label>
<div class="input-group">
<select id="mySelect2" class="form-control accountSelect" text=#Model.AccountNumber
name="AccountSearch.AccountNumber">
<option value="Select" disabled>Select</option>
<option value="aaa">aaa</option>
<option value="bbb">bbb</option>
<option value="ccc">ccc</option>
<option value="ddd">ddd</option>
</select>
</div>
</div>
Result:

MVC Core. Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions

I am passing in my view this model
public class BlogViewModel
{
public List<CommentModel> commentModels { get; set; }
public BlogPostLayoutModel blogPostLayoutModel { get; set; }
public CommentModel commentModel { get; set; }
public int pageNumber { get; set; }
}
CommentModel class
public class CommentModel
{
public int IDComment { get; set; }
public int IDPost { get; set; }
[Required()]
public string Username { get; set; }
[Required()]
public string Content { get; set; }
public static CommentModel CommentToCommentModel(Comment comment)
{
CommentModel commentModel = new CommentModel();
commentModel.IDComment = comment.IDComment;
commentModel.IDPost = comment.IDPost;
commentModel.Username = comment.Username;
commentModel.Content = comment.Content;
return commentModel;
}
public static List<CommentModel> CommentsToCommentModels(List<Comment> comments)
{
List<CommentModel> commentModels = new List<CommentModel>();
foreach (var comment in comments)
{
CommentModel commentModel = new CommentModel();
commentModel.IDComment = comment.IDComment;
commentModel.IDPost = comment.IDPost;
commentModel.Username = comment.Username;
commentModel.Content = comment.Content;
commentModels.Add(commentModel);
}
return commentModels;
}
}
And my view, where the error is thrown (fifth line)
<form asp-action="AddComment">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
#{ var pageNumber = Model.pageNumber;
#Html.HiddenFor(x => pageNumber)
int IDPost = Model.blogPostLayoutModel.IDPost;
#Html.HiddenFor(x => x.commentModel.IDPost == IDPost)
}
<div class="row">
<div class="form-group col-md-4">
<label asp-for="commentModel.Username" class="control-label"></label>
<input asp-for="commentModel.Username" class="form-control" />
<span asp-validation-for="commentModel.Username" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="form-group col-md-12">
<label class="control-label">Comment</label>
<textarea asp-for="commentModel.Content" class="form-control"></textarea>
<span asp-validation-for="commentModel.Content" class="text-danger"></span>
</div>
<div class="form-group col-md-4">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</form>
I have searched for the error, but seems like it's mostly people trying to return a value from a method and getting told to pass the value to a local variable and then put that local variable inside the tag helper, but that doesn't seem to be the case here.
And also blogPostLayoutModel.IDPost is defined as
public int IDPost { get; set; }
EDIT: My commentModel was null, so that was the problem.
Working form
<form asp-action="AddComment">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
#{
#Html.HiddenFor(x => Model.pageNumber)
Model.commentModel.IDPost = Model.blogPostLayoutModel.IDPost;
#Html.HiddenFor(x => x.commentModel.IDPost)
}
<div class="row">
<div class="form-group col-md-4">
<label asp-for="commentModel.Username" class="control-label"></label>
<input asp-for="commentModel.Username" class="form-control" />
<span asp-validation-for="commentModel.Username" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="form-group col-md-12">
<label class="control-label">Comment</label>
<textarea asp-for="commentModel.Content" class="form-control"></textarea>
<span asp-validation-for="commentModel.Content" class="text-danger"></span>
</div>
<div class="form-group col-md-4">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</form>

How Can I Get ViewData in PartialView in Razor Pages

I'm Using Razor Pages For my App, in one part of my app I've used a partial view here is my codes;
public class Permission
{
[Key]
public int PermissionId { get; set; }
public string PermissionTitle { get; set; }
public int? ParentID { get; set; }
}
public class IndexModel : PageModel
{
public PartialViewResult OnGetCreateRole()
{
var ListPermission = permissionService.AllPermission();
return new PartialViewResult()
{
ViewName = "_PCreateRole", // partial's name
ViewData = new ViewDataDictionary<List<Permission>>(ViewData,
ListPermission)
};
}
}
ViewData is a List of Permission class and i've sent ViewData to partial but i dont know how to get ViewData, also my partial use another model, below is my partial:
#model ToMVC.DataLayer.Entities.User.Role
<div class="row">
<div class="col-md-12">
<form asp-page="CreateRole" method="post">
<div class="form-group">
<label class="control-label">Title</label>
<input asp-for="RoleTitle" class="form-control"/>
<p><span class="text-danger" asp-validation-for="RoleTitle"></span></p>
</div>
<div class="form-group">
<input type="submit" value="submit" class="btn btn-primary" />
</div>
//this part needs ViewData
#foreach (var item in ViewData)
{
}
</form>
</div>
</div>
I want to use ViewData in Foreach loop.
A better solution than ViewData would be to simply make a new ViewModel class containing all the information you need for a view.
public class UserRoleAndPermissions{
public UserRoleAndPermissions(){
Permissions = new List<Permissions>();
}
public List<Permission> Permissions {get;set;}
public ToMVC.DataLayer.Entities.User.Role Role {get;set;}
}
And your view
//check your namespace here - this is just an example
#model ToMVC.DataLayer.UserRoleAndPermissions
<div class="row">
<div class="col-md-12">
<form asp-page="CreateRole" method="post">
<div class="form-group">
<label class="control-label">Title</label>
<input asp-for="RoleTitle" class="form-control"/>
<p><span class="text-danger" asp-validation-for="RoleTitle"></span></p>
</div>
<div class="form-group">
<input type="submit" value="submit" class="btn btn-primary" />
</div>
#foreach (var item in Model.Permissions)
{
}
</form>
</div>
</div>

After submit a form in a Area not displayed none of validation message?

I have a area and at this area i have a PostController, and a action with Add name for GET method and a action with Add name for POST method,after submit form i add error to modelstate BUT after postback,not displayed none of validation message and staus code is:301 Moved Permanently !!!
my viewmodel:
public class NewPostViewModel
{
[Required(ErrorMessage = "Please enter title.")]
public string Title { get; set; }
[AllowHtml]
[Required(ErrorMessage = "Please enter article content.")]
public string Content { get; set; }
[Required(ErrorMessage = "Please enter a tag.")]
public string Tags { get; set; }
public bool PublishNow { get; set; }
public string PublishDate { get; set; }
public string PublishTime { get; set; }
}
my view:
#model Soleimanzadeh.Models.ViewModels.NewPostViewModel
#{
ViewBag.Title = "Add";
Layout = MVC.Admin.Shared.Views.BaseLayout;
}
#using (Html.BeginForm(MVC.Admin.Post.Add(), FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false, null, new { #class = "alert" })
<div class="row-fluid">
<div class="span9">
<div class="row-fluid">
<div class="span8">
#Html.TextBoxFor(np => np.Title, new { #class = "span12", placeholder = "Title" })
#Html.ValidationMessageFor(model=>model.Title)
</div>
</div>
<div class="row-fluid">
<div class="span12">
#Html.TextAreaFor(np => np.Content)
#Html.ValidationMessageFor(model=>model.Content)
</div>
</div>
</div>
<div class="span3">
<div class="row-fluid">
<div class="post-options-container">
<div class="header">
Tags
</div>
<div class="content">
<input type="text" placeholder="tags" class="tagManager" style="width: 100px;" />
</div>
</div>
</div>
<div class="row-fluid">
<div class="post-options-container">
<div class="header">
Status
</div>
<div class="content">
<label class="checkbox">
#Html.CheckBoxFor(np => np.PublishNow)
Publish Now?
</label>
<div>
Publish Date:
#Html.TextBoxFor(np => np.PublishDate, new { #class = "calcInput" })
</div>
<div>
Publish Time:
#Html.TextBoxFor(np => np.PublishTime, new { #class = "timeInput" })
</div>
<div>
<input type="submit" value="Save"/>
</div>
</div>
</div>
</div>
</div>
</div>
}
#*some scripts here*#
my controller:
public partial class PostController : Controller
{
public virtual ActionResult Add()
{
var newPost = new NewPostViewModel();
return View(MVC.Admin.Post.Views.Add, newPost);
}
[HttpPost]
[ValidateAntiForgeryToken]
public virtual ActionResult Add(NewPostViewModel post)
{
ModelState.AddModelError("Content","Wrong");
return this.View(MVC.Admin.Post.Views.Add, post);
}
}
Also i use redactor html editor for Content.
Solution:
i use http://lowercaseroutesmvc.codeplex.com/ for convert urls to lowercase,but i not enabled this tool in area section and page redirectd after submit form.

Resources