I am very new to this and I am trying to show multiple entries to show and have a single button for each entry to edit them. I thought it would be really easy but For some reason I am not getting the clicked entity. Is there way of getting modified entity without running javascript?
Any help will be appreciated. I couldn't find any example code that does this. There are many examples that returns all the entries but not the single element.
Here is my entity class Resource:
public class Resource
{
[Required]
public string title { get; set; }
[Required]
public int value { get; set; }
[Key]
[Required]
public int id { get; set; }
}
On the controller side I have:
[HttpGet]
public IActionResult Index()
{
return View(resources);
}
[HttpPost]
public IActionResult Index(Resource resource)
{
return View(resource);
}
Here is the View and EditorTemplates
#model learningMVC.Models.Resource[]
#{
ViewData["Title"] = "index";
}
<h1>index</h1>
<fieldset>
#Html.EditorFor(x => x);
</fieldset>
#addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
#model learningMVC.Models.Resource
<div>
First Name:<br />
<form asp-action="Index" asp-controller="Home">
<input asp-for="id" class="form-controller" />
<input asp-for="value" class="form-controller" />
<input asp-for="title" class="form-controller" />
<input type="submit" value="Save" class="btn btn-primary" id="btnSubmit_#Model.id" />
</form>
</div>
In your case, you should use particalview instead of editfor, you can see my demo below.
_ResourcesPartical.cshtml:
#model Resource
<div>
<form method="post" asp-action="Index">
First Name:<br />
<input asp-for="id" class="form-controller" />
<input asp-for="value" class="form-controller" />
<input asp-for="title" class="form-controller" />
<input type="submit" value="save" />
</form>
</div>
Your Index view:
#model List<Resource>
#foreach(var m in Model)
{
#await Html.PartialAsync("_ResourcesPartical", m)
}
Controller:
[HttpGet]
public IActionResult Index()
{
//here you get your data.below is my fake data.
var resources= new List<Resource>
{
new Resource
{
id = 1,
title = "aa",
value = 3
},
new Resource
{
id = 2,
title = "bb",
value = 4
}
};
return View(resources);
}
[HttpPost]
public IActionResult Index(Resource resource)
{
return RedirectToAction("Index");
}
Test result:
Related
I have two Entities that have many To many relationship, Book and Tags asp Created automatically a table for the two entities, Using the code first approach
I am trying to add a collection of tags in the book creation, but the tag items are null also there is (select asp-for="Tags" ) but it shows me null in [httppost]create.
I tried to add it in through context as it catches the values of tags I add, but there is an error
cannot convert ......dbset<> to Models.tags
Code:
public class Book
{
public int BookID { get; set; }
[Required]
public string Name { get; set; } = null!;
//Navigation property
public virtual ICollection<Tags>? Tags { get; set; }
}
public class Tags
{
public int TagsID { get; set; }
public string TagName { get; set; } = null!;
//Navigation property
public virtual ICollection<Book>? Book { get; set; }
}
//DB Context
public class BLabContext: DbContext
{
public DbSet<Book> Book { get; set; }
public DbSet<Tags> Tags { get; set; }
}
// Book Controller
public class BooksController : Controller
{
private readonly BLabContext _context;
public BooksController(BLabContext context)
{
_context = context;
}
// Tags objects
// public ICollection<Tags> Tags { get; set; }
// GET: Books
public async Task<IActionResult> Index()
{
return View(await _context.Book.ToListAsync());
}
// GET: Books/Create
(on get )
public IActionResult Create()
{
///πππππππ
var tags = _context.Tags.ToList();
ViewData["tags"] = tags;
//ViewBag.tags = tags;
return View();
}
// POST: Books/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("BookID,Name,Description,PublishedOn,Publisher,Price,ImageURL,1)πTagsπ")] Book book)
{
if (ModelState.IsValid)
{
2)π
var tags = _context.Tags;
_context.Add(book);
await _context.SaveChangesAsync();
2)π(cannot convert ......dbset<> to Models.tags
_context.Book.FirstOrDefault(b => b.BookID == book.BookID).Tags.Add(tags);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(book);
}
The create view:
#using Book_Library.Models;
#model Book_Library.Models.Book
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Book</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
#* ///πππππππ*#
<div class="form-group">
<label asp-for="Tags" class="control-label"></label>
#*name="Tags"*#
<select asp-for="Tags" multiple>
#foreach (var tag in #ViewData["tags"] as IList<Tags>)
{
<option value="#tag.TagName">#tag.TagName </option>
}
</select>
</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>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I expect to add a collection of tags to the book on creation, and also view those tags on the details view
You can create a ViewModel to achieve it, Please refer to this demo:
ViewModel
public class CreateBookViewModel
{
[Required]
public string BookName { get; set; }
public List<int> TagId { get; set; }
}
Controller
public IActionResult Create()
{
var tags = _context.Tags.ToList();
List<SelectListItem> dropdown = new List<SelectListItem>();
foreach (var item in tags)
{
var listItem = new SelectListItem();
listItem.Text = item.TagName;
listItem.Value = item.TagsID.ToString();
dropdown.Add(listItem);
}
ViewBag.tags = dropdown;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(CreateBookViewModel book)
{
if (ModelState.IsValid)
{
Book b = new Book();
b.Name = book.BookName;
if (book.TagId.Count>0)
{
foreach (var item in book.TagId)
{
var tag = _context.Tags.Where(x => x.TagsID == item).FirstOrDefault();
b.Tags.Add(tag);
}
}
_context.Book.Add(b);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(book);
}
View
#model CreateBookViewModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Book</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="BookName" class="control-label"></label>
<input asp-for="BookName" class="form-control" />
<span asp-validation-for="BookName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.TagId" class="control-label"></label>
<select asp-for="#Model.TagId" asp-items="#ViewBag.tags" multiple></select>
</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>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
Now, You can add collection of Tags to Book successfully.
Note: You need to instantiate Tags in your Book class, Otherwise the above code will report a nullreferenceexception.
public class Book
{
//.........
//Navigation property
public virtual ICollection<Tags>? Tags { get; set; } = new List<Tags>();
}
the ModelState is Invalid.I Think I pass Model Wrongly.Any Idea?
controller:
[Authorize]
[HttpPost]
public async Task<IActionResult> SendComment([Bind("CommentID,Comment,Date,AdminId")]AdminReport adminReport,int ReportID)
{
var x = _userReport.UserReports.Find(ReportID);
x.IsViewed = true;
adminReport.UserId = x.UserId;
adminReport.AdminId = _userManager.GetUserId(HttpContext.User);
if (ModelState.IsValid){
_adminReport.Add(adminReport);
await _adminReport.SaveChangesAsync();
return View(); }
return RedirectToAction("SendDoneAdmin");
}
its how I pass a Model:
<div class="card-footer">
<form asp-controller="Admin" asp-action="ΩSendComment" method="post">
<input type="hidden" value="#report.ReportID" name="ReportID" />
<button type="submit" class="btn btn-primary">SendComment</button>
</form>
Model:
[Key]
public int CommentID { get; set; }
[Required]
public string Comment { get; set; }
public string AdminId { get; set; }
public string UserId { get; set; }
}`
Your quotation no clear but must know for pass data via Form tag
must all input inside Form tag
Controller
public async Task<IActionResult> SendComment()
{
// write your code....
return View(new AdminReport()); // must return new object
}
POST
Normal write again your action SendComment no any change
HTML
for pass AdminReport model must write flied inside form tag
<form asp-controller="Admin" asp-action="Viewed" method="post">
<input type="hidden" value="#report.ReportID" name="ReportID" />
/* for example */
<input type="hidden" asp-for="model.Comment" />
<button type="submit" class="btn btn-primary">SendComment</button>
</form>
No Problem if you have another view but must return View(new AdminReport());
If you want remove validation for comment prop use below code.
// remove all key
foreach (var key in ModelState.Keys).ToList())
ModelState.Remove(key);
// or for one
ModelState.Remove("comment ");
You put this [Requiried] attribute on the Comment:
[Required]
public string Comment { get; set; }
You will have to include that input in your form in order for the validation to pass.
You can add that field like this:
<div class="card-footer">
<form asp-controller="Admin" asp-action="Viewed" method="post">
<input type="hidden" value="#report.ReportID" name="ReportID" />
<input type="text" name="Comment" />
<button type="submit" class="btn btn-primary">SendComment</button>
</form>
One of my asp.net core pages isn't passing its model back to the controller. I've investigated similar threads in stackoverflow but none could solve my issue - what's different in this particular case?
In the attached MVC, I fill the input box "Enter here some text", then I click on button "CLICK HERE". The debugger enters correctly the controller but the variable "TEST" returns null.
Any geniuses out there able to help me out?
My Model:
namespace MyProgram.Models._04_ModuleTasker
{
public class TaskerViewModel
{
public SelectList SelectListLocations;
public SelectList SelectListUsers;
public SelectList SelectListFilters;
public string SelectedCompanyLocationDesc;
public string SelectedUserId;
public string SelectedFilter;
}
}
My View:
#model MyProgram.Models._04_ModuleTasker.TaskerViewModel
#{
ViewData["Title"] = "AllTasks";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="container-fluid">
<div class="PageTitle">
Tasker
<hr class="TitleHR" />
</div>
<div style="text-align:center;margin-top:20px" class="text-body">
<form asp-controller="Tasker" asp-action="ShowActiveTasksPOST" method="post" role="form">
<input type="text" asp-for="SelectedUserId" /> Enter here some text <br />
<input type="submit" class="btn btn-success InputShortWidth LoginButton" value="CLICK HERE" />
</form>
</div>
</div>
My Controller:
[HttpPost]
public async Task<IActionResult> ShowActiveTasksPOST(TaskerViewModel taskerViewModel)
{
string TEST = taskerViewModel.SelectedUserId;
return RedirectToAction("Index", "Home");
}
To make the binding works, you need to convert the fields to properties with with setters and getters. E.g.:
public string SelectedCompanyLocationDesc { get; set; }
public string SelectedUserId { get; set; }
public string SelectedFilter { get; set; }
I am trying to implement both Create and List actions in a single View. I was being suggested to use ViewModel. I am getting the object reference error. Also some good example on how to achieve this will help.
My Model Class
public class Employee
{
public int ID { get; set; }
[Required(ErrorMessage="Please enter name")]
public string Name { get; set; }
}
My View Model Class
public class EmployeeVM
{
public Employee Employee { get; set; }
public List<Employee> Employees { get; set; }
}
My Controller
[HttpPost]
public ActionResult Create(EmployeeVM emp, string Name)
{
if (ModelState.IsValid) //my modelstate is valid even when the value is empty string; it then gives an Object reference not set error
{
emp.Employee.Name = Name;
repository.SaveRole(emp);
return RedirectToAction("Index");
}
else
{
return View(emp);
}
}
My View
#model ERP.Domain.Entity.EmployeeVM
<body>
<div class="jumbotron">
#using (Html.BeginForm("Create", "MyController", FormMethod.Post))
{
#Html.ValidationSummary(true)
<label>
Name</label>
<input id="txtName" type="text" name="Name" class="btn btn-default" />
#Html.ValidationMessageFor(model => model.Employee.Name)
<input type="submit" value="Save" class="btn btn-primary" />
}
</div>
Also I was suggested in StackOverflow to go with ViewModel approach if I want to use both Create and List in the same View? Is it the right approach. Some example may help.
I am trying to add validations on my View. But I am unable to access the property inside #Html.ValidationMessageFor(?)
My View
#model IEnumerable<Entity.Employee>
<div class="jumbotron">
#using (Html.BeginForm("Create", "Employee", FormMethod.Post))
{
<label>Name</label>
<input id="txtName" type="text" name="EmployeeName" class="btn btn-default" />
#Html.ValidationMessageFor(model => model. //not able to get the Name property
<input type="submit" value="Save" class="btn btn-primary" />
}
</div>
Employee Class
[Required(ErrorMessage="Please enter name")]
public string Name { get; set; }
Controller
[HttpPost]
public ActionResult Create(Employee employee, string EmployeeName)
{
if (ModelState.IsValid)
{
employee.Name = EmployeeName;
repository.SaveRole(role);
return RedirectToAction("Index");
}
else
{
return View(employee);
}
}
I am not sure what have I missed or is it because the View is Strongly couple to IEnumerable<Type>
Your view has defined model of type IEnumerable<Entity.Employee>. This stands for multiple employees. You should change your model declaration to
#model Entity.Employee