i have the following action for the delete operation:
[HttpPost]
public ActionResult Delete(int id, EmployeeDeleteViewModel collection)
{
try
{
if (ModelState.IsValid)
{
Employee e = new Employee
{
EmpId = collection.EmpID,
FirstName = collection.FirstName,
LastName = collection.LastName,
DepartmentId = collection.DepartmentID
};
db.Employees.Remove(e);
db.SaveChanges();
return RedirectToAction("Index", new { id = id });
}
// TODO: Add update logic here
return View(collection);
}
catch
{
return View();
}
}
the delete view is:
#model VirtualCampus2.Models.EmployeeDeleteViewModel
#{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<fieldset>
<legend>EmployeeDeleteViewModel</legend>
<div class="display-label">
#Html.DisplayNameFor(model => model.EmpID)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.EmpID)
</div>
<div class="display-label">
#Html.DisplayNameFor(model => model.FirstName)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.FirstName)
</div>
<div class="display-label">
#Html.DisplayNameFor(model => model.LastName)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.LastName)
</div>
<div class="display-label">
#Html.DisplayNameFor(model => model.DepartmentName)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.DepartmentName)
</div>
<div class="display-label">
#Html.DisplayNameFor(model => model.DepartmentID)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.DepartmentID)
</div>
</fieldset>
#using (Html.BeginForm())
{
<p>
<input type="submit" value="Delete" /> |
#Html.ActionLink("Back to List", "Index")
</p>
}
When i click delete on the delete view the following error occurs:
to action method's second parameter "collection", it sends a collection with zeros and null in their properties
does not delete the record
Here is video that shows the problem
why this happens and how do i fix this?
To Steve:
I have made the changes by creating a separate view model and a delete action:
ViewModel:
public class EmpDeleteCommitViewModel
{
public int EmpID { get; set; }
}
the actions Delete methods:
[HttpGet]//this works fine, gets the record to show on view
public ActionResult Delete(int id)
{
var empList = db.Employees.ToList();
var employee = empList.Where(e => e.EmpId == id).Select(e => new EmployeeDeleteViewModel
{
EmpID=e.EmpId,
FirstName= e.FirstName,
LastName=e.LastName,
DepartmentID=e.DepartmentId,
DepartmentName=e.Department.Name
}).FirstOrDefault();
return View(employee);
}
[HttpPost] //BUT THIS DOES NOT WORK!, evm EmpID does not contain id value
public ActionResult Delete(EmpDeleteCommitViewModel evm)
{
try
{
var employee = db.Employees.Where(e => e.EmpId == evm.EmpID).FirstOrDefault();
db.Employees.Remove(employee);
db.SaveChanges();
return RedirectToAction("Index", new { id = evm.EmpID });
}
catch
{
return View();
}
}
You have no form controls (<input>) between form tags so there is nothing to post back when you submit the form. All you doing is generating some text to display the property values.
There is no need to include the EmployeeDeleteViewModel collection parameter in you method. Your int id parameter will be bound with the id of the employee assuming your using the correct routing, so all you need to to get the original data model from the database based on the id and then delete it.
Related
I'm learning about .NET Core and I'm using code from this tutorial. But my update sql is not working.
Here is the index view code:
public ActionResult Index(int? id)
{
ViewBag.Operation = id;
ViewBag.Name = db.Chars.ToList();
Chars Chars = db.Chars.Find(id);
return View(Chars);
}
As for now it work I see results from sql and here is the updated part:
public ActionResult Update(Chars Chars)
{
if (ModelState.IsValid)
{
db.Entry(Chars).State = EntityState.Modified;
db.SaveChanges();
}
return RedirectToAction("Index", new { id = 0 });
}
Here is index.cshtml part:
#using (Html.BeginForm()
{
#foreach (var item in (IEnumerable<MVC__test_2.Chars>)ViewBag.Name)
{
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(modelItem => item.CharName, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(modelItem => item.CharID, new { id = item.CharID })
</div>
</div>
#Html.ActionLink("Index", "Index", new { id = item.CharID })
<input type="submit" value="Update" name="Update"
style=#((ViewBag.Operation != null && Convert.ToInt32(ViewBag.Operation) > 0) ? "display:block" : "display:none") />
}
}
According to the tutorial you provided , I made a demo to test and it updated the data well. The following is the working example , you could refer to and make the modification as per your need .
Model
public class Description
{
public int Id { get; set; }
public string Display { get; set; }
}
Controller
public IActionResult Index(int? id)
{
ViewBag.Operation = id;
ViewBag.Name = _context.Description.ToList();
Description description= _context.Description.Find(id);
return View(description);
}
public ActionResult Update(Description description)
{
if (ModelState.IsValid)
{
_context.Entry(description).State = EntityState.Modified;
_context.SaveChanges();
}
return RedirectToAction("Index", new { id = 0 });
}
Index.cshtml , you should hide the id of the modified data in the modification section.
#model WebApplication1.Models.Description
#using (Html.BeginForm("Update", "Home", FormMethod.Post))
{
#foreach (var item in (IEnumerable<WebApplication1.Models.Description >)ViewBag.Name)
{
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(modelItem => item.Display, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(modelItem => item.Id, new { id = item.Id })
</div>
</div>
#Html.ActionLink("Edit", "Index", new { id = item.Id })
}
// Create or Update data
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
<fieldset>
<legend> <b>Entry Screen</b></legend>
<div class="form-group">
#Html.LabelFor(model => model.Display, new { #class = "control-label col-md-2" })
#Html.HiddenFor(model => model.Id)
<div class="col-md-10">
#Html.EditorFor(model => model.Display)
#Html.ValidationMessageFor(model => model.Display)
</div>
</div>
<div class="form-group">
<p>
<input type="submit" value="Create" name="Create"
style=#((ViewBag.Operation != null && Convert.ToInt32(ViewBag.Operation) > 0) ? "display:none" : "display:block") />
<input type="submit" value="Update" name="Update"
style=#((ViewBag.Operation != null && Convert.ToInt32(ViewBag.Operation) > 0) ? "display:block" : "display:none") />
</p>
</div>
</fieldset>
</div>
}
Here is the Idea. When an Admin is logged on they can pull up a list of all of the users.It will give the options for edit, details, delete like normal but I have added a link to Purchases like so:
#model IEnumerable<IdentitySample.Models.ApplicationUser>
#{
ViewBag.Title = "Index";
}
<div class="col-12 backgroundImg">
<div class="navbarSpace">
<div class="col-12 formBackground">
<h2 class="formHeader">List of Users</h2>
<h4 class="formText">
#Html.ActionLink("Create New ", "Create")
</h4>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th>
#Html.DisplayNameFor(model => model.UserName)
</th>
<th>
#Html.DisplayNameFor(model => model.FavStrain)
</th>
<th>
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.FavStrain)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id
}) |
#Html.ActionLink("Details", "Details", new { id =
item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id =
item.Id }) |
#Html.ActionLink("Purchases", "PurchaseIndex", new {
id = item.Id})
</td>
</tr>
}
</table>
</div>
</div>
</div>enter code here
When you click the Purchases link it takes you to the PurchaseIndex page which looks like this:
Purchase List
#model IEnumerable<IdentitySample.Models.Purchases>
#{
ViewBag.Title = "Index";
}
<div class="col-12 backgroundImg navbarSpace">
<div class="col-12 formBackground">
<h2 class="formHeader">Index</h2>
<hr />
<div class="formHeaderSmall">
Total Points <br />
#Model.Sum(i => i.Points) </div>
<p class="formText">
#Html.ActionLink("Create New", "CreatePurchase")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Points)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Points)
</td>
<td></td>
</tr>
}
</table>
<p class="formText">
#Html.ActionLink("Back to List", "Index")
</p>
</div>
</div>
It gives a list of Purchases and gives the total points that is why i didnt include a details page. Everything works right EXCEPT for the fact that the Purchases do not map to a specific user. If I create a new user and click Purchases it brings up a list of all of the purchases, not just the purchases specific for that user. How do I get a Purchase to map to a Specific User?
I have created a Purchases class that looks like this:
public class Purchases
{
[Key]
public int PurchaseId { get; set; }
[Required]
[Display(Name = "Product Name")]
[DataType(DataType.Text)]
public string Name { get; set; }
[Required]
[Range(0,5)]
[Display(Name = "Points")]
[DataType(DataType.Text)]
public int Points { get; set; }
public string ApplicationUserId { get; set; }
public virtual ApplicationUser Users { get; set; }
}
My ApplicationUser Class looks like this:
public class ApplicationUser : IdentityUser
{
[Display(Name ="Favorite Strain")]
[DataType(DataType.Text)]
public string FavStrain { get; set; }
public virtual List<Purchases> Purchase { get; set; }
Now up to this point the Database is registering the Foreign Key of the Purchases Class to the ApplicationUser class like it is supposed to.
I can create a new purchase and display them to a list and all of the Crud Operations work just fine.
The problem is when I create a new Purchase it doesn't include the ApplicationUserId in the Database it returns a Null.
Null Database
I am pretty sure that the problem is in my Controller. I have tried just about everything so I don't want to include the failed try's so here is the Controllers as they are now and working.
There is no need for me to include the edit or details because I am not going to give the users that access.
public ActionResult CreatePurchase()
{
return View();
}
private ApplicationDbContext db = new ApplicationDbContext();
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreatePurchase([Bind(Include = "PurchaseId,Name,Points,Id")] Purchases purchases)
{
if (ModelState.IsValid)
{
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchases);
}
// GET: Purchases/Edit/5
public ActionResult PurchaseIndex()
{
var userDetails = db.Purchases.Include(u => u.Users);
return View(db.Purchases.ToList());
}
This is my first Question on Stack Overflow so forgive me if something isn't right.
**************************************Update************************************
This is my PurchaseIndexController. Now this returns only the user associated with the purchase. However it is always 0 because there is no UserID. If I try using an int? type or Guid? it gives an error. Cannot implicitly convert type int to string.
public ActionResult PurchaseIndex(string ID)
{
//this gets all purchases for a certain individual
ApplicationDbContext db = new ApplicationDbContext();
var userDetails = db.Purchases.Where(x => x.ApplicationUserId ==
ID).ToList();
return View(userDetails);
}
Here is the CreatePurchase View
#model IdentitySample.Models.Purchases
#{
ViewBag.Title = "Create";
}
<div class="col-12 backgroundImg navbarSpace">
<div class="col-12 formBackground">
<h2 class="formHeader">Add a New Purchase</h2>
<hr />
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#*#Html.Hidden("id", (string)ViewBag.UserID)*#
#Html.HiddenFor(model => model.ApplicationUserId)
<div class="form-horizontal">
<div class="col-12">
#Html.LabelFor(model => model.Name, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Name, "", new
{ #class = "text-danger" })
</div>
</div>
<div class="col-12">
#Html.LabelFor(model => model.Points, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-md-10">
#Html.EditorFor(model => model.Points, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Points, "",
new { #class = "text-danger" })
</div>
</div>
<div class="col-12">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-
default" />
</div>
</div>
</div>
}
<div class="formText">
#Html.ActionLink("Back to List", "Index")
</div>
</div>
</div>
I also have link in the Manage section for the users to check thier points and purchases but I don't know how to create an ActionLink for this to just get purchases associated with the user.
Here is the controller
public ActionResult WeedPoints(string ID)
{
ApplicationDbContext db = new ApplicationDbContext();
var userDetails = db.Purchases.Where(x => x.ApplicationUserId ==
ID).ToList();
return View(userDetails);
}
Here is the Action Link now.
<div class="col-12 formHeaderSmall">#Html.ActionLink("My
Purchases/Points", "WeedPoints", "Manage")</div>
*********************************Update****************************************
Here is the Controllers with the View Bag reference. The Create Purchase View has the ViewBag I just Uncommented it out.
[Authorize(Roles =
"Admin,DispensaryManager,DispensaryEmployee,DispensaryEastEmployee")]
public ActionResult CreatePurchase(string Id)
{
ViewBag.UserID = Id;
//ApplicationDbContext db = new ApplicationDbContext();
//var userDetails = db.Purchases.Where(x => x.ApplicationUserId == Id;
return View();
}
private ApplicationDbContext db = new ApplicationDbContext();
//POST: Purchases/Create
[HttpPost]
[Authorize(Roles =
"Admin,DispensaryManager,DispensaryEmployee,DispensaryEastEmployee")]
[ValidateAntiForgeryToken]
public ActionResult CreatePurchase([Bind(Include =
"PurchaseId,Name,Points,ApplicationUserId")] Purchases
purchases,string id)
{
if (ModelState.IsValid)
{
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchases);
}
[Authorize(Roles =
"Admin,DispensaryManager,DispensaryEmployee,DispensaryEastEmployee")]
public ActionResult PurchaseIndex(string Id)
{
//this gets all purchases for a certain individual
ApplicationDbContext db = new ApplicationDbContext();
var userDetails = db.Purchases.Where(x => x.ApplicationUserId ==
Id).ToList();
ViewBag.UserID = Id;
return View(userDetails);
}
***************************Total Refactor*********************************8
Here is the new controller in its entirety.
public class PurchasesController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: Purchases
public ActionResult Index()
{
var purchases = db.Purchases.Include(p => p.Users);
return View(purchases.ToList());
}
// GET: Purchases/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Purchases purchases = db.Purchases.Find(id);
if (purchases == null)
{
return HttpNotFound();
}
return View(purchases);
}
// GET: Purchases/Create
public ActionResult Create()
{
ViewBag.Users = new SelectList(db.Users, "Id", "UserName");
List<SelectListItem> selectListItems = new List<SelectListItem>();
foreach (ApplicationUser user in db.Users)
{
SelectListItem selectListItem = new SelectListItem
{
Text = user.UserName,
Value = user.Id.ToString()
};
selectListItems.Add(selectListItem);
}
//ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName");
return View();
}
// POST: Purchases/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 ActionResult Create([Bind(Include =
"PurchaseId,Name,Points,TotalPoints,ApplicationUserId")] Purchases
purchases)
{
if (ModelState.IsValid)
{
var totalPoints = db.Purchases.Sum(x => x.Points);
purchases.TotalPoints = totalPoints;
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName", purchases.ApplicationUserId);
return View(purchases);
}
// GET: Purchases/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Purchases purchases = db.Purchases.Find(id);
if (purchases == null)
{
return HttpNotFound();
}
ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName", purchases.ApplicationUserId);
return View(purchases);
}
// POST: Purchases/Edit/5
// 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 ActionResult Edit([Bind(Include =
"PurchaseId,Name,Points,TotalPoints,ApplicationUserId")] Purchases
purchases)
{
if (ModelState.IsValid)
{
db.Entry(purchases).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName", purchases.ApplicationUserId);
return View(purchases);
}
// GET: Purchases/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Purchases purchases = db.Purchases.Find(id);
if (purchases == null)
{
return HttpNotFound();
}
return View(purchases);
}
// POST: Purchases/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Purchases purchases = db.Purchases.Find(id);
db.Purchases.Remove(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
Now there is a dropdown list of users to choose from when you create a new purchase. Here is the Create View.
<div class="col-12 backgroundImg navbarSpace scrollBar">
<div class="formBackground col-12">
<h2 class="formHeader">Edit Puchase</h2>
<hr />
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger"
})
#Html.HiddenFor(model => model.PurchaseId)
#Html.HiddenFor(model => model.TotalPoints)
<div class="col-12">
#Html.LabelFor(model => model.Name, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-12">
#Html.EditorFor(model => model.Name, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Name, "", new
{ #class = "text-danger" })
</div>
</div>
<div class="col-12">
#Html.LabelFor(model => model.Points, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-12">
#Html.EditorFor(model => model.Points, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Points, "",
new { #class = "text-danger" })
</div>
</div>
#*<div class="col-12">
#Html.LabelFor(model => model.TotalPoints,
htmlAttributes: new { #class = "formText col-12" })
<div class="col-12">
#Html.EditorFor(model => model.TotalPoints, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model =>
model.TotalPoints, "", new { #class = "text-danger" })
</div>
</div>*#
<div class="col-12">#Html.LabelFor(model => model.ApplicationUserId,
"Users", htmlAttributes: new { #class = "formText col-12" })
<div class="col-12"> #Html.DropDownList("Users", null, htmlAttributes:
new { #class = "col-12" })
#Html.ValidationMessageFor(model => model.ApplicationUserId, "", new {
#class = "text-danger" })
</div>
</div>
<div class="col-12">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div class="formText"> #Html.ActionLink("Back to List", "Index")
</div>
</div>
</div>
This creates a drop down list of users displaying their User Name. When I select a user and hit save I get an error saying that
There is no ViewData item of type 'IEnumerable' that has the key 'Id'.
Is the 'Id' being passed to this method null?
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreatePurchase([Bind(Include = "PurchaseId,Name,Points,Id")] Purchases purchases)
{
if (ModelState.IsValid)
{
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchases);
}
If it is null, the userID should be included (as a hidden field) in the form you are posting. Then (once the userID is populated in the DB) you should be able to get only the purchase associated with the userID, doing something like this:
var userDetails = db.Purchases.Where(x=>x.ApplicationUserId == ID).ToList();
The problem you are having is that the 'Create new purchase' action is not passing a user id, it is currently:
#Html.ActionLink("Create New", "CreatePurchase")
Whereas it needs to be this to pass an id:
#Html.ActionLink("Create New", "CreatePurchase", new {
id = Model.Id})
However this assumes that an id has been passed to the purchase index view in the model for that page, which is likely not the case but I can't tell as I can't see your purchase index action. The simplest way to pass it for you is through a viewbag, however I do not recommend using this for your site if you intend to use it seriously. The correct way to handle data across your views would be using viewmodels. There are a lot of tutorials available, e.g. https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-3
Using the CRUD implementation you have you can just pass the id to the page using a weakly type viewbag. Your purchase index action should look something like this:
public ActionResult Index(string id)
{
//this checks to see if an id has been passed to the action
if (id != null){
//this gets all purchases for a certain individual
var purchases = db.purchases.Where(i => i.ApplicationUserId == id).ToList()
//this gets the user id passed to the action and sticks it in a viewbag you can retrieve later on the page
ViewBag.UserID == id;
//returns the view with the list above
return View(purchases);
}
else{
//no id was passed to the action so the list is for all purchases
var purchases = db.purchases.ToList();
return View(purchases);
}
}
Now in your view you need to amend the create new purchase action to include the viewbag item:
#Html.ActionLink("Create New", "CreatePurchase", new {
id = ViewBag.UserID})
Change your create purchase action to accept the user id you are passing:
public ActionResult CreatePurchase(string id)
{
//puts the id in a viewbag to again be used by the view
ViewBag.UserID == id;
return View();
}
Then on your create purchase view you need to pass the viewbag item into the model, you do this by having a hidden field somewhere inside the form:
#Html.Hidden("id", (string)ViewBag.UserID)
I'm converting the viewbag into a string because assuming your are using ASP NET identity the user id is a string and ViewBag is a dynamic object, so needs to be turned into a string before you can put it into the model.id space effectively. This will then pass the user ID to the post action and a purchase will be created specific to the id.
Bear in mind, this is a terrible way to be doing this, the default CRUD stuff whilst handy isn't really that great for production because you are accessing models directly and you will need to use weakly typed ViewBags to transfer data. It's error prone and insecure.
i have the following page generated
when i click the Edit link, the record data must be sent to the input boxes on teh same page (without refreshing the page)
currently i have the controller code and views
controller: ProductCategory
public class ProductCategoryController : Controller
{
//
// GET: /ProductCategory/
TUDBEntities _db = new TUDBEntities();
public ActionResult Index(string Code)
{
var categories = _db.mt_ProductCategories
.Where(pc => Code == "" || Code == null|| pc.CatCode == Code)
.Select(
c => new ProductCategory {
Id = c.Id,
CategoryCode = c.CatCode,
Name = c.CatName,
Url = c.Url
});
if (Request.IsAjaxRequest())
{
return PartialView("_ProductCategoryList", categories);
}
return View(categories);
}
[HttpPost]
public ActionResult Save(ProductCategory userData)
{
try
{
if (ModelState.IsValid)
{
mt_ProductCategories cat = new mt_ProductCategories { CatCode = userData.CategoryCode, CatName = userData.Name };
// TODO: Add insert logic here
_db.mt_ProductCategories.Add(cat);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
catch
{
return View();
}
}
public ActionResult Edit(int id)
{
var category = _db.mt_ProductCategories
.Where(pc => pc.Id == id)
.Select(pc => new ProductCategory
{ Id=pc.Id, CategoryCode=pc.CatCode,Name=pc.CatName }).ToList();
return RedirectToAction("Index", category);
}
}
Index view
#model IEnumerable<eComm1.Models.ProductCategory>
#using(Ajax.BeginForm("Save", "ProductCategory",
new AjaxOptions {
HttpMethod="POST",
UpdateTargetId="prod-grid",
InsertionMode=InsertionMode.Replace,
OnSuccess="loaddivdata"
}))
{
<fieldset class="form-group">
<label for="Code">Category Code</label>
<input type="text" class="form-control focus" id="Code" name="CategoryCode" placeholder="Product category code" >
</fieldset>
<fieldset class="form-group">
<label for="ProdName">Product Name</label>
<input type="text" class="form-control" id="ProdName" name="Name" placeholder="Product Name">
</fieldset>
<button type="Submit" class="btn btn-primary">Save</button>
}
<hr />
<div id="prod-grid">
#Html.Partial("_ProductCategoryList", #Model)
</div>
<script type="text/javascript">
$(function () {
$('.focus :input').focus();
});
function loaddivdata() {
$('#prod-grid').html();
$('#Code, #ProdName').val('');
};
// $('#prod-grid').load(function () {
// $.ajax({
// url:'ProductCategoryController/Index',
// method:'GET',
// type:'application/html',
// success: function () { alert('called');}
// });
// });
//});
</script>
Partial View: _ProductCategoryList
#model IEnumerable<eComm1.Models.ProductCategory>
<div class="panel panel-default">
#if (Model.Count() == 0) { <div class="panel-heading">Product Categories - <span style='color:red;font-weight:bold' >0 RESULTS FOUND</span></div>
}else{
<!-- Default panel contents -->
<div class="panel-heading">Product Categories</div>
}
<!-- Table -->
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.CategoryCode)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Url)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.CategoryCode)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Url)
</td>
<td>
#*#Html.beActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })*#
#Ajax.ActionLink("Edit", "Edit", "ProductCategory", new { id=item.Id}, new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "", OnSuccess = "loadformdata" }) |
#Ajax.ActionLink("Delete", "Delete", "ProductCategory", new { id=item.Id}, new AjaxOptions{ HttpMethod="POST", UpdateTargetId="", OnSuccess="loadformdata"})
</td>
</tr>
}
</table>
</div>
How do i modify my code to send data those input control and in my code how do i create hidden field for Id value so it can be send to the Edit(collection, int id) action to update the record?
for Stephen Muecke, i have added my jquery files through the bundles
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/ecomm").Include(
"~/Scripts/jquery-{version}.js",
"~/Scripts/jquery-2.1.4.min.js",
"~/Scripts/bootstrap.js",
"~/Scripts/bootstrap.min.js",
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"
));
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
"~/Scripts/jquery-ui-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
// Use the development version of Modernizr to develop with and learn from. Then, when you're
// ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.min.css",
"~/Content/bootstrap.css",
"~/Content/style.css"));
bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
"~/Content/themes/base/jquery.ui.core.css",
"~/Content/themes/base/jquery.ui.resizable.css",
"~/Content/themes/base/jquery.ui.selectable.css",
"~/Content/themes/base/jquery.ui.accordion.css",
"~/Content/themes/base/jquery.ui.autocomplete.css",
"~/Content/themes/base/jquery.ui.button.css",
"~/Content/themes/base/jquery.ui.dialog.css",
"~/Content/themes/base/jquery.ui.slider.css",
"~/Content/themes/base/jquery.ui.tabs.css",
"~/Content/themes/base/jquery.ui.datepicker.css",
"~/Content/themes/base/jquery.ui.progressbar.css",
"~/Content/themes/base/jquery.ui.theme.css"));
}
In the partial view
#Ajax.ActionLink("Edit", "Edit", "ProductCategory", new { id = item.Id }, new AjaxOptions { HttpMethod = "GET", OnSuccess = "loadformdata" }) |
#Ajax.ActionLink("Delete", "Delete", "ProductCategory", new { id=item.Id}, new AjaxOptions{ HttpMethod="POST", OnSuccess="loadformdata"})
in the index view the following js function:
function loadformdata() {
var cells = $(this).closest('tr').children('td');
alert(cells.eq(0).text());
//$('#Code').val(cells.eq(0).text());
//$('#ProdName').val(cells.eq(1).text());
}
To: Stephen Muecke:
i have removed above loadformdata() and put everything as you said. this youtube video shows the problem that still does not call that click event
To: Steven Meucke:
there's still no luck, for ease i have added a alert() in the function and the alert() won't show. Here is the video
Give you 'Edit' link a class name (say) class="edit" and handle its .click() event to update the form controls
$('.edit').click(function() {
var cells = $(this).closest('tr').children('td');
$('#Code').val(cells.eq(0).text());
$('#ProdName').val(cells.eq(1).text());
return false; // cancel the default redirect
});
Side note: You could just replace the ActionLink() code with Edit and the return false; line is not necessary.
write script for ajax call:
$('#edit').click(function () {
// var data = {here you will pass item.id }
$.ajax({
url:'ProductCategoryController/Edit',
data: {id : data}
method:'GET',
success: function (data) {
//clear html page here
//reload html page by passing 'data' passes in function
//e.g. suppose html page id is 'xyz' then $("#xyz").html(data)
}
});
});
Can you tell how to make a update in DB, using fileupload (File is updated ok) ?
I dont get any errors message, but the edit controller make a new row in DB, instead of updating the old row.
I have tried to remove insertOnsubmit, but only result is no update at all, in DB
I'am using LINQ to SQL MVC4
Table id - CompanyNameCon - PdfCon
Controller:
public ActionResult Edit(int id = 0)
{
DAT_SupplyCon SupplyCon = db.DAT_SupplyCons.Where(x => x.ID == id).FirstOrDefault();
if (SupplyCon == null)
{
return HttpNotFound();
}
return View(SupplyCon);
}
//
// POST: /Books/Edit/5
[HttpPost]
public ActionResult Edit(DAT_SupplyCon DAT_SupplyCon, HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
// Delete old file
FileUpload.DeleteFile(DAT_SupplyCon.PdfCon);
// Upload our file
DAT_SupplyCon.PdfCon = FileUpload.UploadFile(file);
???? db.DAT_SupplyCons.InsertOnSubmit(DAT_SupplyCon);
db.SubmitChanges();
return RedirectToAction("Index");
}
return View(DAT_SupplyCon);
}
view
#model CFire2.Models.DAT_SupplyCon
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
#using (Html.BeginForm("Edit", "SupplyCon", FormMethod.Post, new { enctype = "multipart/form- data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>DAT_SupplyCon</legend>
<div class="editor-label">
#Html.LabelFor(model => model.SupplierCon)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SupplierCon)
#Html.ValidationMessageFor(model => model.SupplierCon)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CompanyNameCon)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CompanyNameCon)
#Html.ValidationMessageFor(model => model.CompanyNameCon)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PdfCon)
</div>
<div class="editor-field">
<input type="file" name="file" />
#Html.HiddenFor(model => model.PdfCon)
#Html.ValidationMessageFor(model => model.PdfCon)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Utils:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
namespace CFire2.Utils
{
public static class FileUpload
{
public static char DirSeparator =
System.IO.Path.DirectorySeparatorChar;
public static string FilesPath = "Content" +
DirSeparator + "Uploads" + DirSeparator;
public static string UploadFile(HttpPostedFileBase file)
{
if (null == file) return "";
if (!(file.ContentLength > 0)) return "";
string fileName = file.FileName;
string fileExt = Path.GetExtension(file.FileName);
if (null == fileExt) return "";
if (!Directory.Exists(FilesPath))
{
Directory.CreateDirectory(FilesPath);
}
var path = Path.Combine(HttpContext.Current.Server.MapPath("~/Content/SupplyCon"),fileName);
file.SaveAs(Path);
return fileName;
}
public static void DeleteFile(string fileName)
{
if (fileName.Length == 0) return;
var path = Path.Combine(HttpContext.Current.Server.MapPath("~/Content/SupplyCon"), fileName);
if (File.Exists(Path.GetFullPath(path)))
{
File.Delete(Path.GetFullPath(path));
}
}
}
}
Add a hidden field for your primary key property to your form.
#Html.HiddenFor(model => model.ID)
Otherwise it'll be treated as a new record since no primary key is posted.
InsertOnSubmit method work on the basis of primary key .
if primary key value is not exists in DB it will create a new record otherwise it will update that record .
In your case you can use hidden field to store the primary key id into the model .
#Html.HiddenFor(model => model.PrimaryKeyID)
I have been able to create viewmodel like this (please ignore that it includes another viewmodel, that will be fixed after I solve my current problem :) ):
public class UserViewModel
{
#region Variables
private SecUserViewModel user;
private string[] assignedRolesIds;
private List<SecRoleViewModel> availableRoles;
#endregion
#region Properties
public SecUserViewModel User
{
get { return this.user; }
set { this.user = value; }
}
public string Guid
{
get { return this.user.Guid.ToString(); }
set { this.user.Guid = value; }
}
public string UserName
{
get { return this.user.UserName; }
set { this.user.UserName = value; }
}
public string Email
{
get { return this.user.Email; }
set { this.user.Email = value; }
}
public byte[] AuthDigest
{
get { return this.user.AuthDigest; }
set { this.user.AuthDigest = value; }
}
public bool IsUsingTempPasswd
{
get { return this.user.IsUsingTempPasswd; }
set { this.user.IsUsingTempPasswd = value; }
}
public DateTime? LastLogin
{
get { return this.user.LastLogin; }
set { this.user.LastLogin = value; }
}
public DateTime? PasswordChanged
{
get { return this.user.PasswordChanged; }
set { this.user.PasswordChanged = value; }
}
public string[] AssignedRolesIds
{
get { return this.assignedRolesIds; }
set { this.assignedRolesIds = value; }
}
public List<SecRoleViewModel> AvailableRoles
{
get { return this.availableRoles; }
set { this.availableRoles = value; }
}
#endregion
#region Constructor
public UserViewModel()
{
User = new SecUserViewModel();
AssignedRolesIds = null;
AvailableRoles = new List<SecRoleViewModel>(0);
}
public UserViewModel(SecUserViewModel secUser, List<SecRoleViewModel> roleList, List<SecRoleViewModel> availableList)
{
User = secUser;
AssignedRolesIds = roleList.Select(r => r.Role.Guid.ToString()).ToArray();
AvailableRoles = availableList;
}
#endregion
}
My controller has an edit action. On "GET" I pass viewmodel and it is displayed properly including the multiselect list and preselected values. But when I "POST" the "Edit", UserViewModel that is passed back has "AssignedRolesIds" and "AvailableRoles" Empty, although everything else is filled. When I check FormCollection object, there are "AssignedRolesIds" present as a key.
My view looks like this:
#model DocuLive.ViewModels.UserViewModel
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_AdminPage.cshtml";
}
<h2>Edit</h2>
<div class="error-message">#TempData["Fail"]</div>
<div class="success-message">#TempData["Success"]</div>
#using (Html.BeginForm("Edit", "User", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>SecUser</legend>
<div class="editor-label">
#Html.LabelFor(model => model.User.Guid)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.User.Guid)
#Html.HiddenFor(model => model.User.Guid)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.User.UserName)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.User.UserName)
#Html.HiddenFor(model => model.User.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.User.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.User.Email)
#Html.ValidationMessageFor(model => model.User.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.User.IsUsingTempPasswd)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.User.IsUsingTempPasswd)
#Html.ValidationMessageFor(model => model.User.IsUsingTempPasswd)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.User.LastLogin)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.User.LastLogin)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.User.PasswordChanged)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.User.PasswordChanged)
</div>
<div class="hidden-field">
#Html.HiddenFor(model => model.User.AuthDigest)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AssignedRolesIds)
</div>
<div class="editor-field">
#Html.ListBoxFor(x => x.AssignedRolesIds, new MultiSelectList(Model.AvailableRoles, "Guid", "RoleName"), new { Multiple = "multiple", #class = "multi-select-list" })
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Users")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Can anyone advise why only values related to ListBoxFor are not passed back (there is a null value)?
Thanks in advance
It seems that the problem is binding the string array values to string id value of complex object (in my case Role) - but I figured out a way around it eventually. The trick is to have this signature of the method:
[HttpPost]
public ActionResult Edit(UserViewModel user, string [] assignedRolesIds)
And then you have this in view:
#Html.ListBox("AssignedRolesIds",new MultiSelectList(Model.AvailableRoles, "Guid", "RoleName"),new { Multiple = "multiple", #class = "multi-select-list"})
With this solution, you have to reassign "assignedRolesIds" back to "AssignedRolesIds" property on UserViewModel, but that is only two lines (including check that the array is not emppty or null).