I have my ViewModel, and I have my controller to display from the ViewModel correctly, however I'm not sure how I would make the ViewModel editable, as to send the edited data back to the Model. I only want to edit the OrderArchiveViewModel, not the details
ViewModel;
public class OrderArchiveViewModel
{
public int OrderId { get; set; }
public System.DateTime OrderDate { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public decimal Total { get; set; }
public bool HasBeenShipped { get; set; }
public List<OrderDetailArchive> Details { get; set; }
}
public class OrderDetailArchive
{
public string Title { get; set; }
public string Colour { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
}
Controller;
[Authorize(Roles = "Administrator")]
public ActionResult Index()
{
List<T_shirt_Company_v3.ViewModels.OrderArchiveViewModel> list = (from o in new TshirtStoreDB().Orders
.OrderBy(o => o.OrderDate)
.Select(o => new OrderArchiveViewModel()
{
OrderId = o.OrderId,
Address = o.Address,
FirstName = o.FirstName,
LastName = o.LastName,
City = o.City,
OrderDate = o.OrderDate,
PostalCode = o.PostalCode,
Total = o.Total,
HasBeenShipped = o.HasBeenShipped,
Details = (from d in o.OrderDetails
select new OrderDetailArchive
{
Colour = d.Product.Colour,
Quantity = d.Quantity,
Title = d.Product.Title,
UnitPrice = d.UnitPrice
}).ToList()
}).ToList()select o).ToList();
ViewBag.ShippedMessage = list.Where(w => w.HasBeenShipped).Any() ? "Order has been shipped" : "Order is being processed";
return View(list);
}
I can suggest you to make an another two actions.
public ActionResult Edit(int id)
where you will get the Order by it's Id, map to ViewModel and pass it to the view where you will have textboxes for editing. Create another one Action for accepting post request with updated model:
[HttpPost]
public ActionResult Edit(OrderArchiveViewModel model)
When the the edit page is submitted you will have a updated model with the new data, then find your model in database by Id and update the properties.
Can u send the code of your View to get more clarification?
The already given answer could be done by redirect to a page for editing purpose.
Do you want to show the Editing fields above the Grid?
For this purpose, you can add New ViewModel like
public class NewViewModel
{
public OrderArchiveViewModel OrderArchiveViewModel { get; set; }
public List<OrderArchiveViewModel> OrderArchiveViewModelList { get; set; }
}
And you can send data using this NewViewModel to View containing both editable OrderArchiveViewModel depending on the Id and also the List of OrderArchiveViewModel by assigning the list present in Index() action.
Related
Logic: Within another razor view when a user clicks on a highlighted row (selects blog), then that particular blogID (bp parameter) is passed to the IActionResult Display to view that blog. I've checked this with a breakpoint and the value is getting passed correctly. In LINQ I want to query using this blogID to bring back results for that particular blogID, and then display that to view.
I'm currently getting the below expection, I believe the model I'm passing to the return View is not type of type BlogViewModel, maybe the LINQ is not return correctly, why is this?
"The model item passed into the ViewDataDictionary is of type 'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1[ViewModels.BlogViewModel]
Also should I use a submodel such as Comments or is this a bad idea. I've uploaded the ERD diagram, showing the three tables I'm trying to join in LINQ query.
public class CommentModel
{
[Key]
public int commentID { get; set; }
[Display(Name ="Comment")]
[MinLength(5, ErrorMessage = "Comment is too short")]
public string comment { get; set; }
public string reply { get; set; }
public int blogID { get; set; }
public string userID { get; set; }
}
public class BlogViewModel
{
[Key]
public int blogID { get; set; }
public string blogTitle { get; set; }
public string blogContent { get; set; }
public string userID { get; set; }
public DateTime publishedDate { get; set; }
[NotMapped]
public string firstName { get; set; }
public string lastName { get; set; }
//Wrap CommentViewModel in this model, so we can use two models in razor view
public CommentModel Comments { get; set; }
}
public IActionResult Display(int bp)
{
var blogResult = from a in _db.Users
join b in _db.blog on a.Id equals b.userID where b.blogID == bp
join c in _db.comment on b.blogID equals c.blogID
select new BlogViewModel {
blogID = b.blogID,
blogTitle = b.blogTitle,
blogContent = b.blogContent,
userID = a.Id,
publishedDate = b.publishedDate,
firstName = a.firstName,
lastName = a.lastName,
Comments = new CommentModel { comment = c.comment, commentID = c.commentID, reply = c.reply, blogID = b.blogID, userID = a.Id }
};
return View(blogResult);
}
Instead of passing IQueryable<BlogViewModel> to the view you should pass instantiated IEnumerable<BlogViewModel>:
return View(blogResult.ToList());
I suppose in your view the model should be declared as:
#model IEnumerable<BlogViewModel>
I am using autocomplete from JQueryUIHelpers in my Asp.Net MVC project with EF6.
Model structure:
public class Employee
{
[Key]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string SecondName { get; set; }
[NotMapped]
public string FullName => FirstName + " " + SecondName;
public bool IsDriver { get; set; } = false;
public virtual ICollection<Delivery> Deliveries { get; set; }
}
public class Delivery
{
[Key]
public int Id { get; set; }
[Required]
public Employee Driver { get; set; }
public virtual ICollection<EggsMag> Eggs { get; set; }
}
EmployeeController:
public ActionResult Drivers(string term)
{
var drivers = _rep.GetAll(e => e.IsDriver && (e.FirstName.StartsWith(term) || e.SecondName.StartsWith(term)));
return Json((from d in drivers select new { label = d.FullName, value = d.Id }).ToList(), JsonRequestBehavior.AllowGet);
}
DeliveriesController:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,DateOfDelivery,Driver")] Delivery delivery)
{
if (ModelState.IsValid)
{
_rep.Save(delivery);
return RedirectToAction("Index");
}
return View(delivery);
}
View:
#Html.JQueryUI().AutocompleteFor(m => m.Driver.Id, Url.Action("Drivers", "Employees"), "DriverId", null)
Problem Description:
Autocomplete is working correctly but when in Edit view I send POST request I receive all the data, but ModelState.IsValid is false.
The error shows that fields of FirstName and SecondName are empty which is true because I sent just Id of existing Driver, not whole object.
Is there a way to fix it?
Maybe some way to change validation to not check inner model(Driver) fields except Driver.Id existence.
Is there any way to somehow combine the data from two models and THEN map them both to the same viewModel in the context of an edit action?
I have never had to update several tables at once in an edit action in ASP.NET MVC with Entity Framework 6.1.3. This is the layout:
I have a DB table called "Address" which has fields for StreetNumber, StreetName, City, State, ZipCode. It has a one-to-one relationship with another table called Bars. As in, a bar can only have one address and one address can only have one bar.
Because I am storing this data in two separate tables, I am having a very difficult time trying to successfully implement an Edit action which takes data from one form (BarForm) and should update both the Bar and Address database tables. See my code:
BarController
public ActionResult Edit(int id)
{
var bar = _context.Bars.SingleOrDefault(m => m.Id == id);
var address = _context.Addresses.SingleOrDefault(a => a.BarId == id);
//Make sure that the id actually exists:
if (bar == null)
{
return HttpNotFound();
}
var viewModel = Mapper.Map<Bar, BarFormViewModel>(bar, new BarFormViewModel());
if (address == null)
{
address = new Address();
}
Mapper.Map<Address, BarFormViewModel>(address, viewModel);
viewModel.IsNew = false;
return View("BarForm", viewModel);
}
[ValidateAntiForgeryToken]
public ActionResult Save(BarFormViewModel bar)
{
if (!ModelState.IsValid)
{
var viewModel = Mapper.Map<BarFormViewModel, BarFormViewModel>(bar, new BarFormViewModel());
viewModel.IsNew = false;
return View("BarForm", viewModel);
}
if (bar.Id == 0)
{
var newbar = Mapper.Map<BarFormViewModel, Bar>(bar);
newbar.LastUpdated = DateTime.UtcNow;
_context.Bars.Add(newbar);
var addressToAdd = Mapper.Map<BarFormViewModel, Address>(bar);
_context.Addresses.Add(addressToAdd);
}
else
{
var barInDb = _context.Bars.Single(b => b.Id == bar.Id);
var addressInDb = _context.Addresses.Single(a => a.BarId == bar.Id);
Mapper.Map<BarFormViewModel, Bar>(bar, barInDb);
Mapper.Map<BarFormViewModel, Address>(bar, addressInDb);
}
_context.SaveChanges();
return RedirectToAction("Index", "Bar");
}
Domain Models:
public class Bar
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public string GooglePlaceId { get; set; }
public string SundayDiscounts { get; set; }
public string MondayDiscounts { get; set; }
public string TuesdayDiscounts { get; set; }
public string WednesdayDiscounts { get; set; }
public string ThursdayDiscounts { get; set; }
public string FridayDiscounts { get; set; }
public string SaturdayDiscounts { get; set; }
[Display(Name = "Last Updated")]
public DateTime LastUpdated { get; set; }
}
public class Address
{
public int Id { get; set; }
public int? Number { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
public string State { get; set; }
[Required]
public int ZipCode { get; set; }
public Bar Bar { get; set; }
public int BarId { get; set; }
}
View Model which includes both Address and Bar properties:
{
public class BarFormViewModel
{
public int? Id { get; set; }
public string Name { get; set; }
[Required]
[Display(Name = "Google Place ID")]
public string GooglePlaceId { get; set; }
[Display(Name = "Sunday Happy Hour Info:")]
public string SundayDiscounts { get; set; }
[Display(Name = "Monday Happy Hour Info:")]
public string MondayDiscounts { get; set; }
[Display(Name = "Tuesday Happy Hour Info:")]
public string TuesdayDiscounts { get; set; }
[Display(Name = "Wednesday Happy Hour Info:")]
public string WednesdayDiscounts { get; set; }
[Display(Name = "Thursday Happy Hour Info:")]
public string ThursdayDiscounts { get; set; }
[Display(Name = "Friday Happy Hour Info:")]
public string FridayDiscounts { get; set; }
[Display(Name = "Saturday Happy Hour Info:")]
public string SaturdayDiscounts { get; set; }
[Display(Name = "Last Updated")]
public DateTime? LastUpdated { get; set; }
//Address Model Info
public Address Address { get; set; }
public int? AddressId { get; set; }
[RegularExpression("([1-9][0-9]*)", ErrorMessage = "Must be a number")]
public int? Number { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
public string State { get; set; }
[Required]
public int? ZipCode { get; set; }
public bool IsNew { get; set; }
}
The problem here is that I am getting an empty AddressId with this setup, which is causing an exception when the Save action gets run. This is because the BarForm view is getting passed a ViewModel which has been mapped from a Bar object and the Bar domain model actually has no Address information in it, since it is not the Address model/table.
Is there any way to somehow combine the data from both the Address and Bar models and THEN map them both to the same viewModel?
I keep getting a Sequence Contains no Elements error for this line in the Save action:
var addressInDb = _context.Addresses.Single(a => a.Id == bar.AddressId);
I also tried:
var addressInDb = _context.Addresses.Single(a => a.BarId == bar.Id);
Neither work. I understand what the error is saying and have also checked the actual HTML for my hidden Addressid field and it is blank... See code in my BarForm View:
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.AddressId)
#Html.AntiForgeryToken()
Remove the new BarFormViewModel() as the second parameter in your mapping calls as it is not necessary.
In your post action, inside your if statement that checks if the ModelState is valid and if bar.Id == 0, bar is already a view model, so no need to mapping.
And when you create your AutoMapper mapping, you must create a custom property mapping because the Address.Id property will not map automatically to the AddressId property as the name is not the same.
AutoMapper.Mapper.CreateMap<Address, BarFormViewModel>()
.ForMember(dest => dest.AddressId, o => o.MapFrom(source => source.Id));
And then do the same for the inverse mapping.
I'm trying to use the user selected item from the DropDownList to create a new entry in my Database table that is related/linked?(Not sure of correct wording for this) to the DropDownList item.
Here are my Models
public class TaskInstance
{
public int Id { get; set; }
public DateTime DueDate { get; set; }
public int HowMany { get; set; }
public Task TaskId { get; set; }
public virtual Task Task { get; set; }
}
public class TaskInstanceViewModel
{
[DataType(DataType.DateTime)]
public DateTime DueDate { get; set; }
public int HowMany { get; set; }
public IEnumerable<SelectListItem> TaskList { get; set; }
public virtual ICollection<Task> Task { get; set; }
}
public class Task
{
public Task()
{
TaskInstance = new HashSet<TaskInstance>();
}
public int Id { get; set;}
public string Name { get; set; }
public string Unit { get; set; }
public virtual ICollection<TaskInstance> TaskInstance { get; set; }
}
Controllers
public ActionResult Create()
{
var model = new TaskInstanceViewModel();
model.TaskList = db.Task.ToList().Select(x => new SelectListItem
{
Value = x.Id.ToString(),
Text = x.Name
}).ToList();
return View(model);
}
// POST: TaskInstances/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(TaskInstanceViewModel model)
{
if (ModelState.IsValid)
{
var taskinstance = new TaskInstance { DueDate = model.DueDate };
db.TaskInstance.Add(taskinstance);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
View - This is the only one I need to show I think, the others are just fields
#Html.DropDownListFor(x => Model.TaskList, (SelectList)Model.Task)
On the controller where it says var taskinstance = new TaskInstance { DueDate = model.DueDate }; Would be where i need to use the selected item from the User but I have no idea how to get it, i've looked through a lot of posts but most of them is just how to make the DropDownList in the first place but not how to use it(Being a link to another table) with a new database entry.
I'd also like to mention that I am still new to MVC so feel free to point out if im going about this the wrong way
Add a new property of type int to store the selected task from the dropdown. Remember view models are specific to the view.so keep only those properties you absolutely need in the view, in your view model.
public class TaskInstanceViewModel
{
public DateTime DueDate { get; set; }
public int HowMany { get; set; }
public IEnumerable<SelectListItem> TaskList { get; set; }
public int SelectedTask {set;get;} // new property
}
And in your view
#model TaskInstanceViewModel
#using(Html.BeginForm())
{
<label> How many</label>
#Html.TextBoxFor(s=>s.HowMany)
<label>Due date</label>
#Html.TextBoxFor(s=>s.DueDate)
<label>Task</label>
#Html.DropDownListFor(s => s.SelectedTask, Model.TaskList)
<input type="submit" />
}
And in your HttpPost action, you can use the SelectedTask property value which will have the Id of the task selected
[HttpPost]
public ActionResult Create(TaskInstanceViewModel model)
{
if (ModelState.IsValid)
{
var taskinstance = new TaskInstance { DueDate = model.DueDate ,
TaskId=model.SelectedTask };
db.TaskInstance.Add(taskinstance);
db.SaveChanges();
return RedirectToAction("Index");
}
model.TaskList = db.Task.ToList().Select(x => new SelectListItem
{
Value = x.Id.ToString(),
Text = x.Name
}).ToList();
return View(model);
}
It seems to me that you need you point to a property that represents the selected item in the dropdown. The dropdown items is IEnumerable<SelectListItem> why isn't the selected item a property of type SelectListItem?
Add a property to your view model:
public class TaskInstanceViewModel
{
[DataType(DataType.DateTime)]
public DateTime DueDate { get; set; }
public int HowMany { get; set; }
public IEnumerable<SelectListItem> TaskList { get; set; }
public virtual ICollection<Task> Task { get; set; }
//add this property:
public SelectListItem SelectedItem { get; set; }
}
And modify the view:
#Html.DropDownListFor(x => Model.TaskList, Model.SelectedItem )
Developed MVC applciation and Code first approach, had three different class like
First Class
public class Users
{
[Key]
public int UserID { get; set; }
[DisplayName("User Name")]
public string UserName { get; set; }
...
public virtual ICollection<UserDetails> UserDetail { get; set; }
}
Second Class
public class UserDetails
{
[Key]
public int UserDetailsID { get; set; }
[ForeignKey("Users")]
public int UserID { get; set; }
[Required(ErrorMessage = "Please enter the first name")]
[DisplayName("First Name")]
public string FirstName { get; set; }
...
public virtual Users User { get; set; }
public virtual ICollection<UserAddress> UAddress { get; set; }
}
Three Class
public class UserAddress
{
[Key]
public int UserAddressID { get; set; }
[ForeignKey("UserDetailsID")]
public int UserDetailsID { get; set; }
[DisplayName("Address")]
public string Address { get; set; }
...
public virtual UserDetails UserDetail{ get; set; }
}
Need linq result like:
var _result = (from users in objDBMVCSamp.User
join details in objDBMVCSamp.UserDetail
on users.UserID equals details.UserID
select new
{
users.UserName,
users.Password,
details.FirstName,...
}).ToList();
and also need another result like:
var _result = (from users in objDBMVCSamp.User
join details in objDBMVCSamp.UserDetail
on users.UserID equals details.UserID
join address in objDBMVCSamp.UserAddress
on address.UserDetailsID equals details.UserDetailsID
select new
{
users.UserName,
details.FirstName,...
details.UserID,
address.Address,...
}).ToList();
Need to show those result in in view. it's really possible or any other way to show without create a class.
NOTE:
Above result are dynamic, so i though we not able to create a static class for those result. For this scenario what is best method to do this.
If you want to pass the value to view I'd rather suggest to create a ViewModel based upon what you want to show in view.
For example,
ViewModel
public class MyViewModel
{
public var MyValue1{get;set;}
public var MyValue2{get;set;}
public var MyValue3{get;set;}
...
...
}
in controller
public ActionResult MyControllerMethod(someType MyType)
{
// setViewmodels value here
}
then in view
#model MyViewModel
<h1>Model.MyValue1</h1> #*this will display value of MyValue1*#
Define a view model that represents what you want to display
public class UserVM
{
public string UserName { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailID{ get; set; }
public int UserID { get; set; }
}
In your controller
var _result = (from users in objDBMVCSamp.User
join details in objDBMVCSamp.UserDetail
on users.UserID equals details.UserID
select new UserVM
{
UserName = users.UserName,
Password = users.Password,
FirstName = details.FirstName,
LastName = details.LastName,
EmailID = details.EmailID,
UserID = details.UserID
}).ToList();
return View(_result)
View
#model List<UserVM>
#for(int i = 0; i < Model.Count; i++)
{
#Html.DisplayFor(m => m[i].UserName)
....
These are the ways of passing data from controller to view
1) You can use the dynamic object ViewBag to pass data from Controllers to Views.
Add the following to your controller:
ViewBag.MyList = myList;
Then you can acces it from your view:
#ViewBag.MyList
// e.g.#foreach (var item in ViewBag.MyList) { ... }
2) Using ajax service call
$.ajax(
{
type: "GET",
url: "../Home/GetData",
data: { function_param: values}
});
3) Use strongly typed classes
#model MyViewModel
//e.g.#foreach (var item in MyViewModel) { ... }
For more information, kindly refer this link
http://www.c-sharpcorner.com/UploadFile/abhikumarvatsa/various-ways-to-pass-data-from-controller-to-view-in-mvc/