Viewmodel is created twice - asp.net-mvc-5

I pass an instance of LoginViewModel to my view (Login)
When I click the submit button on the form another instance of LoginViewModel is created.
I can observe this because I put a breakpoint in the constructor.
Why does this happen and how can I fix it?
#using ViewModels
#model LoginViewModel
<form action="~/Home/VerifyLogin" method="post">
<table>
<tr>
<td>
User Name:
</td>
<td>
#Html.DropDownListFor(m => m.SelectedUserID, Model.UserList);
</td>
</tr>
<tr>
<td>
Password:
</td>
<td>
#Html.TextAreaFor(m => m.SelectedPassword);
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value ="Login" />
</td>
</tr>
</table>
</form>
Controller
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
public ActionResult RedirectToLogin()
{
return View("Login", new LoginViewModel());
}
[HttpPost]
public void VerifyLogin(LoginViewModel vm)
{
bool sucess = false;
// some logic
if(sucess)
RedirectToAction("ProjectList", "Project");
}
ViewModel
public class LoginViewModel :BaseViewModel
{
public IEnumerable<User> Users { get; private set; }
public IEnumerable<SelectListItem> UserList { get; private set; }
public int SelectedUserID { get; set; }
public string SelectedPassword { get; set; }
public LoginViewModel()
{
Users = DataService.GetUsers();
UserList = new SelectList(Users, "ID", "Name");
}
public bool Login(string userName, string password)
{
return true;
}
}

One is created here...
return View("Login", new LoginViewModel());
And one is created here
public void VerifyLogin(LoginViewModel vm)
The model binder creates objects and fills them with the parameters it receives from the request. This is normal operation.
To fix your problem, I would do this...
public ActionResult RedirectToLogin()
{
var vm = new LoginViewModel()
{
Users = DataService.GetUsers();
UserList = new SelectList(Users, "ID", "Name");
};
return View("Login", vm);
}
public LoginViewModel()
{
//Removed code
}

Related

inherited classes and razor asp.net mvc 5

Good morning, I have a problem with returning my database information (generated using the EF 6 code-first method) for my View Razor. The issue is that I'm wanting to return the information from inherited classes in the View, but they are not available, only the properties of the base class are presented, not those of the dependent classes.
The following are the Model, Controller, and View classes used:
Class ClientModel
public class Client
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ClientId { get; set; }
[DataType(DataType.Date)]
public DateTime Birth { get; set; }
[Display(Name = "Telefone principal")]
public string Phone1 { get; set; }
[Display(Name = "Telefone Alternativo")]
public string Phone2 { get; set; }
public ICollection<OrcamentoContato> Contacts { get; set; }
public ICollection<Contrato> Contracts { get; set; }
}
Class FisicModel
public class PessoaFisica : Client
{
public TipoPessoa PersonType { get; set; }
[Required]
[Display(Name = "Nome completo*")]
[StringLength(250, ErrorMessage = "O campo é obrigatório.")]
public string Name { get; set; }
public string RG { get; set; }
[Required(ErrorMessage = "O campo CPF é obrigatório.")]
[StringLength(14)]
public string CPF { get; set; }
[Display(Name = "Filiação")]
public string Filiacao { get; set; }
[Display(Name = "Endereço")]
public string Address { get; set; }
}
Class JuridicModel
public class PessoaJuridica : Client
{
public TipoPessoa PersonType { get; set; }
[Required]
[Display(Name = "Razão Social*")]
[StringLength(200, ErrorMessage = "O campo é obrigatório.")]
public string SocialName { get; set; }
[Required]
[Display(Name = "CNPJ*")]
[StringLength(200, ErrorMessage = "O campo é obrigatório.")]
public string CNPJ { get; set; }
[Display(Name = "Inscrição Estadual")]
public string InscricaoEstadual { get; set; }
[Display(Name = "Inscrição Municipal")]
public string InscricaoMunicipal { get; set; }
[Display(Name = "Endereço")]
public string Address { get; set; }
public string ContactWith { get; set; }
}
Controller
public ActionResult Index()
{
var clients = db.Clients.ToList();
return View(clients);
}
Index View
#model IEnumerable<CabinePhoto.Models.Entidades.Client>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Birth)
</th>
<th>
#Html.DisplayNameFor(model => model.Phone1)
</th>
<th>
#Html.DisplayNameFor(model => model.Phone2)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Birth)
</td>
<td>
#Html.DisplayFor(modelItem => item.Phone1)
</td>
<td>
#Html.DisplayFor(modelItem => item.Phone2)
</td>
</tr>
}
IdentityModel
public DbSet<Client> Clients { get; set; }
public DbSet<PessoaFisica> PessoaFisica { get; set; }
public DbSet<PessoaJuridica> PessoaJuridica { get; set; }
All the information is stored in the same client table, since I'm using the form of inheritance by hierarchy, but in the view only the client model information is returned
I've been able to solve the problem. I'll leave here recorded what I did to solve the problem in respect to inheritance.
First, I created a ViewModel and put two ICollection properties, I modified the controller by adding the queries referring to the client table, but specifically bringing the required types and finally, I passed the ViewModel to the Index.cshtml and I used two foreachs to retrieve the information from According to the type specified, shown below:
ClientesiewModel.cs
public class ClientesViewModel
{
public IEnumerable<PessoaFisica> Fisica { get; set; }
public IEnumerable<PessoaJuridica> Juridica { get; set; }
}
controlle.cs
public ActionResult Index()
{
var cliente_fisico = db.Clientes.OfType<PessoaFisica>().ToList();
var cliente_juridico = db.Clientes.OfType<PessoaJuridica>().ToList();
var cliente = db.Clientes.ToList();
ClientesViewModel clientes = new ClientesViewModel()
{
Fisica = cliente_fisico,
Juridica = cliente_juridico
};
return View(clientes);
}
View Index.cshtml
#model CabinePhoto.ViewModels.ClientesViewModel
<table class="table">
<tr>
<th>
#Html.DisplayName("Nome")
</th>
<th>
#Html.DisplayName("Telefone")
</th>
<th>
#Html.DisplayName("Telefone 2")
</th>
<th></th>
</tr>
#if (Model.Fisica != null || Model.Juridica != null)
{
foreach (var fisica in Model.Fisica)
{
<tr>
<td>
#Html.DisplayFor(modelItem => fisica.NomeCompleto)
</td>
<td>
#Html.DisplayFor(modelItem => fisica.TelefonePrincipal)
</td>
<td>
#Html.DisplayFor(modelItem => fisica.TelefoneAlternativo)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = fisica.ClienteId }) |
#Html.ActionLink("Details", "Details", new { id = fisica.ClienteId }) |
#Html.ActionLink("Delete", "Delete", new { id = fisica.ClienteId })
</td>
</tr>
}
foreach (var juridica in Model.Juridica)
{
<tr>
<td>
#Html.DisplayFor(modelItem => juridica.PessoaContato)
</td>
<td>
#Html.DisplayFor(modelItem => juridica.CNPJ)
</td>
<td>
#Html.DisplayFor(modelItem => juridica.TelefonePrincipal)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = juridica.ClienteId }) |
#Html.ActionLink("Details", "Details", new { id = juridica.ClienteId }) |
#Html.ActionLink("Delete", "Delete", new { id = juridica.ClienteId })
</td>
</tr>
}
}
</table>
Thanks for the help previously assigned
Suppose you have a variable like this in your controller.
Client c = new Client();
If you later wrote
c.ClientId = 1;
it would work perfectly.
Similarly, if you wrote
PessoaFisica p = new PessoaFisica ();
and later
p.Name = "abc";
it would also work.
However, if you wrote
Client c = new PessoaFisica();
c.Name = "abc";
It will fail to compile.
Along the same lines
#model IEnumerable <CabinePhoto.Models.Entidades.Client>
means static type of your Model is a collection of Client objects, it will only allow you to bind to properties defined in Client class.
Entity Framework actually returns the correct types, but you're effectively upcasting everything to Client by the variable type you're storing into and the model definition of the view. Essentially, you just need to cast to the right type. Unfortunately, there's no way to just know what type it should be after it's been upcasted. You'll have to conditionally check:
if (item is PessoaFisica)
{
var pessoaFiscica = (PessoaFisica)item;
// now you can access those derived typed properties off of `pessoaFiscica`
}
You can also use as and rely on the fact that it returns null when something can't be casted:
var pessoaFiscica = item as PessoaFisica;
if (pessoaFiscica != null)
{
// access PessoaFiscica properties
}
With C# 7.0, you can use the pattern matching syntax to streamline it a little:
if (item is PessoaFiscica pessoaFiscica)
{
// use `pessoaFiscica`
}
The pattern matching syntax also allows you use switch blocks, which may make things much easier on you:
switch (item)
{
case PessoaFisica pessoaFisica:
// do something with `PessoaFisica` instance
break;
case PessoaJuridica pessoaJuridica:
// do something with `PessoaJuridica` instance
break;
default:
// do something with generic `Client` instance
break;
}

Asp.net mvc 5: Partial view with strongly typed model of type list is not displaying

I am calling partial view controller through J_Query, controller action(ListInventoryProduct) is called and execute without error( the list is populated). But the partial view is not displayed.
In browser Developer tool says it is internal server error.
I can't figure out what is the problem.
The following is my code.
Model:
public class InventoryProductsViewModel
{
public long Id { get; set; }
[Display(Name = "Product Name")]
public string Title { get; set; }
[Display(Name = "SubCategory Name")]
public string SubCategory { get; set; }
[Display(Name = "Balance")]
public int Balance { get; set; }
[Display(Name = "Count")]
public int InventoryCount { get; set; }
[Display(Name = "Difference")]
public string Difference { get; set; }
[Display(Name = "IMEINumber")]
public string IMEINumber { get; set; }
[Display(Name = "BarrcodesString")]
public string BarrcodesString { get; set; }
public long subId { get; set; }
// public List<Category> lstCategory { get; set; }
}
Controller Action
public ActionResult LoadInventoryProducts(long categoryId)
{
Session["Products"] = null;
Session["InventoryMissing"] = null;
var userSession = Session.GetSessionUserInfo();
if (userSession != null)
{
List<InventoryProductsViewModel> products = db.Products.Where(p => !p.IsDeleted && p.CompanyId == userSession.CompanyId && (categoryId == 0 || p.SubCategory.CategoryId == categoryId)).Select(p => new InventoryProductsViewModel { Id = p.Id, Title = p.Title, SubCategory = p.SubCategory.Title, IMEINumber = p.IMEINumber, Balance = (p.PurchasedQuantity - p.SoldQuantity) }).ToList(); //&& (subCategoryId == 0 || p.SubCategoryId == subCategoryId)
Session["Products"] = products;
if (Session["InventoryMissing"] == null)
{
Session["InventoryMissing"] = new List<InventoryMissing>();
return PartialView("ProductsPartialView", products);
}
else
{
return Redirect("~/Error/Error");
}
}
}
PartialView
#model List<ViewModel.InventoryProductsViewModel>
<table >
<tr>
<th>#Html.DisplayNameFor(Model[0].Title)</th>
<th>#Html.DisplayNameFor(Model[0].SubCategory)</th>
<th>#Html.Label("Balance")</th>
<th>#Html.Label("Count")</th>
<th>#Html.Label("Difference")</th>
<th>#Html.Label("IMEI Number")</th>
</tr>
#for (int i = 0; i < Model.Count(); i++ )
{
<tr id="#Model[i].Id">
<td>
#Html.Hidden(Model[i].subId)
#Html.DisplayFor(Model[i].Title)
</td>
<td>#Html.DisplayFor(Model[i].SubCategory)</td>
<td class="balance">#Html.DisplayFor(Model[i].Balance)</td>
<td>#Html.EditorFor(Model[i].InventoryCount)</td>
<td class="difference">0</td>
<td>#Html.DisplayFor(modelItem =>Model[i].IMEINumber)</td>
</tr>
}
</table>
Html Helpers For Model were used which was creating problem.
Correct Helpers are as follow.
#model List<HiTechPOS.ViewModel.InventoryProductsViewModel>
<table class=" table table-striped table-advance table-hover">
<tr>
<th>
#Html.Label("Product Name")
</th>
<th>
#Html.Label("SubCategory Name")
</th>
<th>
#Html.Label("Balance")
</th>
<th>
#Html.Label("Count")
</th>
<th>
#Html.Label("Difference")
</th>
<th>
#Html.Label("IMEI Number")
</th>
</tr>
#for (int i = 0; i < Model.Count(); i++ )
{
<tr id="#Model[i].Id">
#*<td>
#Html.DisplayFor(modelItem => item.Id)
</td>*#
<td>
#Html.Hidden(Model[i].subId.ToString())
#Html.Label(Model[i].Title)
</td>
<td>
#Html.Label(Model[i].SubCategory)
</td>
<td class="balance">
#Html.Label(Model[i].Balance.ToString())
</td>
<td>
#Html.Editor(Model[i].InventoryCount.ToString())
</td>
<td class="difference">
#Html.Label(Model[i].Difference.ToString())
</td>
<td>
if(Model[i].IMEINumber == null)
{
#Html.Label("")
}
else
{
#Html.Label(Model[i].IMEINumber)
}
</td>
</tr>
}

omnifaces.SelectItemsConverter returns null

I m working on a project with jsf spring hibernate and primefaces. I have a form in which I add items. One field of that form is not a primitive type but an custom object. I know I have to use a converter and get the object I choose. I have tryied a custom converter with no luck since I cant inject the service and is always null. So I turned to omnifaces to make my life easier and move on. Unfortunately I have problems with that too. I look over and over my code but I cant see whats wrong. Below I add the code related to the problem:
First the page I use omnifaces:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:o="http://omnifaces.org/ui"
xmlns:of="http://omnifaces.org/functions">
<h:head><title>Welcome to OTV_JSF_Spring_Hibernate_Project</title></h:head>
<h:body>
<ui:include src="/pages/topMenu.xhtml"/>
<h:form>
<table>
<tr>
<td><h:outputLabel for="itemId" value="id: "/></td>
<td><h:inputText id="itemId" value="#{inventoryItemsMB.itemId}"></h:inputText></td>
</tr>
<tr>
<td><h:outputLabel for="RefItemCategories" value="Category Code"/></td>
<td>
<h:selectOneMenu value="#{inventoryItemsMB.refItemCategories}" id="refItemCategories" immediate="true" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{refItemCategoriesMB.refItemCategoriesList}" var="ref" itemLabel="#{ref.itemCategoryDescription}" itemValue="#{ref}"/>
<!--<f:converter converterId="catConverter"/>-->
</h:selectOneMenu>
</td>
</tr>
<tr>
<td><h:outputLabel for="description" value="Description"/></td>
<td><h:inputText id="description" value="#{inventoryItemsMB.description}" immediate="false"></h:inputText></td>
</tr>
<tr>
<td><h:outputLabel for="AvgMonthlyUsage" value="Average Monthly Usage: "/></td>
<td><h:inputText id="AvgMonthlyUsage" value="#{inventoryItemsMB.avgMonthlyUsage}"></h:inputText></td>
</tr>
<tr>
<td><h:outputLabel for="reorderQuantity" value="Reorder Quantity: "/></td>
<td><h:inputText id="reorderQuantity" value="#{inventoryItemsMB.reorderQuantity}"></h:inputText></td>
</tr>
<tr>
<td><h:outputLabel for="Price" value="Price: "/></td>
<td><h:inputText id="Price" value="#{inventoryItemsMB.price}"></h:inputText></td>
</tr>
<tr>
<td><h:outputLabel for="ItemStockLevels" value="Item stock levels: "/></td>
<td><h:inputText id="ItemStockLevels" value="#{itemStockLevelsMB.quantityInStock}"></h:inputText></td>
</tr>
<tr>
<td><p:commandButton id="addInventoryItems" value="ADD" action="#{inventoryItemsMB.addInventoryItems}" ajax="false" immediate="false"/></td>
</tr>
</table>
</h:form>
</h:body>
Secondly my managedBeans:
#ManagedBean(name="inventoryItemsMB")
#RequestScoped
public class InventoryItemsManagedBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final String SUCCESS = "successInventoryItems";
private static final String ERROR = "error";
#ManagedProperty(value="#{InventoryItemsService}")
IInventoryItemsService inventoryItemsService;
List<InventoryItems> inventoryItemsList;
private int id;
private String avgMonthlyUsage;
String description;
Set<ItemStockLevels> itemStockLevels;
float price;
String reorderQuantity;
RefItemCategories refItemCategories;
byte[] photo;
public String addInventoryItems(){
try{
InventoryItems inventoryItems = new InventoryItems();
//RefItemCategories refItemCategories = new RefItemCategories();
inventoryItems.setAvgMonthlyUsage(getAvgMonthlyUsage());//DONE is it a string?look at impl
inventoryItems.setDescription(getDescription());//DONE
inventoryItems.setItemId(getItemId());//DONE
inventoryItems.setItemStockLevels(getItemStockLevels());//DONE ----- why is it a set?
inventoryItems.setPhoto(getPhoto());
inventoryItems.setPrice(getPrice());//DONE
inventoryItems.setRefItemCategories(getRefItemCategories());//DONE---why refItemCategories
inventoryItems.setReorderQuantity(getReorderQuantity());//DONE-----why string?
//refItemCategories.getItemCategoryCode();
//refItemCategories.getItemCategoryDescription();
getInventoryItemsService().addInventoryItems(refItemCategories, inventoryItems);
return SUCCESS;
} catch (DataAccessException e) {
e.printStackTrace();
}
return ERROR;
}
public List<InventoryItems> getInventoryItemsList() {
inventoryItemsList = new ArrayList<InventoryItems>();
inventoryItemsList.addAll(getInventoryItemsService().getInventoryItems());
return inventoryItemsList;
}
public IInventoryItemsService getInventoryItemsService() {
return inventoryItemsService;
}
public void setInventoryItemsService(IInventoryItemsService inventoryItemsService) {
this.inventoryItemsService = inventoryItemsService;
}
public void setInventoryItemsList(List<InventoryItems> inventoryItemsList) {
this.inventoryItemsList = inventoryItemsList;
}
public int getItemId() {
return id;
}
public void setItemId(int id) {
this.id = id;
}
public void setAvgMonthlyUsage(String avgMonthlyUsage){
this.avgMonthlyUsage = avgMonthlyUsage;
}
public String getAvgMonthlyUsage(){
return avgMonthlyUsage;
}
public void setDescription(String description){
this.description = description;
}
public String getDescription(){
return description;
}
public void setItemStockLevelses(Set<ItemStockLevels> itemStockLevels){
this.itemStockLevels = itemStockLevels;
}
public Set<ItemStockLevels> getItemStockLevels(){
return itemStockLevels;
}
public void setPrice(float price){
this.price = price;
}
public float getPrice(){
return price;
}
public void setReorderQuantity(String reorderQuantity){
this.reorderQuantity = reorderQuantity;
}
public String getReorderQuantity(){
return reorderQuantity;
}
public void setRefItemCategories(RefItemCategories refItemCategories){
this.refItemCategories = refItemCategories;
}
public RefItemCategories getRefItemCategories(){
return refItemCategories;
}
public byte[] getPhoto(){
return photo;
}
public void setPhoto(byte[] photo){
this.photo = photo;
}
}
and the other managedbean
#ManagedBean(name="refItemCategoriesMB")
#RequestScoped
public class RefItemCategoriesManagedBean implements Serializable{
private static final long serialVersionUID = 1L;
private static final String SUCCESS = "addCategorySuccess";
private static final String ERROR = "error";
#ManagedProperty(value="#{RefItemCategoriesService}")
IRefItemCategoriesService refItemCategoriesService;
List<RefItemCategories> refItemCategoriesList;
private int id;
private String description;
public String addRefItemCategories(){
try{
RefItemCategories refItemCategories = new RefItemCategories();
refItemCategories.setItemCategoryCode(getItemCategoryCode());
refItemCategories.setItemCategoryDescription(getItemCategoryDescription());
getRefItemCategoriesService().addRefItemCategories(refItemCategories);
return SUCCESS;
} catch (DataAccessException e){
e.printStackTrace();
}
return ERROR;
}
public List<RefItemCategories> getRefItemCategoriesList() {
refItemCategoriesList = new ArrayList<RefItemCategories>();
refItemCategoriesList.addAll(getRefItemCategoriesService().getRefItemCategories());
return refItemCategoriesList;
}
public IRefItemCategoriesService getRefItemCategoriesService() {
return refItemCategoriesService;
}
public void setRefItemCategoriesService(IRefItemCategoriesService refItemCategoriesService) {
this.refItemCategoriesService = refItemCategoriesService;
}
public void setRefItemCategoriesList(List<RefItemCategories> refItemCategoriesList) {
this.refItemCategoriesList = refItemCategoriesList;
}
public String DeleteCategory(RefItemCategories refItemCategoriesList){
getRefItemCategoriesService().deleteRefItemCategories(refItemCategoriesList);
return SUCCESS;
}
public void setItemCategoryCode(int id){
this.id = id;
}
public int getItemCategoryCode(){
return id;
}
public void setItemCategoryDescription(String description){
this.description = description;
}
public String getItemCategoryDescription(){
return description;
}
#Override
public boolean equals(Object object){
if(this.id == ((RefItemCategories) object).getItemCategoryCode()) {
return true;
}else {
return false;
}
}
}
the service Im not able to inject:
#Transactional(readOnly = true)
#Service("RefItemCategoriesService")
public class RefItemCategoriesService implements IRefItemCategoriesService{
#Autowired
RefItemCategoriesDAOImpl refItemCategoriesDAO;
#Transactional(readOnly = false)
#Override
public void addRefItemCategories(RefItemCategories refItemCategories) {
getRefItemCategoriesDAO().addRefItemCategories(refItemCategories);
}
#Transactional(readOnly = false)
#Override
public void updateRefItemCategories(RefItemCategories refItemCategories) {
getRefItemCategoriesDAO().updateRefItemCategoriesUser(refItemCategories);
}
#Transactional(readOnly = false)
#Override
public void deleteRefItemCategories(RefItemCategories refItemCategories) {
getRefItemCategoriesDAO().deleteRefItemCategories(refItemCategories);
}
#Override
public RefItemCategories getRefItemCategoriesByID(int id) {
return getRefItemCategoriesDAO().getRefItemCategoriesById(id);
}
#Override
public List<RefItemCategories> getRefItemCategories() {
return getRefItemCategoriesDAO().getRefItemCategories();
}
public RefItemCategoriesDAO getRefItemCategoriesDAO(){
return refItemCategoriesDAO;
}
}
this way I get a transactional error where item_cat_code (the db table) cannot be null.
Thank you for your time.
As per the documentation, the omnifaces.SelectItemsConverter relies on the Object#toString() representation of the to-be-converted object to properly match the selected item. You didn't show the code of your RefItemCategories entity, so it's just guessing, but the symptoms indicate that you indeed didn't #Override the toString() on your entity, and it is thus relying on the default FQN#hashcode representation. This all would then only work if the available items (the value of <f:selectItems>) is exactly the same across postbacks on the same view.
However, the RefItemCategoriesManagedBean backing bean which provides the available items is request scoped and loads them in a getter. Everytime the getter is hit, a brand new list with brand new instances is returned. Those don't have the same hashcode as those which are already loaded in the list of available items and the submitted selected item thus never matches any of the newly loaded items.
That are thus at least 2 design problems.
You should choose the right bean scope for the data it holds. Make it at least #ViewScoped.
You should never interact with the DB in a getter method. Do it in #PostConstruct.
If you fix both, then the problem should disappear.
Or, just implement the toString() for your entity. E.g.
public class RefItemCategories {
#Override
public String toString() {
return "RefItemCategories[" + id + "]";
}
}
By the way, it's strange to see an equals() method on the RefItemCategoriesManagedBean backing bean class. Didn't you mean to put it on the RefItemCategories entity instead? In any way, carefully read the documentation of SelectItemsConverter how to properly use it and how to properly design your entities.

MVC 5 - Show item details in the same page when item is selected

I use the sample code located here: http://code.msdn.microsoft.com/MVC5-Demo-with-Entity-c6bc81df
Basically this is a CRUD sample for movies database where you can create read, update and delete them.
My question is: how can I show the details of a selected movie when clicking on Details link on the same page?
At the moment when clicking details you are redirected to the controller and action that will display the info about that movie.
Can I show the details on the same page where movies are listed?
There are a number of ways to do this, but they all come down to making some sort of Ajax call to load the partial view. The following is a working implementation, albeit minimal:
The Model - Movie.cs
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
I'd recommend a view model here, but that seems a bit out of the scope of the question at hand.
The Controller - MoviesController.cs
public class MoviesController : Controller
{
private readonly List<Movie> movies = new List<Movie>
{
new Movie { Title = "SomeTitle", Price = 23.25M, ID = 1 },
new Movie { Title = "AnotherTitle", Price = 123.25M, ID = 2 }
};
public ActionResult Index()
{
return this.View(this.movies);
}
public PartialViewResult Details(int id)
{
return this.PartialView("_details", this.movies.First(x => x.ID == id));
}
}
Partial View - _Details.cshtml
#model YourProject.Models.Movie
<hr/>
<h3>Details</h3>
#Html.DisplayFor(m => m.Title)
#Html.DisplayFor(m => m.Price)
...etc
<button type="button" id="clearDetails">Details</button>
Main View - Index.cshtml
#model IEnumerable<YourProject.Models.Movie>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
$(function () {
var detailsPartial = $('#details');
$('.details-link').click(function (event) {
event.preventDefault();
var url = $(this).attr('href');
$.ajax({
url: url,
type: 'get',
success: function(data) {
detailsPartial.html(data);
$('#clearDetails').click(function () {
detailsPartial.html(null);
});
}
});
});
});
</script>
<h2>Movies</h2>
<table>
<thead>
<tr>
<th></th>
<th>#Html.DisplayNameFor(x => x.Title)</th>
<th>#Html.DisplayNameFor(x => x.Price)</th>
</tr>
</thead>
<tbody>
#foreach (var movie in Model)
{
<tr>
<td>#Html.ActionLink("Details", "Details", new { id = movie.ID }, new { #class = "details-link" })</td>
<td>#Html.DisplayFor(x => movie.Title)</td>
<td>#Html.DisplayFor(x => movie.Price)</td>
</tr>
}
</tbody>
</table>
<div id="details"></div>
Put this altogether and you're left with a view wherein one can click details, Ajax will retrieve the partial view HTML from the server and insert it into the specified div tag.

two submit buttons Orchard CMS

I am trying to create a view where i need to two different button calling two different controller methods. but when i place and run it gives me error.
"The required anti-forgery form field "__RequestVerificationToken" is not present."
View
#model Course.ViewModels.CourseIndexVM
#{
Script.Require("ShapesBase");
Layout.Title = T("Course").ToString();
}
#using(Html.BeginForm("Index", "CourseAdmin", FormMethod.Get)) {
<fieldset class="bulk-actions">
<label for="search">#T("Search:")</label>
#Html.TextBoxFor(m => m.Search.Expression)
<button type="submit">#T("Search")</button>
#T("Clear")
</fieldset>
}
#using (Html.BeginForm("Create", "CourseAdmin", FormMethod.Post))
{
<fieldset class="bulk-actions">
<button type="submit">#T("Create")</button>
</fieldset>
}
<fieldset>
<table class="items" summary="#T("List of Courses")">
<colgroup>
<col id="Col1" />
<col id="Col2" />
<col id="Col3" />
<col id="Col4" />
<col id="Col5" />
<col id="Col6" />
</colgroup>
<thead>
<tr>
<th scope="col">#T("ID")</th>
<th scope="col">#T("Name")</th>
<th scope="col">#T("Description")</th>
</tr>
</thead>
#foreach (var item in Model.Courses) {
<tr>
<td>#item.Id</td>
<td>#item.Name</td>
<td>#item.Description</td>
<td>
<div>
#T("Edit")#T(" | ")
#T("Delete")
</div>
</td>
</tr>
}
</table>
#Display(Model.Pager)
</fieldset>
Controller
namespace CourseAdmin.Controllers
{
[Admin]
public class CourseAdminController : Controller, IUpdateModel
{
private readonly IContentDefinitionService _contentDefinitionService;
private readonly IRepository<CoursePartRecord> _coursePartRepository;
private readonly IContentManager _contentManager;
private readonly IProjectionManager _projectionManager;
private readonly INotifier _notifier;
private readonly IOrchardServices _orchardService;
private dynamic Shape { get; set; }
private readonly ISiteService _siteService;
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
}
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
ModelState.AddModelError(key, errorMessage.Text);
}
public CourseAdminController(
IShapeFactory shapeFactory,
ISiteService siteService,
IContentDefinitionService contentDefinitionService,
IContentManager contentManager,
IProjectionManager projectionManager,
IRepository<CoursePartRecord> coursePartRepository,
IOrchardServices orchardService,
INotifier notifier)
{
Shape = shapeFactory;
_siteService = siteService;
_contentDefinitionService = contentDefinitionService;
_contentManager = contentManager;
_projectionManager = projectionManager;
_coursePartRepository = coursePartRepository;
_notifier = notifier;
_orchardService = orchardService;
}
public ActionResult Create()
{
var course = _orchardService.ContentManager.New("Courses");
var editor = Shape.EditorTemplate(TemplateName: "Parts/Course.Create", Model: new CourseCreateVM(), Prefix: null);
editor.Metadata.Position = "2";
dynamic model = _orchardService.ContentManager.BuildEditor(course);
model.Content.Add(editor);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
[HttpPost, ActionName("Create")]
public ActionResult CreatePOST(CourseCreateVM courseModel)
{
var course = _orchardService.ContentManager.New("Courses");
// Cast the customer to a UserPart
var coursePart = course.As<CoursePart>();
coursePart.Name = courseModel.Name;
coursePart.Description = courseModel.Description;
_orchardService.ContentManager.Create(course);
return RedirectToAction("Index");
}
}
}
I think you need to use
#using (Html.BeginFormAntiForgeryPost(...)) { ... }
as the wrapper for your form.

Resources