For each loop with submitting form - asp.net-mvc

Model Room:
public class Room
{
public int Id { get; set; }
public string NumberRoom { get; set; }
public double CostPerNight { get; set; }
public virtual Category Category { get; set; }
}
My view model code
public class RoomModel
{
public IList<Room> Rooms { get; set; }
}
My Razor code:
#model hotel.Models.RoomModel
#using (Html.BeginForm("ComfortLevelView", "Category"))
{
for (int i = 0; i < Model.Rooms.Count(); i++)
{
<table class="simple-little-table" cellspacing='0'>
<tr>
<td>#Html.DisplayFor(m => Model.Rooms[i].NumberRoom) </td>
<td>#Html.DisplayFor(m => Model.Rooms[i].Categoryid)</td>
<td>#Html.DisplayFor(m => Model.Rooms[i].NumberOfSeats) </td>
<td>
#{ var result = Model.Rooms[i].CostPerNight * numberNights; }
<p>#ViewBag.NumberNights ночей</p>:#result
</td>
<td>
<input type="submit" id="submit" value="Booking" />
</td>
</tr>
</table>
</div>
}
}
Controller:
public ActionResult ComfortLevelView(int NumberNights, int CategoryId, int NumberPeoples ,DateTime SelectedDate)
{
IRoomService roomService = new RoomService();;
return View(roomService.GetRoomsByCategory(CategoryId, SelectedDate, NumberNights, NumberPeoples));
}
[HttpPost]
public ActionResult ComfortLevelView(RoomModel model)
{
//
}
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery`1[Hotel.BusinessObject.Room]', but this dictionary requires a model item of type 'hotel.Models.RoomModel'.

The error message is self explanatory. You have this in your view
#model hotel.Models.RoomModel
but you pass an instance of System.Data.Entity.Infrastructure.DbQuery<Hotel.BusinessObject.Room> to your view because of this line of code in your controller
return View(roomService.GetRoomsByCategory(CategoryId, SelectedDate, NumberNights, NumberPeoples));
You need to pass an instance of RoomModel instead of System.Data.Entity.Infrastructure.DbQuery<Hotel.BusinessObject.Room>. I would suggest changing your controller code to below
public ActionResult ComfortLevelView(int NumberNights, int CategoryId, int NumberPeoples, DateTime SelectedDate)
{
IRoomService roomService = new RoomService();
var rooms = roomService.GetRoomsByCategory(CategoryId, SelectedDate, NumberNights, NumberPeoples);
RoomModel model = new RoomModel();
model.Rooms = rooms.ToList();
return View(model);
}

Related

ViewModel, View and PartialView Post to Controller

I am trying to do the following: I have two models, header and List(details), sent to a view by a view model. When loading the main view, a dropdown is displayed from a list in the ViewModel.header model previously loaded. When you click on that dropdown, a partial view is loaded with some values, filtered by the value of the ddl, of the ViewModel.List(details) for the user to complete the information. So far everything works fine, but when doing the Post, controller it receives the ViewModel.List(details) in null.
what am I doing wrong?
Header
public class StockTransactionsHeader
{
[Key]
public int TransactionHeaderID { get; set; }
public DateTime TransactionDate { get; set; }
public string TransactionDocument { get; set; }
public int CategoryID { get; set; }
[NotMapped]
public List<SelectList> CategoryCollection { get; set; }
public virtual List<StockTransactionsDetails> StockTransactionsDetails { get; set; }
}
Details
public class StockTransactionsDetails
{
[Key]
public int TransactionDetailID { get; set; }
public int TransactionHeaderID { get; set; }
public int ProductID { get; set; }
public decimal Qty { get; set; }
public decimal Amount { get; set; }
public decimal TransactionAmount { get; set; }
[NotMapped]
public string ProductDescription { get; set; }
public virtual StockTransactionsHeader StockTransactionsHeader { get; set; }
}
ViewModel
public class StockTransactionsViewModel
{
public StockTransactionsHeader StockTransactionsHeader { get; set; }
public List<StockTransactionsDetails> StockTransactionsDetails { get; set; }
}
Controller Create
public ActionResult Create()
{
var stockTransactions = new StockTransactionsViewModel();
stockTransactions.StockTransactionsHeader = GetHeaderCategories();
return View(stockTransactions);
}
GetHeaderCategories()
private StockTransactionsHeader GetHeaderCategories()
{
var header = new StockTransactionsHeader();
header.CategoryCollection = CommonServices.GetSelecList((int)DeliveryCommonHelper.ConfigurationType.Categoria);
return header;
}
MainView
#model DeliverySolutionCommon.ViewModels.StockTransactionsViewModel
#using (Html.BeginForm())
{
<div class="form-row">
<div id="partialView" class="table-responsive">
</div>
</div>
<div class="form-group">
<div class="col-md-2">
<input type="submit" value=" Procesar " class="btn btn-warning" />
</div>
</div>
}
Script to load partial view
<script>
$(document).ready(function () {
$("#Category").on("change", function () {
autoFiltro();
})
})
function autoFiltro() {
var url = "#Url.Action("GetProductsListByCategory", "StockTransactions")";
var id = $("#Category").val();
var data = { idCategory: id };
$.post(url, data).done(function (data) {
$("#partialView").html(data);
})
}
</script>
GetProductsListByCategory
[HttpPost]
public PartialViewResult GetProductsListByCategory(int idCategory)
{
var products = ProductsServices.GetProductsListByCategory(idCategory);
var stockTransactions = new StockTransactionsViewModel();
stockTransactions.StockTransactionsDetails = GetTransactionsDetails(products);
return PartialView("_createStockTransactions", stockTransactions);
}
GetTransactionsDetails
private List<StockTransactionsDetails> GetTransactionsDetails (List<Products> products)
{
var details = new List<StockTransactionsDetails>();
foreach (var item in products)
{
StockTransactionsDetails detail = new StockTransactionsDetails();
detail.ProductID = item.ProductID;
detail.ProductDescription = item.Description;
details.Add(detail);
}
return details;
}
PartialView
#model DeliverySolutionCommon.ViewModels.StockTransactionsViewModel
<table class="table table-sm table-bordered table-striped">
#foreach (var item in Model.StockTransactionsDetails)
{
<tr class="d-flex">
<td class="col-7">
#Html.DisplayFor(modelItem => item.ProductDescription)
</td>
<td class="col-1">
#Html.EditorFor(modelItem => item.Qty, new { htmlAttributes
= new { #class = "form-control" } })
</td>
<td class="col-2">
#Html.EditorFor(modelItem => item.Amount, new {
htmlAttributes = new { #class = "form-control" } })
</td>
<td class="col-2">
#Html.EditorFor(modelItem => item.TransactionAmount, new {
htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</table>
Aaaaand finally Create Post
[HttpPost]
public ActionResult Create(StockTransactionsViewModel stockTransactionsView)
{
// StockStransactionsView.StockTransactionsDetails = null
}
The problem is you are posting back a list and there is no indexing information in your HTML... MVC model binder does not know how to put the items in a list without the index info...
you can try something like this:
#for (int i = 0; i < Model.StockTransactionsDetails.Count, i++)
{
<tr class="d-flex">
<td class="col-7">
#Html.EditorFor(modelItem => Model[i].Amount, new {
htmlAttributes = new { #class = "form-control" } })
</td>
// more code...
This would add the indexing information to your HTML...
Alternatively you can use EditorTemplate... something like this:
// Note that EditorFor template would iterate the list item for you
#Html.EditorFor(m => m.Model.StockTransactionsDetails)
This tutorial might help

ASP.NET mvc pass parameter to form

1) How to pass id in AddBookInCategory form in #Html.EditorFor(model=>model.CategoryID).
2) And second how after adding new item in public ActionResult AddBookInCategory(Books book) return to category with id it must be something like return RedirectToAction("ShowBooksFromCategory", 1); but it does not work.
HomeController:
public class HomeController : Controller
{
//
// GET: /Home/
TEntities TE = new TEntities();
public ActionResult Index()
{
return View();
}
public ActionResult ShowCategories()
{
return View(TE.Category.ToList());
}
public ActionResult ShowBooksFromCategory(int id)
{
return View(TE.Books.Where(key => key.CategoryID == id).ToList());
}
public ActionResult AddBookInCategory(int id)
{
return View();
}
[HttpPost]
public ActionResult AddBookInCategory(Books book)
{
TE.Books.Add(book);
TE.SaveChanges();
return View();
//return RedirectToAction("ShowBooksFromCategory", 1);
}
}
AddBookInCategory.cshtml:
#model TestDataBase.Models.Books
#{
ViewBag.Title = "AddBookInCategory";
}
<h2>AddBookInCategory</h2>
#using(#Html.BeginForm())
{
<p>Book Name:</p>
<br />
#Html.EditorFor(model=>model.BookName)
<p>Category:</p>
<br />
#Html.EditorFor(model=>model.CategoryID)
<input type="submit" value="Create"/>
}
Index.cshtml
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Html.ActionLink("ShowCategories", "ShowCategories")
ShowBooksFromCategory.cshtml
#model IEnumerable<TestDataBase.Models.Books>
#{
ViewBag.Title = "ShowBooksFromCategory";
}
<h2>ShowBooksFromCategory</h2>
<table>
#foreach(var item in Model)
{
<tr>
<td>
#item.BookName
</td>
</tr>
}
</table>
#Html.ActionLink("Add item", "AddBookInCategory", new {id=Model.Select(key=>key.CategoryID).FirstOrDefault() })
ShowCategories.cshtml
#model IEnumerable<TestDataBase.Models.Category>
#{
ViewBag.Title = "ShowCategories";
}
<h2>ShowCategories</h2>
<table>
#foreach(var item in Model )
{
<tr>
<td>
#item.CategoryName
</td>
<td>
#Html.ActionLink("ShowBooksFromCategory", "ShowBooksFromCategory", new { id=item.CategoryID})
</td>
</tr>
}
</table>
Models:
public partial class Books
{
public int CategoryID { get; set; }
public string BookName { get; set; }
public int BookID { get; set; }
}
public partial class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
}
Hi #A191919 To redirect with an int you can do the following (this is exactly the same thing you did within your actionlink to get the first form..)
return RedirectToAction("ShowBooksFromCategory", new { id = book.CategoryID });
To get the ID into your form you could use ViewBag.
public ActionResult AddBookInCategory(int id)
{
ViewBag.id = id;
return View();
}
The use the viewbag value to fill in your form input.

ASP.NET MVC 5 Html.CheckboxFor only return default value on post

I have read the tutorials and prepared a list of checkboxes for the page. When the form is submitted, the Selected property only get the value false.
Is there something I missed?
The Model
public class SelectStudentModel
{
public int StudentID { get; set; }
public string CardID { get; set; }
public string Name { get; set; }
public bool Selected { get; set;}
}
The ViewModel
public class SelectStudentViewModel
{
public List<SelectStudentModel> VMList;
public SelectStudentViewModel()
{
VMList = SelectStudentModel.GETStudent();
}
}
The View
#using Student.Models
#model SelectStudentViewModel
#using (Html.BeginForm("AddStudent", "SectionStudent", FormMethod.Post, new { #role = "form" }))
{
#{ for (int i = 0; i < Model.VMList.Count(); i++)
{
<tr>
<td>#Html.CheckBoxFor(m => m.VMList[i].Selected)</td>
<td>#Html.DisplayFor(model => model.VMList[i].Name)</td>
</tr>
}
}
<input type="submit" value="submit" />
}#* end form *#
The Controller for posted data
[HttpPost]
public ActionResult AddStudent(SelectStudentViewModel model)
{
foreach (SelectStudentModel m in model.VMList)
{
Console.Write(m.Selected.ToString());
}
return PartialView("StudentSelectForm", model);
}
VMList is a field in your SelectStudentViewModel model. You need to change it to a property (with a getter/setter) so the DefaultModelBinder can set the values
public class SelectStudentViewModel
{
public List<SelectStudentModel> VMList { get; set; } // change
public SelectStudentViewModel()
{
VMList = SelectStudentModel.GETStudent();
}
}
Side note: Suggest you change #Html.DisplayFor(model => model.VMList[i].Name) to #Html.LabelFor(m => m.VMList[i].Selected, Model.MList[i].Name) so that you get a label associated with the checkbox

Get Total of a field in mvc entitymodel 1st

I am trying to get total on my view.
I am confused what should i do? Please help me out..
I am not understanding where should i use query and ofc I need it in my view to show but how?
Model class
namespace BOL1
{
public class ADetailsVm
{
public List<BOL1.tbl_Transiction> Payables { get; set; }
public List<BOL1.tbl_Transiction> Reciveables { get; set; }
}
}
DbContext
public partial class bankingEntities : DbContext
{
public bankingEntities()
: base("name=bankingEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<tbl_Accounts> tbl_Accounts { get; set; }
public virtual DbSet<tbl_Transiction> tbl_Transiction { get; set; }
public virtual DbSet<tbl_TransictionType> tbl_TransictionType { get; set; }
public DbSet<ADetailsVm> ADetailsVm { get; set; }
}
}
Controller
public class Details : Controller
{
private TransictionBs objbs;
public Details()
{
objbs = new TransictionBs();
}
// GET: Shinwari/AccountDetails
[HttpGet]
public ActionResult Index(int accountid)
{
ADetailsVm v = new ADetailsVm();
//Load both the collection properties
v.Payables = objbs.GetALL().Where(p => p.AId == accountid && p.tbl_TransictionType.Type.Contains("Payable")).ToList();
v.Reciveables = objbs.GetALL().Where(r => r.AId==accountid && r.tbl_TransictionType.Type.Contains("Reciveable")).ToList();
return View(v);
View
#model BOL1.ADetailsVm
#{
ViewBag.Title = "Index";
}
<h2>AccountDetails</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table id="Payables" class="table">
<tr>
<th>
Date
</th>
<th>
Discription
</th>
<th>
Amount
</th>
</tr>
#foreach (var item in Model.Payables)
{
<tr>
<td>
#item.Date
</td>
<td>
#item.TDiscription
</td>
<td>
#item.Amount
</td>
</tr>
}
</table>
Use the LINQ Sum extension method on the Amount property(assuming it is of numeric type) of your Payables collection property of the view model.
<h2> #Model.Payables.Sum(s=>s.Amount) </h2>
Or if you do not like adding so much C# code in razor views(like me :)), You may add a new property to store the total in your view model.
public class ADetailsVm
{
public decimal TotalPayableAmount { set;get;}
public List<BOL1.tbl_Transiction> Payables { get; set; }
public List<BOL1.tbl_Transiction> Reciveables { get; set; }
}
and in your action method, call the Sum method and set the value to our new TotalPayableAmount property.
public ActionResult Index(int accountid)
{
var v = new ADetailsVm();
v.Payables = objbs.GetALL().Where(p => p.AId == accountid &&
p.tbl_TransictionType.Type.Contains("Payable")).ToList();
v.Reciveables = objbs.GetALL().Where(r => r.AId == accountid &&
r.tbl_TransictionType.Type.Contains("Reciveable")).ToList();
v.TotalPayableAmount= v.Payables.Sum(s=>s.Amount)
return View(v);
}
and in your view
#model ADetailsVm
<h2>#Model.TotalPayableAmount</h2>

Binding difficulty upon postback of list

I am having the difficulty to post back the new data being entered. It seems that the data sent to the view are sent back to the controller, despite changes made to the data before submit.
My code is as follows:
Controller
public class GroupRateController : Controller
{
//
// GET: /GroupRate/
public ActionResult Index()
{
GroupRateModel model = new GroupRateModel();
return View(model);
}
[HttpPost]
public ActionResult Index(GroupRateModel model)
{
model.Save(model);
return View(model);
}
}
View
#model MvcApplication1.Models.GroupRateModel
#{
View.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<table>
<thead>
</thead>
<tr><th>Rate Group</th><th>Default Amount</th><th>Client Amount</th></tr>
#foreach (var item in #Model.ClientRateDetails)
{
<tr><td>#item.RateGroupName</td><td align="right">#Html.DisplayFor(m => #item.RateGroupID)</td><td>#Html.EditorFor(model => item.ClientRate)</td></tr>
}
</table>
<p> <input type ="submit" value="Save" id="submit" /></p>
}
Model
using System.ComponentModel.DataAnnotations;
namespace MvcApplication1.Models
{
public class GroupRateModel
{
public List<ClientRateDetailsModel> ClientRateDetails = new List<ClientRateDetailsModel>() ;
public string Name { get; set; }
public GroupRateModel()
{
ClientRateDetails.Add(new ClientRateDetailsModel
{
RateGroupID = 1,
RateGroupName = "Test1",
ClientRate = 100
});
ClientRateDetails.Add(new ClientRateDetailsModel
{
RateGroupID = 2,
RateGroupName = "Test2",
ClientRate = 200
});
ClientRateDetails.Add(new ClientRateDetailsModel
{
RateGroupID = 3,
RateGroupName = "Test3",
ClientRate = 300
});
}
public void Save(GroupRateModel model)
{
foreach (var item in model.ClientRateDetails)
{
//...;
}
}
}
public class ClientRateDetailsModel
{
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:00.00}", NullDisplayText = "")]
[Range(0, (double)decimal.MaxValue, ErrorMessage = "Please enter a valid rate")]
public decimal? ClientRate { get; set; }
public int? RateGroupID { get; set; }
public string RateGroupName { get; set; }
}
}
This might be because the names of your input controls don't have correct names for the model binder to be able to fetch the values correctly. Also I see that the ClientRateDetails is not a property but a field in your model which won't be bound correctly. So here's how I would suggest you to improve your code:
Start with the model:
public class GroupRateModel
{
public IEnumerable<ClientRateDetailsModel> ClientRateDetails { get; set; }
public string Name { get; set; }
public GroupRateModel()
{
// Remark: You cannot assign your ClientRateDetails collection here
// because the constructor will be called by the default model binder
// in the POST action and it will erase all values that the user
// might have entered
}
public void Save(GroupRateModel model)
{
foreach (var item in model.ClientRateDetails)
{
//...;
}
}
}
public class ClientRateDetailsModel
{
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:00.00}", NullDisplayText = "")]
[Range(0, (double)decimal.MaxValue, ErrorMessage = "Please enter a valid rate")]
public decimal? ClientRate { get; set; }
public int? RateGroupID { get; set; }
public string RateGroupName { get; set; }
}
then a controller:
public class HomeController: Controller
{
public ActionResult Index()
{
var model = new GroupRateModel();
model.ClientRateDetails = new[]
{
new ClientRateDetailsModel
{
RateGroupID = 1,
RateGroupName = "Test1",
ClientRate = 100
},
new ClientRateDetailsModel
{
RateGroupID = 2,
RateGroupName = "Test2",
ClientRate = 200
},
};
return View(model);
}
[HttpPost]
public ActionResult Index(GroupRateModel model)
{
model.Save(model);
return View(model);
}
}
and then the corresponding view:
#model MvcApplication1.Models.GroupRateModel
#{
View.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<table>
<thead>
<tr>
<th>Rate Group</th>
<th>Default Amount</th>
<th>Client Amount</th>
</tr>
</thead>
#Html.EditorFor(x => x.ClientRateDetails)
</table>
<p><input type ="submit" value="Save" id="submit" /></p>
}
and then have a corresponding editor template (~/Views/Home/EditorTemplates/ClientRateDetailsModel.cshtml):
#model MvcApplication1.Models.ClientRateDetailsModel
<tr>
<!-- Make sure you include the ID as hidden field
if you want to get it back inside the POST action
-->
#Html.HiddenFor(x => x.RateGroupID)
<td>#Model.RateGroupName</td>
<td align="right">#Model.RateGroupID</td>
<td>#Html.EditorFor(x => x.ClientRate)</td>
</tr>

Resources